mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 16:37:46 +08:00
feat(providers): add xAI provider with device code OAuth
Add a separate `xai` provider type for xAI Grok CLI subscription accounts. It is independent of the existing `grok` provider, which reverse-proxies grok.com with browser cookies; behavior of `grok` is unchanged. Account binding uses the xAI device code flow, so no local callback listener is needed and headless deployments can bind accounts. Refresh tokens can also be imported individually or in batches, and are rotated on refresh. OAuth requests default to the cli-chat-proxy Responses API; API keys and compact stay on api.x.ai. Explicit custom gateways are preserved. Only `openai:responses` and `openai:responses:compact` are exposed; Chat, Claude and Gemini clients reach the provider through Aether's existing cross-format conversion rather than new native endpoints. Upstream Responses payloads are sanitized for what xAI actually rejects: `previous_response_id` and `metadata.user_id` are dropped, hosted `tool_choice` is rewritten, `web_search` is restored for converted clients, `image_generation` is stripped on older Grok conversation models, unsupported reasoning effort is removed, and requested `reasoning.encrypted_content` is preserved with a replay policy keyed on the configured provider type rather than the model name. 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. The admin UI shows remaining weekly quota as a labeled bar in the provider drawer and the pool list. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
# 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.
|
||||
|
||||
## 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
|
||||
the fixed-provider endpoint template. OAuth tests cover the device code lifecycle,
|
||||
token import, and batch import.
|
||||
|
||||
```sh
|
||||
cargo test -p aether-ai-formats -p aether-provider-transport -p aether-oauth --lib
|
||||
cargo test -p aether-gateway --lib xai
|
||||
```
|
||||
Reference in New Issue
Block a user