|
/api/generate no auth |
/api/generate_v2 auth required |
/api/generate_async auth · v4 new |
|
|---|---|---|---|
| Auth required | None | API key | API key |
| HTTP status on success | 200 OK | 200 OK | 202 Accepted |
| Blocks caller | Yes — sync | Yes — sync | No — returns immediately |
| Result delivery | Response body | Response body | Callback + poll |
| Redis cache | ✓ | ✓ | Never (always fresh) |
| Logs api_key_id | — | ✓ | ✓ |
| model | optional DB default fallback |
optional DB default fallback |
required |
| prompt | required | required | required |
| callback_url | — | — | required |
| callback_secret | — | — | optional |
| system | optional | optional | optional |
| context | optional | optional | — always [] |
| options | optional | optional | optional |
| images | optional | optional | optional |
| no_cache | optional | optional | — |
| stream | accepted, ignored | accepted, ignored | — |
| use_fallback | — | optional default: true |
optional |
| fallback_model | — | optional | optional |
| api_key body field | — | optional | optional |
| Best for | Internal / trusted-network callers that don't need per-key tracking | Authenticated clients where per-key usage logging matters | Long-running inference or fire-and-forget jobs — caller doesn't wait |
Routes the request to the correct provider (Ollama / Gemini / Groq) based on the model name. Checks Redis cache first; on a miss, calls the provider and caches the result. Response shape is identical to Ollama's /api/generate so existing clients need no changes.
| Field | Type | Description |
|---|---|---|
| modeloptional | string | Model ID as registered — e.g. gemma3:4b, gemini-2.0-flash, llama-3.3-70b-versatile default: DB default_model setting |
| promptrequired | string | User input text |
| systemoptional | string | System instruction prepended to the conversation default: "" |
| contextoptional | number[] | Ollama conversation context tokens from a previous response; ignored by cloud providers default: [] |
| optionsoptional | object | Inference params: { temperature?, top_p?, num_predict? } default: {} |
| imagesoptional | string[] | Base64-encoded images for multimodal models (moondream, gemini-pro-vision, etc.) default: [] |
| no_cacheoptional | boolean | Set true to bypass Redis cache and force a fresh inference call default: false |
| streamoptional | boolean | Logged only — streaming not implemented; always returns full response default: false |
{
model: string, // Model used (may differ if default was applied)
created_at: string, // Timestamp in app timezone (Asia/Kuala_Lumpur)
response: string, // Model text reply
done: true, // Always true (non-streaming)
context: number[], // Ollama context tokens; [] for cloud providers
total_duration: number, // Duration in nanoseconds (ms x 1,000,000)
load_duration: number, // Ollama model load ns; 0 for cloud providers
prompt_eval_count: number, // Input token count
eval_count: number // Output token count
}
| 429 | Provider rate limit exceeded — back off and retry |
| 503 | Provider unreachable (ECONNREFUSED / ETIMEDOUT) |
| 500 | Unexpected server error — check server logs |
Extends POST /api/generate with API key authentication and automatic model fallback. Requires a valid API key; stamps api_key_id on the api_logs row for per-key usage tracking. Fallback is on by default (use_fallback=true): if the primary model fails, the proxy retries with fallback_model (or proxy_settings.default_fallback_model) and returns a 200 with extra fallback fields. The primary model is marked unavailable regardless of whether the fallback succeeds. When both models fail, the error response includes a combined fallback_reason in api_logs: primary[<model>]: <err> | fallback[<model>]: <err>. The api_key body field (if supplied) is stripped before the request is forwarded to the provider.
| Field | Type | Description |
|---|---|---|
| modeloptional | string | Model ID as registered — e.g. gemma3:4b, gemini-2.0-flash, llama-3.3-70b-versatile default: DB default_model setting |
| promptrequired | string | User input text |
| systemoptional | string | System instruction prepended to the conversation default: "" |
| contextoptional | number[] | Ollama conversation context tokens from a previous response; ignored by cloud providers default: [] |
| optionsoptional | object | Inference params: { temperature?, top_p?, num_predict? } default: {} |
| imagesoptional | string[] | Base64-encoded images for multimodal models default: [] |
| no_cacheoptional | boolean | Set true to bypass Redis cache and force a fresh inference call default: false |
| streamoptional | boolean | Logged only — streaming not implemented; always returns full response default: false |
| use_fallbackoptional | boolean | Retry with a fallback model when the primary model fails. Defaults to true (opt-out to disable). Fallback is resolved from fallback_model if set, otherwise from proxy_settings.default_fallback_model. default: true |
| fallback_modeloptional | string | Explicit fallback model ID. If omitted (and use_fallback is true), the proxy reads proxy_settings.default_fallback_model. Must differ from model. default: null |
| api_keyoptional | string | Alternative to header-based auth — stripped before forwarding to provider |
// Primary model succeeded (or use_fallback=false):
{
model: string,
created_at: string,
response: string,
done: true,
context: number[],
total_duration: number,
load_duration: number,
prompt_eval_count: number,
eval_count: number
}
// Fallback model was used (primary failed, fallback succeeded):
{
model: string, // originally requested model
actual_model: string, // fallback model that actually ran
fallback_used: true,
fallback_reason: string, // primary-model error that triggered fallback
created_at: string,
response: string,
done: true,
context: number[],
total_duration: number,
load_duration: number,
prompt_eval_count: number,
eval_count: number
}
| 401 | Missing or invalid API key |
| 429 | Provider rate limit exceeded — back off and retry |
| 503 | Provider unreachable or both primary and fallback failed |
| 500 | Unexpected server error — check server logs |
Enqueues an inference job and returns 202 with a job_id immediately — the client does not wait for the model to respond. The in-process job worker picks up the job from Redis, runs inference via routeGenerate(), stores the result in async_jobs, then POSTs to callback_url. Requires a valid API key. Callback delivery retries up to proxy_settings.callback_retry_max times (default 0 = no retry), waiting proxy_settings.callback_retry_delay_sec seconds between each attempt (default 30 s). Retries run detached and do not block new jobs. If all attempts fail, callback_failed is set to true and the client can poll GET /api/jobs/:id. Optional fallback: set use_fallback=true to automatically retry with a different model if the primary fails.
| Field | Type | Description |
|---|---|---|
| modelrequired | string | Primary model ID — same format as /api/generate (e.g. gemma3:4b, gemini-2.0-flash). No DB default fallback — must be supplied explicitly. |
| promptrequired | string | User input text sent to the model |
| callback_urlrequired | string | Absolute URL the worker POSTs the result to when inference completes |
| callback_secretoptional | string | If set, sent as the X-Callback-Secret header on this job's callback POST so the receiver can authenticate it. Max 128 chars. Stored on the job row, never returned by GET /api/jobs/:id. Takes precedence over the ASYNC_CALLBACK_SECRET env var. |
| systemoptional | string | System instruction prepended to the prompt default: "" |
| optionsoptional | object | Inference params: { temperature?, top_p?, num_predict? } default: {} |
| imagesoptional | string[] | Base64-encoded images for vision models default: [] |
| use_fallbackoptional | boolean | If true, retry with a fallback model when the primary model fails. Fallback is resolved from fallback_model if set, otherwise from proxy_settings.default_fallback_model. default: false |
| fallback_modeloptional | string | Explicit fallback model ID. If omitted (and use_fallback is true), the worker reads proxy_settings.default_fallback_model. Must differ from model. default: null |
| api_keyoptional | string | Alternative to header-based auth — stripped before processing, never forwarded to provider or stored |
{
job_id: string, // UUID — use this to poll GET /api/jobs/:id
status: "queued",
model: string,
poll_url: string // convenience URL: /api/jobs/
}
| 400 | Missing model, prompt, or callback_url; or callback_url is not a valid URL |
| 401 | Missing or invalid API key |
callback_retry_max (Y — max retries, default 0 = no retry) and callback_retry_delay_sec (X — seconds between retries, default 30). callback_attempts in async_jobs records how many delivery attempts were made. Retries run detached from the job worker loop so they never block new jobs.http://host.docker.internal:8080/webhook — Windows/Mac Docker Desktop (reaches host machine); http://172.17.0.1:8080/webhook — Linux Docker host (default bridge gateway IP); http://<LAN-IP>:8080/webhook — any machine on the same network; http://other-container:8080/webhook — another service on the same Docker network.Returns the current state of an async job created via POST /api/generate_async. Requires the same API key that created the job — cross-key access is rejected with 404. Use this endpoint when callback delivery is uncertain (callback_failed: true) or when your client cannot receive inbound HTTP callbacks.
| Field | Type | Description |
|---|---|---|
| ?:idrequired | string (path) | Job UUID returned by POST /api/generate_async |
{
job_id: string,
status: "queued" | "processing" | "completed" | "failed",
model: string, // actual model that ran (fallback model if fallback was used)
requested_model: string | null, // original requested model — non-null only when fallback was used
fallback_used: boolean, // true if fallback inference was triggered
fallback_reason: string | null, // primary error; or "primary[m]: e | fallback[m]: e" when both fail
provider: string | null, // OLLAMA | GEMINI | GROQ | null (while queued)
created_at: string,
started_at: string | null,
completed_at: string | null,
response: string | null, // inference result when status=completed
error: string | null, // error detail when status=failed
prompt_tokens: number,
completion_tokens: number,
duration_ms: number,
callback_url: string,
callback_failed: boolean, // true if all callback delivery attempts failed
callback_attempts: number // how many delivery attempts were made (0 = not yet attempted)
}
| 401 | Missing or invalid API key |
| 404 | Job not found or belongs to a different API key |
Returns the in-memory model registry built at startup by loadModelRegistry(). The .providermodelignore list is already applied — ignored models do not appear. No external API calls; instant read from memory.
{
ollama: [{ name: string, id: string, size: number, provider: "ollama", details: object }],
gemini: [{ name: string, id: string, provider: "gemini", details: object }],
groq: [{ name: string, id: string, provider: "groq", details: object }]
}
| 405 | Method not allowed — only GET is accepted |
Returns the current in-memory statusMap — updated by the health check scheduler and reactively by generate failures. No probing is performed on this call; response is instant.
{
lastUpdated: string,
summary: {
total: number, available: number,
unavailable: number, pending: number, ignored: number
},
providers: {
ollama: [{ modelId, provider, status, latencyMs, lastChecked, error }],
gemini: [...],
groq: [...]
}
}
// status: "available" | "unavailable" | "pending" | "ignored"
Returns rolling usage counters from the in-memory metrics store. All values reset on server restart. Optional query params filter the response.
| Field | Type | Description |
|---|---|---|
| ?provideroptional | string | Filter to one provider: ollama | gemini | groq |
| ?modelIdoptional | string | Filter to a single model ID (must be used together with provider) |
| ?flatoptional | "1" | Return a flat array instead of the grouped object |
// Default grouped response:
{
lastUpdated: string,
providers: {
ollama: [{
modelId, provider,
rpm, tpm, rpd,
avgLatencyMs, // rolling 60-second avg (successful only)
avgLatencyHourMs, // rolling 1-hour avg (successful only)
totalRequests, totalTokens, errorCount, lastUsed
}],
gemini: [...], groq: [...]
}
}
// With ?flat=1 → { lastUpdated, models: [...same fields...] }
// With ?provider=X&modelId=Y → { lastUpdated, model: {...} }
| 404 | No metrics found for the given provider + modelId |
Runs the same incremental rollup that the background scheduler executes automatically every TOKEN_ROLLUP_INTERVAL_MIN minutes. Reads unprocessed rows from api_logs and aggregates them into token_usage_summary. Idempotent — safe to call multiple times.
{ success: true, processed: number } // rows rolled up this call
| 500 | Rollup failed — check MySQL connectivity and server logs |
Self-contained HTML page generated from in-memory state on every request. No DB or network calls. Auto-refreshes every 60 seconds by default (configurable interval with pause option).
Content-Type: text/html — the gateway diagnostic page
Standalone HTML page listing all endpoint contracts. No DB or network calls.
Content-Type: text/html — this page