Skip to main content

xdog-ai

Unified LLM provider API.

A single interface over LLM providers — chat, embeddings, web search, and local Anthropic Messages, OpenAI Responses, and OpenAI Chat Completions facades. Ships the xdog-ai CLI for logging in, listing models, and talking to one from a terminal.

uv run xdog-ai login copilot
uv run xdog-ai chat copilot gpt-5.6-sol "Explain the CAP theorem in three sentences."

Antigravity login

uv run xdog-ai login antigravity
uv run xdog-ai models antigravity --sync --json

Login prints a Google authorization URL. Open it in your browser and grant consent; the command receives the callback on localhost:51121. When running XDOG over SSH, forward that port from the browser's machine to the remote host. The command does not import credentials from other applications.

OAuth client registration and backend endpoints are fixed internal provider settings, not environment or CLI options. No client configuration is required. XDOG uses the public installed-app registration used by CLIProxyAPI's Antigravity integration.

Access and refresh tokens obtained through login are stored in XDOG's own ~/.local/xdog/auth.json, with restrictive file permissions. Google account eligibility and backend availability still apply; the bundled registration is not a user token, API quota, or a guarantee of access.

Reading model capabilities

xdog-ai models <provider> uses the same compact one-line format for every provider: model ID, input/output token limits, selected wire protocol, and capability tags (reasoning, embedding, image_generation, web_search). Copilot rows also show the premium-request multiplier, with zero displayed as free. Limits of at least 1,000 tokens are abbreviated to whole thousands (k). The input value uses the reported prompt limit, falling back to context capacity when no separate input cap is available. A ? means unreported. An absent capability tag is not proof of non-support; use --details or --json to distinguish unknown values from explicit negatives. Antigravity's maxTokens supplies context capacity; a separate maxInputTokens value, when present, is retained as the input limit. Membership in Antigravity's imageGenerationModelIds catalogue also establishes image output support, without assuming the same model can read images. Absence from that list alone does not establish a negative capability.

web_search is also a capability tag. Model.supports_web_search is tri-state: True/False/None in Python and true/false/null in JSON. Antigravity uses explicit supportsWebSearch flags or positive membership in webSearchModelIds; unlisted models remain unknown. Copilot uses explicit capabilities.supports.web_search booleans when supplied. Missing metadata, ordinary tool calling, or availability of a Responses endpoint does not prove native web-search support. Old caches without this field retain unknown. The compact listing adds the tag only for known positive support; --details shows yes/no/unknown.

When a Copilot request produces a confirmed search-tool completion event, XDOG also remembers that positive observation for 24 hours. This evidence is scoped to the current login, survives model-catalogue refresh, and only fills otherwise unknown capability values. It does not override an explicit negative from the provider. Answer prose, source links, tool-start events, and failed searches do not establish support. Observations are saved separately from credentials; the cache contains a login fingerprint, never an access token.

Copilot's current search implementation requires an advertised Responses endpoint and selects that adapter even if ordinary chat prefers Chat Completions. It never silently treats a normal chat answer as a searched answer by ignoring the search option. Explicitly unsupported search requests fail; unknown capability can still be attempted through the supported adapter.

The selected protocol is shown for embeddings too: no generation endpoint does not mean no wire protocol. For exact token counts, all accepted protocols, separate image input/output support, friendly names, and transport details, use:

uv run xdog-ai models antigravity --details

generateContent is non-streaming generation; streamGenerateContent is streaming generation. These are two transport operations using the same Gemini wire format, not separate image capabilities. --json retains all metadata, including distinct input/output modalities, limits, and endpoint descriptions.

Generate images

Image generation follows the same provider/model command pattern as web search:

uv run xdog-ai search antigravity gemini-3.1-flash-lite "Find the official WeChat mini-program documentation."
uv run xdog-ai image antigravity gemini-3.1-flash-image \
  "A Chinese WeChat coffee shop home screen with warm neutral colors" \
  --aspect-ratio 9:16 --image-size 1K --output-dir design

image_generation is an alias for image. These commands and claw's generate_image tool share the native XDOG provider API and its stored OAuth credentials; no CLIProxyAPI server is needed. Authenticate with xdog-ai login antigravity and use a model from xdog-ai models antigravity (normally tagged image_generation).

