2026-09-14 21:09:03 +08:00
|
|
|
# xAI provider behavior
|
|
|
|
|
|
|
|
|
|
The following rules preserve the provider-specific behavior of the `xai` provider
|
|
|
|
|
across Aether's request and transport layers.
|
|
|
|
|
|
|
|
|
|
## Responses and tools
|
|
|
|
|
|
|
|
|
|
- HTTP requests drop `previous_response_id`. Clients must supply conversation
|
|
|
|
|
history; this provider does not add an HTTP response-ID history store.
|
|
|
|
|
- `metadata.user_id` is removed. Claude clients copy it onto converted Responses
|
|
|
|
|
bodies and xAI rejects the field.
|
|
|
|
|
- Preserve requested `reasoning.encrypted_content`. On a native Responses-to-Responses
|
|
|
|
|
hop, keep provider-owned input items instead of rebuilding them through the canonical
|
|
|
|
|
format. xAI encrypted reasoning may have IDs that do not use OpenAI's `rs` prefix.
|
|
|
|
|
Aether's Gemini signature carriers remain excluded from xAI replay.
|
|
|
|
|
- The replay policy is selected from the configured provider type. A model called
|
|
|
|
|
`grok-*` on another provider does not opt into that policy. WebSocket continuation
|
|
|
|
|
metadata retains the selected policy across reconnects.
|
|
|
|
|
- A regular client function called `web_search` remains a function. Claude hosted
|
|
|
|
|
search choices are resolved against the original typed tool declaration, including
|
|
|
|
|
declarations with a different name.
|
|
|
|
|
- When only `image_generation` is allowed, keep only that tool and retain the requested
|
|
|
|
|
`auto` or `required` mode. For mixed allowed-tool lists, remove the image choice while
|
|
|
|
|
preserving the other allowed entries, as required by xAI's tool-choice schema.
|
|
|
|
|
- Reasoning effort is stripped for models that do not accept it.
|
|
|
|
|
- OpenAI-style image reference aliases in a request body are rewritten to xAI's
|
|
|
|
|
shape without touching chat message parts.
|
|
|
|
|
|
|
|
|
|
## Routing and credentials
|
|
|
|
|
|
|
|
|
|
OAuth requests default to `https://cli-chat-proxy.grok.com/v1`; API-key or
|
|
|
|
|
`using_api=true` requests default to `https://api.x.ai/v1`. Explicit custom gateways
|
|
|
|
|
are preserved. Compact remains on the official endpoint. CLI identity headers are
|
|
|
|
|
applied where the selected upstream requires them.
|
|
|
|
|
|
|
|
|
|
Account binding uses the xAI device code flow: the gateway requests a device code,
|
|
|
|
|
the operator authorizes it out of band, and the gateway polls for the token set.
|
|
|
|
|
There is no local callback listener, so headless deployments can bind accounts.
|
|
|
|
|
Refresh tokens can also be imported individually or in batches, and are rotated
|
|
|
|
|
on refresh.
|
|
|
|
|
|
|
|
|
|
Quota refresh reads `/user` and `/billing?format=credits` and stores a structured
|
|
|
|
|
usage snapshot. A prepaid balance keeps an account selectable after the weekly
|
|
|
|
|
allowance is exhausted. API-key accounts skip the subscription billing surface.
|
|
|
|
|
|
2026-09-14 21:17:21 +08:00
|
|
|
## Images and videos
|
|
|
|
|
|
|
|
|
|
OAuth media requests default to `https://cli-chat-proxy.grok.com/v1`; API-key or
|
|
|
|
|
`using_api=true` requests default to `https://api.x.ai/v1`. Explicit custom gateways
|
|
|
|
|
are preserved. Compact remains on the official endpoint. CLI identity headers are
|
|
|
|
|
applied to media requests and restored when a persisted video task's polling transport
|
|
|
|
|
is reconstructed.
|
|
|
|
|
|
|
|
|
|
Aether's OpenAI-compatible task parser accepts xAI's `request_id` creation field,
|
|
|
|
|
status aliases such as `pending` and `done`, nested `video.url` and `video.duration`,
|
|
|
|
|
and failure payloads containing `code` / `error` without a status. Existing OpenAI
|
|
|
|
|
`id` takes precedence. The client receives Aether's local task ID; polling uses the
|
|
|
|
|
upstream task ID and selected credential. Completed video downloads use the returned
|
|
|
|
|
media URL without forwarding provider authentication headers to the media host.
|
|
|
|
|
|
|
|
|
|
### Public video protocols
|
|
|
|
|
|
|
|
|
|
The xAI provider supports two video surfaces:
|
|
|
|
|
|
|
|
|
|
| Operation | xAI native | OpenAI compatible |
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
| Create | `POST /v1/videos/generations` | `POST /openai/v1/videos` |
|
|
|
|
|
| Edit / extend | `POST /v1/videos/edits`, `POST /v1/videos/extensions` | — |
|
|
|
|
|
| Retrieve | `GET /v1/videos/{request_id}` | `GET /openai/v1/videos/{id}` |
|
|
|
|
|
| Download | use the returned `video.url` | `GET /openai/v1/videos/{id}/content` |
|
|
|
|
|
|
|
|
|
|
For xAI, `POST /v1/videos` is a native creation alias. Other providers retain
|
|
|
|
|
Aether's existing OpenAI-compatible `/v1/videos` behavior. xAI callers using
|
|
|
|
|
OpenAI `seconds` / `size` parameters must use `/openai/v1/videos`. The adapter
|
|
|
|
|
maps these to numeric `duration`, `aspect_ratio`, and `resolution`; it also adapts
|
|
|
|
|
image references. This implementation defaults to 4 seconds, portrait, and 720p,
|
|
|
|
|
clamps `duration` to 1-15, and validates inputs. Explicit native requests retain
|
|
|
|
|
native parameters and additional provider fields.
|
|
|
|
|
|
|
|
|
|
Default xAI creation targets `/videos/generations` on the selected upstream host.
|
|
|
|
|
Explicit custom endpoint paths still take precedence. Native generation, editing,
|
|
|
|
|
and extension paths only select xAI provider candidates.
|
|
|
|
|
|
|
|
|
|
Native creation returns `request_id`; native retrieval preserves `done`, nested
|
|
|
|
|
`video.url`, and provider fields such as `respect_moderation`. The identifier is
|
|
|
|
|
an opaque Aether task ID so queries remain scoped to the owning user and pinned
|
|
|
|
|
to the original upstream task and credential. The explicit `/openai/v1/videos`
|
|
|
|
|
surface projects `id`, `completed`, and `video_url`.
|
|
|
|
|
|
|
|
|
|
The task row records the native client protocol as `xai:video`, while its provider
|
|
|
|
|
transport remains `openai:video`. This survives restart without storing request
|
|
|
|
|
bodies or credentials. Raw native responses are cached only in memory; after
|
|
|
|
|
reconstruction the gateway refreshes from the original provider to recover its
|
|
|
|
|
response fields, including for completed tasks. If refreshing is unavailable,
|
|
|
|
|
the stored task still provides the native status and media URL projection.
|
|
|
|
|
|
|
|
|
|
OpenAI/xAI task persistence supplies a stable 16-character `short_id`, as required
|
|
|
|
|
by the PostgreSQL schema. Existing rows retain their original short ID across
|
|
|
|
|
reconstruction, including legacy embedded snapshots. This internal identifier is
|
|
|
|
|
separate from the opaque local task ID returned to clients; no schema change or
|
|
|
|
|
historical row rewrite is needed.
|
|
|
|
|
|
|
|
|
|
Task retrieval and content downloads are admitted by the production GET execution
|
|
|
|
|
gate. Reconstructed tasks resolve proxy nodes, system proxy defaults, tunnel affinity,
|
|
|
|
|
and transport profiles through the same deployment resolver used for creation;
|
|
|
|
|
configured proxy routes must not silently turn into direct requests after restart.
|
|
|
|
|
|
|
|
|
|
### Runtime configuration
|
|
|
|
|
|
|
|
|
|
Standalone Rust deployments must set
|
|
|
|
|
`AETHER_GATEWAY_VIDEO_TASK_TRUTH_SOURCE_MODE=rust-authoritative` and restart the
|
|
|
|
|
gateway to enable video task retrieval, polling, and content downloads. The CLI's
|
|
|
|
|
legacy default is `python-sync-report`: creation can return a task ID in that mode,
|
|
|
|
|
but the local task read/refresh paths are disabled and may return HTTP 503.
|
|
|
|
|
|
|
|
|
|
When the gateway also serves the frontend, `/openai/v1/videos` and its subpaths
|
|
|
|
|
must bypass the static SPA handler and be mounted as API routes. Otherwise a
|
|
|
|
|
successful-looking HTTP 200 response to a video query may contain `text/html`
|
|
|
|
|
instead of the task's JSON response. The lifecycle regression includes the static
|
|
|
|
|
frontend to cover this production configuration.
|
|
|
|
|
|
2026-09-14 21:09:03 +08:00
|
|
|
## Regression coverage
|
|
|
|
|
|
|
|
|
|
The format tests cover client and hosted search choices, image-only and mixed tool
|
|
|
|
|
restrictions, encrypted reasoning replay, image reference rewriting, and unchanged
|
|
|
|
|
OpenAI replay restrictions. Transport tests cover OAuth/API-key/custom routing and
|
2026-09-14 21:17:21 +08:00
|
|
|
media identity headers. Video-task tests exercise creation, polling, terminal
|
|
|
|
|
projection, persistence fields, content-download planning, and status-less errors
|
|
|
|
|
using local fixtures. They do not make paid generation requests.
|
|
|
|
|
|
|
|
|
|
The HTTP regression exercises all native creation paths and the compatibility
|
|
|
|
|
prefix through the public router and candidate planner, then checks polling,
|
|
|
|
|
cross-user denial, persistence, retrieval from a fresh gateway instance, and downloads
|
|
|
|
|
through both prefixes without leaking authorization to the media host. It uses the
|
|
|
|
|
real HTTP executor and a managed proxy node backed by a local test server, with no
|
|
|
|
|
execution-runtime override. The background poller also has a real HTTP proxy-node
|
|
|
|
|
regression, so production method guards and transport reconstruction are exercised.
|
|
|
|
|
CI also runs the same HTTP lifecycle with the PostgreSQL repository and the
|
|
|
|
|
production column constraints/indexes in an isolated temporary table. This catches
|
|
|
|
|
persistence failures that the in-memory repository cannot expose. The test uses
|
|
|
|
|
local `initdb`, `postgres`, and `pg_ctl` (already provided by the gateway CI job),
|
|
|
|
|
or an explicit `AETHER_TEST_DATABASE_URL` pointing to an isolated test database.
|
2026-09-14 21:09:03 +08:00
|
|
|
|
|
|
|
|
```sh
|
2026-09-14 21:17:21 +08:00
|
|
|
cargo test -p aether-ai-formats -p aether-provider-transport -p aether-video-tasks-core --lib
|
2026-09-14 21:09:03 +08:00
|
|
|
cargo test -p aether-gateway --lib xai
|
|
|
|
|
```
|