feat(ai-providers): add xAI as an AI provider#1865
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2bb8762fcd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| throw AIProviderError.mapHTTPError( | ||
| statusCode: httpResponse.statusCode, | ||
| body: body, | ||
| treatForbiddenAsAuthFailure: true | ||
| ) |
There was a problem hiding this comment.
Treat invalid_grant as an auth failure
If an xAI refresh token is expired or revoked and the token endpoint returns the standard OAuth 400 {"error":"invalid_grant"} response, this maps it to .serverError; XAITokenStore.refresh only clears stored credentials on .authenticationFailed, so TablePro leaves a dead xAI session in Keychain and every later request retries the same bad refresh token instead of prompting the user to sign in again. Please map token-endpoint invalid_grant/auth 400s to an authentication failure here.
Useful? React with 👍 / 👎.
| if let effort = options.reasoningEffort { | ||
| body["reasoning"] = ["effort": effort.xaiReasoningEffort] |
There was a problem hiding this comment.
Gate reasoning by the selected Grok model
When the no-key subscription path is used with grok-composer-2.5-fast from XAI.subscriptionModelIDs and a reasoning effort is set on the provider (for example after using Grok 4.5 or an inline model override), this unconditional block sends reasoning.effort to Composer, which does not accept configurable reasoning effort and will reject the request. Gate this field to the Grok models that support it, or clear/ignore the effort when switching to non-reasoning subscription models.
Useful? React with 👍 / 👎.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
Adds xAI (Grok) as an AI provider. One provider, two auth paths, chosen by whether an API key is present (the same either/or shape Cursor already uses):
api.x.aiResponses API and bills xAI API credits. Models Grok 4.5 and Grok 4.3.auth.x.ai, then inference throughcli-chat-proxy.grok.com.Design
The API-key path reuses
OpenAIResponsesProviderwith a newdialectparameter (default.openAI, so existing OpenAI behavior is unchanged). The subscription path is a full OAuth stack inTablePro/Core/AI/XAI/, mirroring the existing ChatGPT Codex provider (browser-redirect PKCE, loopback callback server, actor token store). The stream parser gainedresponse.reasoning_text.deltaso Grok's reasoning renders in chat; this is additive and inert for OpenAI and Copilot.Adding the provider is otherwise a descriptor registration: the Add Provider menu, Keychain, and settings storage need no changes because they are all descriptor-driven.
What the research changed
Model IDs and OAuth constants were verified against live sources, not memory:
grok-4,grok-4-0709,grok-3,grok-3-mini, andgrok-code-fast-1were retired on 2026-05-15 and now silently redirect togrok-4.3, billing at its rate instead of erroring. Shipping one would be an invisible cost regression, so the curated list is only the two current models. A test asserts none of the retired slugs are curated.grok-4.5always reasons and rejectsreasoning_effortvalues outside low/medium/high; the effort picker and the wire mapping both clamp to that set.Auth risk, stated plainly
The subscription path reuses xAI's own first-party Grok Build OAuth client, because xAI publishes no third-party client registration. Two consequences, both handled rather than hidden:
X-XAI-Token-Auth: xai-grok-cliandx-grok-client-version: 0.2.93, a version floor xAI can bump. Every fragile constant lives in one file (XAI.swift) so a bump is a one-line change, and the API-key path stays fully working as a fallback.Error handling keeps the two surfaces distinct: a subscription-exhausted
403from the inference proxy stays a server error (it is a billing signal), while a403from the token endpoint is treated as an auth failure so a dead session is cleared.Tests
New deterministic unit tests (no network, Keychain, or pasteboard): registration and capabilities, PKCE, OAuth authorize/exchange/refresh encoding, token store refresh and single-flight, the Grok CLI request headers and body, the
.xaireasoning body shape, and thereasoning_text.deltaparser case. A new test also asserts everyAIProviderTypehas a registered descriptor.Docs
docs/features/ai-assistant.mdx(provider count, add list, an xAI subsection, tool-calling support) anddocs/customization/settings.mdx.Still needs a live key before merge
Three things the public docs do not fully pin down, to confirm with a real key:
api.x.ai/v1/responsesaccepts the.xaireasoning body (reasoning: {effort}only).grok-4.5/grok-4.3viax-grok-model-override, or only its own catalog (grok-build,grok-composer-2.5-fast,grok-4.5-build-free). If it rejects them, the model picker should be gated by auth mode.Built and linted locally with
swiftlint --strict. Not yet run through a full Xcode build.