The command writes uniquely named PNG/JPEG/WebP files without overwriting existing assets and prints their paths. --output-dir defaults to generated-images. For edits, repeat --reference PATH up to four times (10 MiB per image). Size/aspect-ratio defaults are left to the provider when omitted; supported values remain model-dependent. Add --json to print paths, accompanying text, model/provider, and usage metadata instead of human-readable output. Image data itself is not printed as base64.

The corresponding SDK method is provider.image_generation(model, prompt), or runtime.image_generation("provider/model", prompt). Pass an ImageGenerationRequest instead of a string to supply reference images, aspect ratio, or image size. ImageGenerationResponse contains decoded GeneratedImage bytes/MIME types, optional text, and usage. SDK calls do not write files; storage is the caller's responsibility.

Antigravity is the initial image-generation implementation. Providers without an implementation raise NotImplementedError, rather than falling back to text chat. Known non-image models and unsupported reference-image input are rejected before authentication. Unknown capability may be tried explicitly. Generation is not automatically retried, since retries may consume quota.

Local API proxy

uv run xdog-ai proxy --port 8082

The proxy exposes these routes through the same provider runtime:

  • POST /v1/messages — native Anthropic Messages when available, otherwise a best-effort projection.
  • POST /v1/messages/count_tokens — native Anthropic token counting when available, otherwise a marked local best-effort estimate.
  • POST /v1/responses — native OpenAI Responses when available, otherwise the strict stateless compatibility path documented below.
  • POST /v1/chat/completions — native OpenAI Chat Completions only. A model without that exact generation capability receives an OpenAI HTTP 400 error with param: "model"; the proxy does not translate the request.
  • GET /v1/models — the runtime's model catalog, including context_window, max_prompt_tokens, and max_output_tokens (zero means unknown), plus reasoning, input, and tool capabilities. GET /v1/models/{model_id} exposes the same metadata for one model.

Use a provider/model ID for deterministic routing. A bare model ID is accepted only when exactly one provider is active. For example, after logging into Copilot:

curl http://127.0.0.1:8082/v1/responses \
  -H 'Content-Type: application/json' \
  -d '{"model":"copilot/gpt-5.6-sol","input":"Hello!","store":false,"stream":true}'

An OpenAI SDK client can use base_url="http://127.0.0.1:8082/v1" and call client.responses.create(...) or client.chat.completions.create(...). Set --api-key <key> to authenticate the local listener through Authorization: Bearer <key> or x-api-key; these values are never forwarded upstream. Provider-resolved credentials and base URLs are authoritative. Without local proxy authentication, an SDK may use any dummy API key.

Configure an agent CLI

xdog-ai switch-cli replaces the standalone switch-cli script and also sets user-level defaults for XDOG's native clients:

uv run xdog-ai switch-cli coding copilot --model copilot/gpt-6-astra
uv run xdog-ai switch-cli claw copilot --model copilot/gpt-6-astra

These targets use the local XDOG model catalog and do not require a running proxy. Omit --model for interactive selection, or use coding list / claw list to list available providers. Refresh local metadata with xdog-ai models <provider> --sync if a model is missing.

  • coding updates default_model in ~/.config/xdog/coding/settings.json. Project settings, explicit model options, and resumed sessions can override this default.
  • claw updates model in ~/.config/xdog/claw/config.yaml. Group-specific model_id values are preserved. Restart the gateway to load the new default.

Both targets honor XDG_CONFIG_HOME, plus CODING_DIR or CLAW_DIR respectively, create missing config files, and preserve unrelated settings. YAML is serialized again, so comments/formatting may change; the original is retained in the .bak backup. They use the apps' existing metadata handling for context limits.

For external clients, start (or restart) the updated proxy first so its model endpoint includes token limits:

The proxy listens immediately using locally available model metadata and refreshes each active provider independently in the background (up to 60 seconds per provider). An unreachable provider does not prevent the server or other providers from working. With no cached metadata, that provider's model list may be empty until refresh succeeds. Refresh manually with xdog-ai models <provider> --sync after restoring connectivity. Cached model listings do not imply that upstream generation is online. switch-cli connects to an already-running server; it does not start one.

uv run xdog-ai switch-cli set-url http://127.0.0.1:8082
uv run xdog-ai switch-cli set-key your-proxy-key  # only needed for an authenticated proxy
uv run xdog-ai switch-cli codex list
uv run xdog-ai switch-cli codex copilot --model copilot/gpt-6-astra

