praisonai serve openai, which runs a separate standalone OpenAI-only process.
Quick Start
Enable in gateway.yaml
api block to your gateway config:Start with CLI flags
Enable in Python
How It Works
Each API request dispatches into the gateway’s own registered agents, sharing the same session store and admission gate as chat users.Endpoints Exposed
Background Runs
Submit long agent turns in the background, retrieve the result by id, and cancel in-flight runs — over the same OpenAI-compatible surface. The synchronous path is unchanged and remains the default — omitbackground and store to keep the exact previous behaviour.
Which mode fits
Quick Start
Submit in the background
Poll until terminal
Cancel if needed
User Flow
Status Vocabulary
completed response always carries an assistant message (even with empty text), so a client reading output[0] keeps working. Polling the same completed response returns the same output[0].id each time.
Response Object
error block appears only when status is failed. A GET on an unknown id returns 404 with {"error": {"type": "invalid_request_error", "message": "No such response: resp-…"}}.
OpenAI-Session / X-Session-Id header), only that caller can retrieve or cancel their own background runs. A different token receives 404 (not 403, to avoid leaking id existence). Anonymous callers on auth-disabled gateways are looked up by id alone — the same open posture that applies to every /v1/* route without auth._api_guarded wrapper as POST /v1/responses (gated by gateway.auth_token) and only mount when gateway.api.openai = true.
Safe Retries (Idempotency-Key)
Send the same Idempotency-Key header on a retry and the gateway returns the first run instead of starting a second — no duplicate LLM spend, no duplicate tool calls.
Opt-in, backward-compatible: no header → unchanged behaviour (every POST starts a fresh run, as before).
Quick Start
Send a key with /v1/responses
Retry safely with the same key
Works on /v1/chat/completions too
What happens on each case
User Flow
Scoping and limits
gateway.api.openai = true. There is no YAML config knob, no CLI flag, and no new dependency.Token Usage
Every response reports real per-turn token counts from the agent’s LLM instance. Each surface reports usage in its own OpenAI-native shape:usage falls back to zeros in the correct spec shape, so clients never need to special-case a missing block.
Streamed usage
Opt in withstream_options.include_usage: true to receive a streamed usage chunk — the same contract OpenAI’s own API uses.
usage-only chunk is emitted right before data: [DONE]. Without include_usage, the stream frames are unchanged and no extra chunk is sent. This is orthogonal to token-level streaming below — it works with or without it.
Token-level streaming (opt-in)
Enablegateway.api.stream to emit each DELTA_TEXT / FIRST_TOKEN piece as its own chat.completion.chunk with delta.content, so time-to-first-content matches token latency instead of full-turn latency.
Default is off: without opt-in the SSE surface is byte-for-byte the previous single buffered chunk, so existing clients keep working unchanged.
Enable it in gateway.yaml:
Configuration Options
Thegateway.api block maps to the ApiConfig dataclass. All three flags are opt-in; when openai and mcp are both False (default), no extra routes are mounted.
ApiConfig also exposes an enabled property (true if either surface is on), plus to_dict() and from_dict().
How Auth Works
Every API route is protected by the samegateway.auth_token as /info and /metrics.
The /info endpoint advertises which surfaces are enabled in its api field, so clients can introspect a running gateway before connecting.
GET /info also returns scopes, the caller’s effective operator scopes on this request, resolved from the bearer token against the auth_scopes policy. With no policy, every authenticated caller gets every scope.
praisonai gateway status prints this as an Access: line — see Gateway CLI → status and Operator Scopes.
When to Use vs praisonai serve openai
api: block when you want SDK clients and MCP tools to share live agents and sessions with chat users. Use praisonai serve openai when you want a standalone, lightweight OpenAI-only process with no gateway state. See OpenAI-Compatible Server.Best Practices
Keep surfaces off unless needed
Keep surfaces off unless needed
openai: false and mcp: false (the defaults) unless a client needs them. Disabled surfaces mount no routes and leave the gateway unchanged.Protect with auth_token in production
Protect with auth_token in production
/v1/* and /mcp route uses the same gateway.auth_token. Set a strong token when binding to any non-loopback interface.Pin conversations with a session header
Pin conversations with a session header
OpenAI-Session or X-Session-Id header to reuse one agent session across calls. Without it, stateless callers get a fresh session per request.Introspect surfaces with /info
Introspect surfaces with /info
GET /info returns an api field listing enabled surfaces, so tooling can confirm what a gateway exposes before dispatching.Enable streamed usage for cost accounting
Enable streamed usage for cost accounting
stream_options={"include_usage": True} when using stream=True if your client tracks cost per response. The extra chunk arrives right before [DONE] and carries real per-turn totals.Usage is per-turn, even under concurrent sessions
Usage is per-turn, even under concurrent sessions
usage field returned to one caller cannot be corrupted by a concurrent turn on the same shared agent. No configuration required.Run long turns in the background
Run long turns in the background
POST /v1/responses with background: true returns a persisted id in ~milliseconds; poll GET /v1/responses/{id} at a comfortable interval (2–5s) until status is terminal. If the caller loses connectivity, the run keeps going and the result is still retrievable by id (subject to the in-process store cap).Set a bearer token to scope background runs
Set a bearer token to scope background runs
gateway.auth_token in any deployment where multiple clients share the same gateway.Cancel is cooperative
Cancel is cooperative
/cancel fires the same interrupt seam as the WebSocket /stop path — the agent checks it at its own checkpoints and stops between LLM calls / tool calls. A tight, non-yielding loop inside a tool won’t stop until it yields; the task-cancel fallback covers most cases but not synchronous CPU-bound work.Set an Idempotency-Key for safe retries
Set an Idempotency-Key for safe retries
Idempotency-Key: <uuid> on every POST /v1/responses and POST /v1/chat/completions. The gateway dedupes the retry at the API boundary, so a timeout or load-balancer re-send never starts a second agent run or doubles your LLM spend. Reuse the same key on every retry of the same logical request. Applies to the buffered (non-stream) path only — streaming responses are not replayable.
