GenAI chat completions and model discovery¶
API version
GenAI API: 1.4.14
The GenAI application provides a single generation contract across configured OpenAI, Azure OpenAI, Anthropic and OVH models. Use discovery to choose a model and its supported options before generating a response.
| Operation | Mounted URL | Purpose |
|---|---|---|
GET |
/genai/api/v1/catalogue |
Public shared providers, models, profiles and capabilities; no authentication or client context |
GET |
/genai/api/v1/models |
List configured models, provider IDs, profile mappings and capabilities |
POST |
/genai/api/v1/chat/completions |
Generate a Chat Completions response or SSE stream |
GET |
/genai/docs |
Interactive Swagger documentation |
GET |
/genai/redocs |
Interactive ReDoc documentation |
GET |
/genai/openapi.json |
Machine-readable contract |
The /api/v1/... paths in Swagger are relative to the /genai mount. For the
local service on port 8086, the SDK base URL is
http://127.0.0.1:8086/genai/api/v1.
Authentication and configuration scope¶
POST /chat/completions and GET /models require an AIHub X-API-Key. The usual AIHub authentication and
client-routing rules apply, including Client-ID where required by your deployment.
An OpenAI provider key is not an AIHub API key. Sending only the OpenAI SDK's
Authorization: Bearer ... header does not replace X-API-Key.
The authenticated configuration supplies the default provider and tenant profile mapping. The provider catalogue supplies explicit models and the profiles of other providers. PostgreSQL deployments use their configured database; MongoDB deployments use the existing client context. These endpoints do not accept a different backend URL, credentials or a different deployment environment in the JSON body.
Successful responses and GenAI error envelopes include an X-Request-ID correlation header.
An optional X-GenAI-Client label, such as hrx, is used for logging when no client
identifier is available. It is not an authorization or tenant-selection mechanism.
Labels must contain 1–64 letters, digits, dots, underscores or hyphens.
export AIHUB_BASE_URL='http://127.0.0.1:8086/genai/api/v1'
# Set AIHUB_API_KEY to your actual AIHub key outside source code.
curl --fail-with-body "$AIHUB_BASE_URL/models" \
-H "X-API-Key: $AIHUB_API_KEY"
Swagger request examples¶
The Chat Completions operation provides an Examples dropdown above its request body. The first example shows explicit provider/model/profile selection, reasoning, JSON Schema output, token limits and prompt-cache controls. Additional examples cover profile-based JSON output, sampling, function tools, SSE with final usage, and the minimal request using configured defaults.
Select an example before using Try it out. Model IDs and profile capabilities
are illustrative: use /models or the live catalogue below to adapt them to your
configuration. The explicit model overrides the profile when both are present.
Sampling controls and reasoning controls are shown separately because support
varies by model. Examples do not change runtime defaults.
Live catalogue¶
Live provider configuration
Shared providers, models and profiles load automatically from this AIHub instance. Database mappings take precedence over code fallbacks, which are shown separately. No API key or Client-ID is needed; client-specific settings are not included.
Loading shared configuration… Static tables below describe code fallbacks.
This view automatically reads the public GET /genai/api/v1/catalogue endpoint.
It requires no API key or Client-ID and shows shared provider profiles, provenance,
GenAI capabilities and independent code fallbacks. It never reads client-specific
configuration. Use Refresh configuration to retrieve database changes. See live configuration details
for inheritance and refresh behavior.
GET /catalogue takes no parameters. Its ModelList response has the same
metadata structure as /models, but the default provider and profiles come only
from the shared provider database. Supplied API keys or Client-ID headers do not
select a client. Provider credentials, connection settings and raw provider
documents are never returned. A database failure returns HTTP 502 with the stable
catalogue_unavailable error code; code fallbacks are not presented as a
successfully loaded database catalogue. Responses use Cache-Control: no-store.
Selecting a provider, model or profile¶
provider, model and profile are optional. Omitted and JSON null selectors
have the same meaning. Empty strings are invalid. Provider and profile identifiers
are case-sensitive.
| Provider input | Model input | Profile input | Selection |
|---|---|---|---|
| Absent | Absent | Absent | Configured default provider and its effective default profile |
| Present | Absent | Absent | That provider's default profile |
| Absent | Absent | Present | Named profile on the configured default provider |
| Present | Absent | Present | Named profile on that provider |
| Absent | Present | Any valid value or absent | Explicit model on the configured default provider; profile ignored |
| Present | Present | Any valid value or absent | Explicit provider/model; profile ignored |
The public profile values are tiny, mini, large and reasoning. The internal
default profile is selected by omitting both model and profile; do not send
"profile": "default".
model accepts an ID returned by /models or one of that entry's catalogue
aliases. Model selection does not search other providers. For example, a Claude
model without provider: "anthropic" is rejected if the configured provider is
OpenAI. An invalid explicit model does not fall back to a valid profile.
When a profile is used, its configured model and reasoning_effort are preserved.
An explicit reasoning_effort overrides that profile setting, including a profile
that sets reasoning_effort: "none". Profile settings are defaults, not capability
restrictions. For example, profile: "tiny" with reasoning_effort: "high" enables
high reasoning if the resolved model supports it, even when that profile normally
disables reasoning. Validation uses the effective request value and the model's
capabilities; it does not reject an override because of the profile's default. When model is
supplied, no reasoning setting is inherited from profile; an omitted effort uses
the provider's model default. A profile named reasoning chooses a configured
model/settings combination, while reasoning_effort controls effort on the model
that was actually selected. They are different controls.
The shared profile resolver's configured profile mappings remain authoritative.
For a different provider, its existing profile-resolution rules apply. A missing
or inconsistent catalogue/profile returns unknown_model; the endpoint does not
invent a model or switch providers to complete the request.
Use the configured defaults¶
Select a profile on Azure OpenAI¶
{
"provider": "azure",
"profile": "mini",
"messages": [
{"role": "user", "content": "Summarize the benefits of mentoring."}
],
"max_completion_tokens": 1000
}
Select a model explicitly¶
{
"provider": "openai",
"model": "gpt-6-luna",
"profile": "large",
"messages": [{"role": "user", "content": "Explain mentoring in one sentence."}],
"reasoning_effort": "low",
"max_completion_tokens": 1000
}
In this example, gpt-6-luna wins over large. It must exist in the deployment's
catalogue. The examples use models from AIHub's seeded catalogues, but /models
is the source of truth for your deployment.
Discovering models and capabilities¶
GET /models returns an OpenAI-style list envelope. Use ?provider=azure to
filter it. Valid filters are openai, azure, anthropic and ovh.
A known provider with no configured models returns an empty data list. An invalid
provider filter returns HTTP 422. There is no pagination; the response contains
the configured generation catalogue, excluding embedding, audio and realtime models.
An illustrative entry is:
{
"object": "list",
"data": [
{
"id": "gpt-4o",
"object": "model",
"created": 0,
"owned_by": "azure",
"provider": "azure",
"aliases": [
"gpt_4o"
],
"profiles": [
"default",
"tiny",
"mini"
],
"capabilities": {
"reasoning": false,
"reasoning_efforts": [],
"json_output": true,
"response_formats": [
"text",
"json_object",
"json_schema"
],
"streaming": true,
"tools": true,
"forced_tool_choice": true,
"tool_reasoning_efforts": null,
"prompt_cache_parameters": [
"prompt_cache_key"
],
"prompt_cache_retentions": [],
"supported_parameters": [
"frequency_penalty",
"max_completion_tokens",
"max_tokens",
"parallel_tool_calls",
"presence_penalty",
"prompt_cache_key",
"response_format",
"seed",
"stop",
"stream",
"stream_options",
"temperature",
"tool_choice",
"tools",
"top_p"
]
}
}
]
}
| Field | Meaning |
|---|---|
id |
Concrete provider model/deployment ID for generation |
provider, owned_by |
AIHub provider identifier; use it together with id |
created |
0 means the catalogue does not contain a model creation timestamp |
aliases |
Configured alternate keys for this model; entries are grouped by model ID |
profiles |
Effective profiles resolving to this model for the selected configuration; includes internal default |
capabilities.forced_tool_choice |
Whether required and named function choices are supported; Sonnet 5.5 and Opus 5.5 return false |
capabilities.reasoning |
Whether this adapter exposes an effort control for this model |
reasoning_efforts |
Accepted values for reasoning_effort, not a universal provider-wide enum |
json_output |
Whether at least one JSON output format is available |
response_formats |
Accepted response_format.type values; distinguish json_object from json_schema |
streaming |
Whether this endpoint can deliver incremental SSE for this provider |
tools |
Whether function tools are supported by this model on this endpoint |
tool_reasoning_efforts |
If non-null, function tools require one of these effort values |
prompt_cache_parameters |
Explicit OpenAI cache controls accepted by this adapter/model |
prompt_cache_retentions |
Allowed values of prompt_cache_retention |
supported_parameters |
Optional generation controls accepted by this adapter/model |
messages, the three selectors and stream: false are universally accepted and
are not repeated in supported_parameters. Actual values and combinations remain
subject to validation; presence in the list does not mean every possible value is
valid. Discovery and request validation share the same capability definitions.
Availability means configured in AIHub. Discovery does not contact providers,
probe credentials, check quotas, verify Azure deployments or make billable model
calls. An entry can therefore still fail upstream. No credentials, connection
URLs, pricing records or arbitrary private catalogue properties are returned.
The same model ID can appear under more than one provider; identify it by the pair
(provider, id). Conflicting catalogue configurations for one ID require selecting
an unambiguous alias or fixing the catalogue.
Capabilities are deliberately conservative for unrecognized model/deployment names. In particular, an arbitrary Azure deployment name does not automatically inherit GPT reasoning/JSON capabilities. The capability definitions must recognize a model before those controls are advertised.
Request parameters¶
Unknown top-level fields are rejected. This is a documented Chat Completions
subset with AIHub selection extensions, not a pass-through for every OpenAI API
field. Optional generation fields omitted or set to null are not forwarded;
explicit false, 0 and reasoning_effort: "none" are preserved.
| Parameter | Accepted shape and behavior |
|---|---|
messages |
Required list of 1–100 messages; roles: system, developer, user, assistant, tool |
provider |
Optional provider enum described above |
model |
Optional model ID or alias, 1–150 characters; overrides profile |
profile |
Optional tiny, mini, large, reasoning |
response_format |
text, json_object, or json_schema envelope described below |
reasoning_effort |
Schema accepts none, minimal, low, medium, high, xhigh, max; selected model narrows the set |
max_completion_tokens |
Integer 1–16,000; output budget, including reasoning where reported |
max_tokens |
Legacy spelling for the output budget; cannot coexist with max_completion_tokens |
temperature |
Number 0–2; only where advertised |
top_p |
Number 0–1; only where advertised |
frequency_penalty, presence_penalty |
Numbers −2–2; only where advertised |
seed |
Optional integer; determinism remains provider-dependent and is not guaranteed |
stop |
One non-empty string, or 1–4 non-empty strings, each at most 1,024 characters |
tools |
Up to 20 OpenAI function definitions; AIHub does not execute them |
tool_choice |
auto, none, required, or a named function object |
parallel_tool_calls |
Optional boolean for providers/models advertising this control |
prompt_cache_key |
Optional key of 1–64 characters |
prompt_cache_retention |
Legacy in_memory or 24h for supported models before GPT-5.6; rejected for GPT-5.6/GPT-6 |
prompt_cache_options |
Closed object with optional mode: implicit|explicit and ttl: 30m |
stream |
Boolean, defaults to false |
stream_options |
Closed object with include_usage boolean; valid only with stream: true |
The two output-token spellings are normalized to the native provider API. Omitting both leaves the provider's default unchanged, except Anthropic, whose required output limit defaults to 4,096. The GenAI endpoint does not inject the shared business-workflow sampling, reasoning or prompt-cache policy.
For non-stream requests, the HTTP service calls AIHub's existing call_llm_async()
entry point. Model resolution, provider dispatch, timeouts and normalized internal
usage/cost handling stay in the shared connector. A typed, optional explicit-control
bundle preserves the public API parameters and native OpenAI response without
changing the default policies of other AIHub workflows. The existing incremental
OpenAI/Azure SSE transport remains the streaming path.
OpenAI/Azure responses use the native Chat Completions API. Anthropic uses its native Messages
API, mapping JSON Schema and effort to output_config. OVH uses its OpenAI-compatible
API. Unsupported explicit options return an error instead of being ignored or
replaced with a different setting.
Provider differences to account for¶
| Provider/model family | JSON | Effort | Incremental streaming | Explicit OpenAI cache controls |
|---|---|---|---|---|
| Recognized OpenAI models | JSON object and JSON Schema on supported families | Model-dependent | Yes | Key, retention and options according to model |
| Recognized Azure OpenAI models | JSON object and JSON Schema | Model-dependent | Yes | Cache key; retention/options are not exposed |
| Supported Claude models | JSON Schema; no json_object mode |
Exposed on recognized Sonnet/Opus models and Haiku 5.5 | No | Not exposed |
| Recognized OVH models | JSON object; JSON Schema not exposed | Qwen3.5-9B / gpt-oss-120b mappings | No | Not exposed |
Anthropic sampling controls are not exposed by this endpoint. The native API
version 2026-10-21 used by the pinned SDK rejects temperature, top_p and
top_k; AIHub does not send them.
GPT‑6 Astra and GPT‑6.1 Sol do not accept none or minimal effort. GPT‑6.1 Terra
accepts none, low, medium, high, xhigh and max. None of these three
models supports function calling through this Chat Completions endpoint, even
Terra at effort none. The shared connector and LangGraph use Responses for
their tool calls. The default OpenAI large profile selects Terra with medium. GPT‑5.4,
GPT‑5.5, GPT‑5.6 and GPT‑6 Luna require effective effort none when tools
are supplied. Set it explicitly or select a profile that configures it; AIHub will
not disable reasoning automatically. /models reports these restrictions through
tools and tool_reasoning_efforts.
An omitted effort is rejected unless the selected profile supplies none.
The built-in OpenAI and Azure mini profiles now supply low; requests using
these profiles with restricted function tools must explicitly set
reasoning_effort: "none".
Unsupported combinations return HTTP 422 before document processing or generation.
Text, JSON mode and JSON Schema¶
The HTTP response remains a Chat Completions object in every output mode.
The generated text is at choices[0].message.content. JSON output is encoded as a
JSON string in content, exactly as in an OpenAI Chat Completions response.
It is not returned as a replacement HTTP body or a nested object in content.
Plain text¶
Omit response_format, or send {"type":"text"}. Do not infer a JSON mode from
the prompt alone.
JSON object mode¶
{
"provider": "azure",
"profile": "mini",
"messages": [
{"role": "system", "content": "Return a JSON object containing a summary string."},
{"role": "user", "content": "Summarize a mentoring program for new engineers."}
],
"response_format": {"type": "json_object"},
"max_completion_tokens": 1000
}
Instruct the model to produce JSON in the messages. JSON mode does not enforce specific keys, types or a schema. OpenAI can reject JSON-mode requests without a JSON instruction. Refusals and token-limit truncation still need handling.
Structured JSON Schema output¶
{
"provider": "openai",
"model": "gpt-4o",
"messages": [
{"role": "user", "content": "Summarize a mentoring program for new engineers."}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "mentoring_summary",
"description": "A concise description of a mentoring program.",
"strict": true,
"schema": {
"type": "object",
"properties": {"summary": {"type": "string"}},
"required": ["summary"],
"additionalProperties": false
}
}
},
"max_completion_tokens": 1000
}
json_schema.name is required, 1–64 ASCII letters/digits/underscores/hyphens.
schema is required and must be a JSON object; description and strict are
optional. The selected provider enforces its supported JSON Schema dialect.
AIHub validates the envelope and input budget; it does not compile arbitrary
schemas, repair output, strip Markdown fences or retry generation to force validity.
Anthropic receives the schema through output_config.format. OpenAI's schema name/description/strict fields are sent
unchanged to OpenAI/Azure; other providers enforce their native schema semantics.
A successful response can contain:
{
"id": "chatcmpl-example",
"object": "chat.completion",
"created": 1790596800,
"model": "gpt-4o",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "{\"summary\":\"Pair new engineers with experienced mentors.\"}", "refusal": null},
"finish_reason": "stop",
"logprobs": null
}
],
"usage": {"prompt_tokens": 60, "completion_tokens": 20, "total_tokens": 80}
}
Check finish_reason, message.refusal when present, and non-null content before
parsing it. A length result can contain incomplete JSON, including no visible
text if reasoning consumed the budget. A content_filter result is not a successful
structured answer. Tool-call responses may have content: null.
Prompt caching¶
Prompt caching is provider-side prefix reuse. It is not an AIHub result cache, document store or conversation-history store. Sending the same key does not return a previous completion, and cache hits are not guaranteed.
prompt_cache_keyis forwarded unchanged to supported OpenAI/Azure models.prompt_cache_retentioncontrols cache lifetime on supported models before GPT‑5.6. GPT‑5.5 accepts24h; older supported models expose their permitted values in/models. GPT‑5.6 and GPT‑6 reject this legacy field with HTTP 422, including when it is combined withprompt_cache_options.prompt_cache_options.modechoosesimplicitorexplicitcaching on supported OpenAI models. For GPT‑5.6 and GPT‑6,ttl: "30m"is the currently exposed minimum lifetime. Their discovery metadata lists no legacy retention values.- Explicit breakpoints are preserved on text content parts. At most four can be supplied. With explicit-only mode and no breakpoints, no cache write is requested.
- AIHub does not manufacture breakpoints, generate a key or retry without caching after the provider rejects an explicit cache setting.
- An empty
prompt_cache_parameterslist means these request controls are not exposed; it does not mean the provider never performs automatic caching.
{
"provider": "openai",
"model": "gpt-6-luna",
"messages": [
{
"role": "developer",
"content": [
{
"type": "text",
"text": "You summarize mentoring programs using the supplied reusable policy.",
"prompt_cache_breakpoint": {"mode": "explicit"}
}
]
},
{"role": "user", "content": "Summarize this program: weekly mentoring for new engineers."}
],
"prompt_cache_key": "mentoring-policy-v1",
"prompt_cache_options": {"mode": "explicit", "ttl": "30m"},
"reasoning_effort": "low",
"max_completion_tokens": 1000
}
This short example demonstrates the request shape; it is too short to guarantee cache eligibility. Provider minimum prefix lengths, prefix matching, cache-write pricing and retention rules still apply. Put stable reusable content first and varying user content last. Treat cache keys as opaque identifiers, not a place for personal data or credentials.
When reported, cache usage appears in usage.prompt_tokens_details.cached_tokens;
reasoning usage appears in usage.completion_tokens_details.reasoning_tokens.
These are subsets of input/output usage, not extra tokens to add to total_tokens.
OpenAI-compatible native usage fields are retained. Anthropic usage is
normalized into the same names when available; missing usage remains null.
Function tools¶
Supply OpenAI function definitions in tools. tool_choice can be auto, none,
required, or {"type":"function","function":{"name":"submit_summary"}}.
A required/named choice needs tool definitions; a named choice must reference a
defined function. Other tool types, including hosted web-search/computer tools,
are not exposed by this contract.
Check capabilities.forced_tool_choice in model discovery before requiring a
function. Sonnet 5.5 and Opus 5.5 support tools with auto and none, but reject required
and named choices. Use response_format.type=json_schema for structured JSON.
For all Anthropic models, none explicitly disables tool calls even when tool
definitions are supplied; it is never omitted or converted to auto.
Sonnet's effort values are none, low, medium, high, xhigh and max;
none disables up-front thinking using Anthropic's between_tools mode, which
can still think between tools. A profile selects its configured or central effort;
a concrete model selected without a profile or request effort keeps the API
default high. Request effort overrides the profile.
A function call is returned in choices[0].message.tool_calls; its
function.arguments is a JSON string. AIHub does not execute the function. The
caller executes its own authorized operation, then sends the assistant message
and corresponding role: "tool" messages with matching tool_call_id values.
For Haiku 5.5, reasoning_effort: "none" disables thinking. Profile-based calls
use the resolved profile effort (mini/default=low, tiny=none). Selecting a
concrete model without a profile or request effort keeps the API default medium. Other supported levels are low, medium, high,
xhigh and max. Unlike Sonnet 5.5 and Opus 5.5, Haiku 5.5 accepts forced function choices.
For every Anthropic model, including Haiku, Sonnet and Opus 5.5, when continuing
a tool call, retain the returned assistant message's
anthropic_content extension unmodified alongside its tool_calls. It contains
native content and opaque thinking signatures, not reasoning text for display.
Keep prior system/tools/messages unchanged and use the same Anthropic account.
Native replay must also respect API version 2026-10-21: a document block
directly inside an assistant's anthropic_content is rejected locally with
HTTP 422 and error.code="invalid_request", before configuration lookup or a
provider call. Supply new documents as user file parts through the document
contract above. Documents nested inside native tool-result blocks remain
accepted and unchanged, as do assistant text citations and signed thinking.
AIHub never moves or strips native blocks to repair an invalid history.
Streaming¶
Set stream: true for a provider/model advertising streaming: true. The endpoint
returns Content-Type: text/event-stream and Cache-Control: no-cache. It relays
OpenAI Chat Completions chunks and finishes with:
data: {"id":"chatcmpl-example","object":"chat.completion.chunk","created":1790596800,"model":"gpt-4o","choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"},"finish_reason":null}]}
data: [DONE]
SSE preserves the OpenAI Chat Completions HTTP contract and works with OpenAI
SDK streaming clients. Generation needs a request followed by a server-to-client
stream, so a separate WebSocket endpoint is unnecessary. A future bidirectional
real-time interaction, such as continuous audio, would require a separate contract.
Browser clients should use streaming fetch for this POST request with its JSON
body and authentication headers; native EventSource uses GET.
Request "stream_options":{"include_usage":true} for the upstream usage chunk.
It commonly has choices: []; do not assume every event has a first choice. Usage
is not requested automatically, and interrupted streams may never deliver it.
JSON output streams as text fragments in choices[].delta.content. Concatenate
them and wait for successful completion before parsing JSON. Tool argument deltas
also need assembly. Do not parse individual chunks as complete output objects.
curl --fail-with-body --no-buffer "$AIHUB_BASE_URL/chat/completions" \
-H "X-API-Key: $AIHUB_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"provider":"openai","model":"gpt-4o","messages":[{"role":"user","content":"Explain mentoring briefly."}],"stream":true,"stream_options":{"include_usage":true}}'
Failures before streaming starts use a regular HTTP error. Once HTTP 200 and the
stream have started, a later failure is emitted as an SSE data: event containing
the error envelope, followed by [DONE]; it cannot change the HTTP status already
sent. Client disconnect/cancellation closes the upstream stream. Anthropic
and OVH streaming are not exposed by this endpoint and return HTTP 422.
Discovery provenance fields¶
GET /models retains the OpenAI object: "list" and data envelope and adds:
| Field | Meaning |
|---|---|
default_provider |
Effective provider used when generation omits provider; remains global to this authenticated context when filtering by provider |
providers[].provider |
Supported provider ID present in the database or selected as the runtime default |
providers[].configured |
Whether this provider has a database catalogue entry |
providers[].is_default |
Whether this is the effective default provider |
providers[].profiles[].name |
Semantic profile, including the internal default profile |
model, reasoning_effort |
Effective shared-connector profile settings; null means unresolved model or unspecified effort, respectively |
source |
tenant for a tenant/application override, provider for a database mapping, code_fallback for a runtime code fallback, or unresolved when no provenance is available |
inherited_from |
Immediate parent profile when the resolver inherited a mapping; otherwise null |
available_in_genai |
Whether GenAI can select this profile against its configured model catalogue |
fallback.model, fallback.reasoning_effort |
Independent centralized code fallback, not an alternative active mapping |
On authenticated /models, the optional provider query filter applies to both
data and providers. Public /catalogue always lists the shared catalogue.
An unused fallback does not add a model to data. Responses use Cache-Control:
no-store; values are read on each request, and database failures retain the
sanitized error contract. No configuration is embedded into the static Swagger
schema or shared documentation site. Swagger examples are illustrative: use
Try it out on /models for authenticated client-specific values, or the live
public catalogue above for shared provider values.
For example, a valid explicit database mapping still has source: "provider"
when its model name equals fallback.model. An invalid tenant override that
falls back to a valid provider mapping also reports source: "provider".
Profile settings are not the final request options: explicit generation controls
and existing per-workflow policies still apply as documented above.
Documents¶
Messages can contain OpenAI-style inline file parts:
{
"provider": "azure",
"profile": "mini",
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "Summarize this document."},
{"type": "file", "file": {"filename": "program.pdf", "file_data": "data:application/pdf;base64,REPLACE_WITH_ACTUAL_BASE64_BYTES"}}
]
}
]
}
Replace the Base64 placeholder with actual file bytes. Supported MIME types are
application/pdf and
application/vnd.openxmlformats-officedocument.wordprocessingml.document.
This existing extraction workflow uses a configured OpenAI document model even
when the final completion selects another provider. PDF pages, including scans,
are sent in small page groups with the reasoning effort resolved from that profile. DOCX paragraphs and tables are read in order, with
embedded images sent for transcription. Extracted text remains user content.
Only send documents approved for that processing path. Selecting another completion
provider does not change document extraction routing. Incomplete extraction fails
with document_extraction_failed. AIHub does not persist uploaded documents and
does not log their content or filenames. URLs and provider file IDs are not accepted
as replacements for inline file bytes. General image/audio/video message parts are
not exposed by this endpoint.
Errors, limits and operational behavior¶
Generation/discovery errors use this envelope:
{
"error": {
"message": "The selected model does not support the requested option. See GET /models.",
"type": "invalid_request_error",
"param": "reasoning_effort",
"code": "unsupported_capability"
},
"request_id": "example-request-id"
}
| HTTP status | Code / category | Meaning and client action |
|---|---|---|
| 400 | invalid_request |
Provider rejected the request; check model-specific options/schema |
| 403 | AIHub authentication error | Missing/invalid key; existing AIHub detail response applies |
| 422 | invalid_request |
Invalid schema, bounds, conflicting controls or input-safety violation |
| 422 | unknown_model |
Selected provider/model/profile is absent or inconsistent in the catalogue |
| 422 | unsupported_capability |
Option/value/combination is not exposed for this model; inspect /models |
| 422 | document_extraction_failed |
Invalid, unsupported, oversized or incompletely extracted document |
| 429 | upstream_unavailable, type rate_limit_error |
Provider rate limit; use bounded client-side backoff |
| 502 | upstream_unavailable, type api_error |
Provider/configuration/timeout failure; retain request ID for support |
Request-validation errors do not echo the input body. param is populated for
recognized unsupported generation fields and can otherwise be null. Provider
exception messages, credentials and payloads are not copied into public errors.
Malformed JSON and unknown fields produce the same sanitized validation envelope.
AIHub's authentication middleware and infrastructure can additionally enforce their
existing error/rate-limit policies.
| Limit | Value |
|---|---|
| Messages | 1–100 |
| Parts per message | At most 30 |
| Text per message text part / string | 500,000 characters |
| Aggregate message text | 2,000,000 characters |
| Function tools | At most 20; 50,000 characters per text value and 200,000 total |
| General nested control objects | Shared budget: 20 levels, 50,000 nodes, 200,000 characters per text value, 2,000,000 total |
| Inline files | At most 10; total encoded file data at most 60 MiB of characters |
| Decoded file | At most 20 MiB |
| PDF pages | At most 120 |
| DOCX embedded images | At most 30, with archive expansion limits |
| Explicit output-token limit | 1–16,000 |
| Explicit cache breakpoints | At most 4 |
Non-stream completion execution has a 120-second timeout. OpenAI/Azure streaming uses the 120-second SDK timeout, including upstream reads; this is not a promise of unlimited idle stream duration. Document extraction happens before completion and has its own bounded processing. Generation does not retry with a different model/provider or remove rejected options. All supported providers use at most three attempts per request through the shared SDK retry policy. Transient connection errors, timeouts and retryable HTTP errors use exponential backoff; permanent errors fail without retries. The 120-second non-stream deadline includes retries and backoff. Streaming may retry opening the request but never replays a stream already returned to the caller. See provider retries.
Application logs identify the request, client when available, selected model, provider, status, elapsed time and reported input/output usage. They exclude prompt text, generated content, files and cache keys. A response is not stored as chatbot conversation history by these endpoints.
Using the OpenAI Python client¶
import json
import os
from openai import OpenAI
client = OpenAI(
base_url="http://127.0.0.1:8086/genai/api/v1",
api_key=os.environ["AIHUB_API_KEY"],
default_headers={"X-API-Key": os.environ["AIHUB_API_KEY"]},
)
completion = client.chat.completions.create(
model="gpt-4o", # Choose an ID from GET /models.
messages=[
{"role": "system", "content": "Return a JSON object with a summary field."},
{"role": "user", "content": "Describe mentoring for new engineers."},
],
response_format={"type": "json_object"},
max_completion_tokens=1000,
extra_body={"provider": "openai"},
)
choice = completion.choices[0]
if choice.finish_reason == "stop" and not choice.message.refusal and choice.message.content:
summary = json.loads(choice.message.content)
print(summary["summary"])
else:
print("Handle refusal, truncation, filtering or tool calls before using the result.")
provider and profile are AIHub extensions, so use the SDK's extra_body for
those fields. The SDK's typed chat.completions.create requires a model; for
profile-only or fully default requests, use a direct HTTP request with the bodies
shown above. Supplying both SDK model and extra_body.profile still selects the
model, not the profile. The official OpenAI /models response has fewer fields;
AIHub adds capability metadata without changing the list envelope.
Migrating from the initial GenAI contract¶
| Previous request | Current request |
|---|---|
"model": "profile:mini" |
"profile": "mini" |
"model": "openai/gpt-4o" |
"provider": "openai", "model": "gpt-4o" |
Mandatory model |
Omit all selectors to use the configured default profile |
max_tokens |
Still accepted; prefer max_completion_tokens, and send only one |
Flat "error": "unknown_model" |
Read error.code; the error is now an object |
| Automatic streaming usage request | Explicitly set stream_options.include_usage: true |
The old profile:... and combined provider/model selector syntax is replaced;
it is not a second supported routing path. Update callers before deploying this
contract change. Authentication and inline document extraction use the existing
AIHub mechanisms.
Contract references¶
- OpenAI structured outputs
- OpenAI prompt caching
- OpenAI reasoning controls
- GPT-6 Luna capabilities
- GPT-6.1 Terra capabilities
- GPT-6.1 Sol capabilities
- GPT-6 Astra capabilities
- AIHub provider configuration
The deployed Swagger schema and /models response describe the supported AIHub
contract. Native provider documentation describes upstream behavior and may include
features that this endpoint does not expose.
The generated genai HTTP reference is the authoritative source for parameters, schemas, responses and examples.