# Interactive model selection, including Claude's Opus/Sonnet/Haiku roles:
uv run xdog-ai switch-cli codex copilot
uv run xdog-ai switch-cli claude copilot

The command saves its proxy settings in ~/.config/xdog/switch-cli.json, falling back to the old ~/.config/switch-cli/config.json until settings are first saved. switch-cli config shows the URL and whether a key is configured, without printing the credential. Existing client files receive a one-time .bak backup; updates are atomic and preserve unrelated TOML/JSON settings. Generated files containing credentials are readable only by the current user.

For Codex, the command respects CODEX_HOME (default ~/.codex) and sets:

  • model and the xdogproxy Responses provider, with a provider-specific bearer token; existing OpenAI login credentials in auth.json are left intact.
  • model_context_window from the selected model's provider metadata, replacing stale overrides. Unknown context limits produce an error instead of a guess.
  • model_auto_compact_token_limit to 90% of the smaller of the context window and prompt limit. If the prompt limit is unknown, it reserves the advertised maximum output tokens first.
  • model_catalog_json pointing to ~/.local/xdog/codex_models.json. The catalog includes chat models with tool support and known context limits, supported reasoning options, and generic coding-assistant instructions. It does not copy model-specific OpenAI prompts. Its format is tested with Codex 0.154.0.
  • web_search = "disabled" when native hosted search is not advertised, so Codex does not send an unsupported hosted tool by default.

After the first Codex switch, Copilot and Antigravity provider syncs refresh the generated catalog automatically, including syncs at proxy startup and xdog-ai models <provider> --sync. Refresh replaces only that provider's rows and updates the selected model's limits in registered Codex configs that still use the XDOG catalog/provider. No catalog or client config is created by sync before opting in through switch-cli. Export failures log a warning without failing provider sync. Restart Codex after switching or refreshing; explicit Codex profiles and command-line overrides can still take precedence.

For Claude Code, selecting a primary/Opus model also updates the saved model and sets CLAUDE_CODE_MAX_CONTEXT_TOKENS from provider metadata. It sets CLAUDE_CODE_AUTO_COMPACT_WINDOW using the same conservative prompt budget, capped at Claude Code's supported 1M auto-compaction window. No separate Claude catalog is required for this fixed-model setup. These environment overrides are global to the Claude process, including custom subagent models; rerun the switch command when changing the primary model, and use a smaller compaction window if mixing roles with smaller prompt limits. Unknown model limits clear stale overrides instead of retaining another model's window. Restart Claude Code after switching; custom model IDs may still produce an unknown-model notice even when the context limit is correctly configured.

xdog-ai switch-cli gemini <provider> also preserves the old script's Gemini .env configuration flow. Gemini CLI requires a Gemini-native endpoint; XDOG's proxy currently exposes Anthropic/OpenAI routes, so writing these settings alone does not make Gemini CLI work against it.

Generation requests must carry Content-Length. The raw local server rejects chunked request bodies with an HTTP 400 error before provider activity.

Native responses retain content-type, request-id/x-request-id, retry-after, openai-organization, openai-project, openai-version, openai-processing-ms, and x-ratelimit-*/ratelimit-* headers. Cookies, credentials, and transport headers are not forwarded.

Anthropic Messages compatibility

For Copilot models that advertise the native anthropic-messages protocol, POST /v1/messages preserves the Anthropic JSON and SSE contract:

  • All current request fields and nested content/tool unions are retained, including unknown future fields and opaque encrypted values. max_tokens: 0 is preserved for prompt-cache warming.
  • Both JSON responses and SSE events are forwarded in their native form. Unknown event and content-block types are retained, and an SSE error event is terminal rather than followed by a synthesized message_stop.
  • anthropic-version, anthropic-beta, anthropic-workspace-id, and anthropic-user-profile-id are forwarded. Repeated/provider beta tokens are combined without duplicates. Safe request ID, retry, workspace/organization, and rate-limit response headers are returned to the client.
  • Native non-streaming requests remain non-streaming upstream, so response-only fields, errors, status codes, and opaque blocks are not reconstructed from an internal event stream.

If the selected model exposes only an OpenAI protocol, the request uses an explicit best-effort projection instead of rejecting the model. Common text, base64 images, custom tools and tool results, sampling controls, tool choice, structured JSON output, metadata, service tier, and reasoning controls are mapped where the selected OpenAI endpoint has an equivalent. The response includes x-xdog-upstream-protocol: best-effort; any supplied controls or nested semantics that cannot be represented are listed as safe field paths in x-xdog-ignored-parameters. For example, Responses has no stop-sequence control, and Chat Completions does not receive Anthropic server-side web search.

