Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions .env (Copy)
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
APP_NAME=Laravel
APP_ENV=production
APP_KEY=base64:D71urbf6EWko9MribnCex3FRzFgKIyW/KIbA5z5Bnjg=
APP_DEBUG=false
APP_URL=https://ai.hellyer.test

APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US

APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database

# PHP_CLI_SERVER_WORKERS=4

BCRYPT_ROUNDS=12

LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=error

DB_CONNECTION=sqlite
# DB_DATABASE defaults to database/database.sqlite when unset
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=

SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null

BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database

CACHE_STORE=redis
# CACHE_PREFIX=

MEMCACHED_HOST=127.0.0.1

REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_MAILER=log
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false

VITE_APP_NAME="${APP_NAME}"

# AI provider
GITMEH_PROVIDER=opencode
OPENCODE_API_KEY=sk-LPYs1WVmEeSpMKXGwk2q2uAek9ikBIvg6hVAXxhEp6AL2UXNbAtMAUjLaMbvYt5o
# OPENCODE_MODEL=big-pickle

GITMEH_CHAT_INFERENCE_TIMEOUT=120
GITMEH_CHAT_TIMEOUT=120

API_DAILY_LIMIT=1000
SESSION_SECURE_COOKIE=true

TRUSTED_PROXIES=*
26 changes: 17 additions & 9 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,30 @@ AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
AWS_USE_FILE_STYLE_ENDPOINT=false

VITE_APP_NAME="${APP_NAME}"

OPENROUTER_API_KEY=
OPENROUTER_MODEL=google/gemma-3-4b-it
# GITMEH_PROMPT=
# OPENROUTER_HTTP_REFERER="${APP_URL}"
# OPENROUTER_TITLE="${APP_NAME}"
# OPENROUTER_TIMEOUT=120
# AI provider (opencode, openrouter, openai, anthropic, groq, mistral, xai, deepseek, gemini, ollama)
GITMEH_PROVIDER=opencode
OPENCODE_API_KEY=
# OPENCODE_MODEL=big-pickle

# Other providers (uncomment and fill as needed)
# OPENAI_API_KEY=
# ANTHROPIC_API_KEY=
# OPENROUTER_API_KEY=

API_DAILY_LIMIT=1000
# API_RATE_LIMIT_TIMEZONE=

# Hosted API overrides (optional)
# GITMEH_HOSTED_TOKEN=gitmeh-public-client
# GITMEH_MAX_JSON_BYTES=2097152
# GITMEH_CHAT_INFERENCE_TIMEOUT=120
# GITMEH_PROMPT=
# GITMEH_CHAT_TIMEOUT=120

# Production (set APP_ENV=production, APP_DEBUG=false; use https APP_URL; then php artisan optimize)
# SESSION_SECURE_COOKIE=true
# TRUSTED_PROXIES=*

# TRUSTED_PROXIES=*
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,57 @@ This application provides a free API for [gitmeh](http://github.com/ryanhellyer/

This API uses the Gemma 3-4b model, which is extremely cost-effective, allowing me to provide this service for free to users of `gitmeh`. The application acts as a proxy, routing requests through its own API to protect the underlying AI provider's API key from being exposed or abused by end-users.

The JSON endpoint is **`POST /v1/chat/completions`**, intentionally the same path pattern as [OpenAI’s Chat Completions API](https://platform.openai.com/docs/api-reference/chat/create) (`/v1/` API version + `chat/completions` resource). That lets the gitmeh client (and anything else expecting an OpenAI-compatible base URL) call `https://<this-host>/v1` and append `/chat/completions` without a special case for this server.

## Authentication

Bearer auth is optional. Three modes:

| Mode | Behavior | API key forwarded downstream |
|---|---|---|
| No `Authorization` header | Uses the server's configured `OPENAI_API_KEY` | No |
| `Authorization: Bearer gitmeh-public-client` (or `GITMEH_HOSTED_TOKEN`) | Uses the server's configured `OPENAI_API_KEY` | No |
| `Authorization: Bearer <your-key>` (any non-hosted token) | Uses **your key** as the downstream API key | Yes |

## Model selection

The `model` field in the JSON body sets the primary model. When omitted, the provider's default model is used.

```json
"model": "gpt-4o-mini"
```

## Fallback models

The optional `fallback_models` field provides models to try if the primary fails (context-length exceeded, transient errors, rate limits). Each model is retried up to 3 times with exponential backoff before moving to the next fallback.

```json
"fallback_models": ["gpt-4o", "gpt-3.5-turbo"]
```

Full example with custom key and fallbacks:

```bash
curl -k -sS -X POST 'https://ai.hellyer.test/v1/chat/completions' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer sk-your-openai-key' \
-d '{
"model": "gpt-4o-mini",
"fallback_models": ["gpt-4o", "gpt-3.5-turbo"],
"messages": [
{"role": "user", "content": "Unified diff:\n+a\n"}
]
}' | jq .
```

## Provider selection

The optional `provider` field selects an upstream provider from `config/ai.php` (default from `GITMEH_PROVIDER`, falls back to `openrouter`). Available providers include `openai`, `anthropic`, `groq`, `mistral`, `deepseek`, `gemini`, `xai`, `ollama`, and others defined in that config file. Each provider uses its own configured API key and default model unless overridden via the `Authorization` header or `model` field.

## Legacy endpoint

The plain-text `POST /gitmeh` endpoint still works and always routes through the server's default provider configuration.

Uses PHP 8.5 and Laravel 13.

## Tests
Expand All @@ -22,6 +73,26 @@ Uses PHP 8.5 and Laravel 13.

Use this command to test the API.

```bash
curl -k -sS -X POST 'https://ai.hellyer.test/v1/chat/completions' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer gitmeh-public-client' \
-d '{
"model": "gitmeh-hosted",
"fallback_models": ["google/gemma-3-4b-it"],
"messages": [
{"role": "user", "content": "Unified diff:\n+a\n"}
]
}' | jq .
```

Smoke-test against a live host (defaults to production URL; override with `GITMEH_VERIFY_BASE` and `GITMEH_VERIFY_TOKEN` for staging):

```bash
./scripts/verify-hosted-api.sh
```

Legacy API request:
```bash
curl -skS --request POST \
--header 'Content-Type: text/plain; charset=UTF-8' \
Expand Down
11 changes: 11 additions & 0 deletions TEST.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail

BASE="${GITMEH_VERIFY_BASE:-https://ai.hellyer.test/v1}"
TOKEN="${GITMEH_VERIFY_TOKEN:-gitmeh-public-client}"

curl -k -X POST "${BASE}/chat/completions" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"model":"gitmeh-hosted","messages":[{"role":"system","content":"Write a short git commit message. Imperative mood. Message only."},{"role":"user","content":"Unified diff:\n--- a/foo\n+++ b/foo\n@@ -0,0 +1 @@\n+bar\n"}],"temperature":0.3,"max_tokens":512}'
Loading