mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 08:27:46 +08:00
Expose the xAI Imagine image and video surfaces on top of the `xai` provider, and make the shared OpenAI video-task layer survive the production configuration they need. Native video requests live under /v1 (generations, edits, extensions, with /v1/videos as a creation alias that only selects xAI candidates); the OpenAI-compatible adapter stays under /openai/v1/videos and maps `seconds` / `size` onto numeric duration, aspect ratio and resolution. Clients receive an opaque Aether task ID scoped to the owning user; polling uses the upstream task ID and the original credential, and completed downloads fetch the returned media URL without forwarding provider authorization to the media host. Three fixes to the shared video layer are required for this to work outside tests: - OpenAI/xAI task persistence now supplies a stable 16-character short_id, which the PostgreSQL schema requires. Existing rows keep their original value across reconstruction, so no schema change or historical rewrite is needed. - Task retrieval and content downloads are admitted by the production GET execution gate, and reconstructed tasks resolve proxy nodes, system proxy defaults, tunnel affinity and transport profiles through the same deployment resolver used for creation. A configured proxy route no longer silently becomes a direct request after restart. - When the gateway also serves the frontend, /openai/v1/videos and its subpaths bypass the static SPA handler. Otherwise a video query returns HTTP 200 with text/html instead of the task JSON. Co-Authored-By: Claude Opus 5 <[email protected]>
148 lines
8.3 KiB
Markdown
148 lines
8.3 KiB
Markdown
# 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.
|
|
|
|
## 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.
|
|
|
|
## 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
|
|
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.
|
|
|
|
```sh
|
|
cargo test -p aether-ai-formats -p aether-provider-transport -p aether-video-tasks-core --lib
|
|
cargo test -p aether-gateway --lib xai
|
|
```
|