The proxy validates protocol-independent request structure locally but leaves model-, beta-, and feature-specific semantic validation to the native upstream. Upstream Anthropic error envelopes and safe diagnostic headers are preserved.

Count Message Tokens

POST /v1/messages/count_tokens is a non-streaming, non-generation endpoint. For models with native Anthropic Messages support, the proxy forwards the complete stable, beta, future, and opaque request body to /v1/messages/count_tokens, changing only the wire model alias. Semantic Anthropic headers are retained; the obsolete token-counting-2024-11-01 beta is not injected. Native success bodies—including beta context_management details—and upstream failures, statuses, and safe headers remain intact. The operation never creates a message, executes tools, writes a prompt cache, or retries through generation.

A known model without native Anthropic support receives a deterministic local estimate with x-xdog-upstream-protocol: best-effort. This fallback is selected only before authentication or upstream I/O; unknown models and every failure after native admission remain errors. The estimate walks the raw request JSON, but it is not Anthropic's model-specific tokenizer and may differ from message usage, especially for Anthropic prompt framing, images/PDFs, signed or redacted thinking, server tools, and context management. Native upstream validation still governs unsupported server tools, MCP connectors, and URL or Files API media.

Native OpenAI Responses

A model advertising openai-responses uses the native transport. The proxy preserves current, deprecated, vendor-specific, unknown future fields, and nested union members. A request with stream: false remains genuinely non-streaming upstream, and native response bodies, error bodies, statuses, and safe headers are retained.

Streaming keeps Responses' named SSE framing (event: <type> plus data: <json>) and has no [DONE] marker. Unknown events are preserved. When the provider uses a wire model alias, the proxy restores the client model only at the top-level JSON response model and an SSE payload's response.model; it does not rewrite unrelated nested model fields.

Strict stateless Responses fallback

When a model does not advertise native openai-responses generation, the proxy uses its strict compatibility adapter and adds x-xdog-upstream-protocol: best-effort. The adapter rejects unsupported or unknown semantics with HTTP 400 rather than silently dropping them. Its JSON and SSE output is synthesized from provider-neutral events, so the following capabilities and limits apply only to this fallback:

  • JSON responses and SSE lifecycle, text, function-argument, and reasoning-summary events; completion, token-limit, and failure terminal events.
  • String input or full message/item history, instructions, system/developer messages, base64 image data URLs, function tools and results, parallel calls, max_output_tokens, temperature, and reasoning effort. Reasoning summaries and encrypted replay content depend on the upstream model/protocol. Encrypted reasoning retains its signed upstream identity across SSE and history replay. Reasoning events are buffered until block completion because Copilot may replace provisional IDs and ciphertext. Other output continues streaming afterward. Opaque, unprefixed upstream IDs are wrapped as rs_xdog_v1_... IDs for Codex. Short wrappers are reversible. If that wrapper would exceed the Responses API's 64-character limit, replayable upstream IDs use a deterministic rs_xdog_v2_... alias and a canonical digest-bound envelope in encrypted_content; upstream IDs that themselves exceed the limit remain summary-only and are not replayed. Native Responses input also migrates canonical legacy v1 reasoning history: replayable IDs are restored, while over-limit reasoning items are omitted before forwarding.
  • Codex Responses Lite additional_tools input items are normalized into the tool set, alongside top-level tools. Tool definitions use the same validation in either form; identical duplicates are deduplicated and conflicting definitions are rejected. These items are not chat messages.
  • Namespace groups containing function or custom tools are supported. Distinct, deterministic internal names avoid cross-namespace collisions; JSON responses, SSE items, and history replay preserve each original namespace and tool name.
  • Custom/freeform tools (including apply_patch) are adapted to functions with one string input argument and returned as custom_tool_call items. Custom input deltas are emitted after the complete JSON wrapper has been decoded, so escapes never leak into the raw input. Text and Lark/regex grammar formats are accepted; grammars are passed as model guidance, not enforced by a parser.
  • Client-executed tool_search is adapted to a function call and returned as tool_search_call. Replayed tool_search_output items expose the discovered tool definitions to subsequent model calls. The proxy never executes searches.
  • parallel_tool_calls accepts both true and false. The setting is forwarded to OpenAI Responses/Chat Completions upstreams and mapped to Anthropic tool_choice.disable_parallel_tool_use when using an Anthropic upstream.
  • text.verbosity accepts low, medium, and high. It is forwarded as text.verbosity to OpenAI Responses upstreams and as verbosity to Chat Completions upstreams. Anthropic has no native equivalent and ignores this hint. When omitted, the upstream default is unchanged.
  • Client-only client_metadata objects are accepted and ignored; they are not forwarded to models, echoed in responses, or persisted.
  • prompt_cache_key is accepted and forwarded to upstream Responses APIs as a cache-routing hint. Other upstream protocols ignore it; caching is controlled by the provider, not guaranteed or stored locally by this proxy.
  • Stateless: storage defaults to off. Send prior output items and tool results in input for subsequent turns. store=true, previous_response_id, conversations, item references, background jobs, and retrieval/deletion are not supported.
  • Hosted tools, nested namespace groups, strict schemas, structured output, remote image URLs, files/audio, forced tool choice, top_p, and automatic truncation are not supported. Unsupported controls are rejected with HTTP 400 rather than silently changing generation behavior.
  • Token usage includes cached input tokens in input_tokens; cached reads are also reported in input_tokens_details. The runtime does not separately track reasoning-token counts, so reasoning_tokens is reported as zero.

Native OpenAI Chat Completions

POST /v1/chat/completions requires an exact native openai-completions generation capability; embedding support through the same protocol adapter does not imply Chat support. Unsupported models receive an OpenAI invalid_request_error with HTTP 400 and param: "model", without a fallback request.

The proxy preserves current, deprecated, vendor-specific, unknown future fields and chunks. Non-streaming requests remain non-streaming upstream. Chat streams use data-only data: frames, never an event: field, and terminate with the exact data: [DONE]\n\n frame. A provider wire-model alias is restored only at the root model field in complete responses and stream chunks; unrelated nested fields remain unchanged.

If a session created by an older proxy reports Encrypted content item_id did not match the target item id, start a new conversation. Restarting the proxy cannot repair a mismatched ID/ciphertext pair already stored in client history; the encrypted payload is opaque and must not be reassigned to a generated ID.

Optional Codex client integration tests

With Codex CLI 0.154 installed, run:

XDOG_TEST_CODEX_CLI=1 uv run pytest packages/ai/tests/test_proxy_codex_cli.py -q

These use an isolated read-only workspace and a loopback mock provider—no paid upstream calls. They cover standard requests, Responses Lite namespaces with custom tools, and a real namespace tool-call/result round trip using Codex's read-only collaboration.list_agents tool, including encrypted reasoning ID preservation during replay, including provisional and opaque Copilot-style IDs. Hosted web search is disabled.

Part of xdog

This package is one piece of xdog, a local-first toolkit for building, running and scheduling LLM workflows. The centrepiece is xdog-flow — a typed workflow format and compiler.

Documentation: https://xdog.942295.xyz

Licence

Copyright (c) 2026 HugeMan <942295.xyz>

GNU Affero General Public License v3.0 or later — see LICENSE. Output compiled by xdog-flow is exempt; see the Generated Output Exception.

Metadata

Release files for xdog-ai 2.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for xdog-ai 2.4.1
File Size Uploaded
xdog_ai-2.4.1.tar.gz 213.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for xdog-ai 2.4.1
File Interpreter ABI Platform
xdog_ai-2.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 383.8 kB

Release files / xdog_ai-2.4.1.tar.gz

Download URL xdog_ai-2.4.1.tar.gz
Size 213.3 kB
Tags Source
SHA-256 checksum
How to use checksums
49a28f5fa26373a23fd4ff9a67c22e2cb0722792bb00869c49f4f39f453af0a9
BLAKE2b-256 checksum
How to use checksums
af06e34bd6fbc58e5b3c30fa3ca3b380951de0d8664f3674873e8a1bcf72b264
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / xdog_ai-2.4.1-py3-none-any.whl

Download URL xdog_ai-2.4.1-py3-none-any.whl
Size 170.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a4c9e41315478352974674d5c0003b73da592e9f30a8d6a76f58180b92725c43
BLAKE2b-256 checksum
How to use checksums
06a2882fb42528af4ef285031823eeb171cd7096c882e664ee415d1cf246da1b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.4.1 This release

2 release files

2.3.0

2 release files

2.2.0

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page