xAI provider
Standalone — usable in any Node project, no
@warlock.js/corerequired.
@warlock.js/ai-xai is the provider adapter for xAI’s Grok models. xAI
speaks the OpenAI Chat Completions protocol, so XaiSDK is a thin wrapper
over the OpenAI adapter: it constructs one
internal OpenAISDK pointed at https://api.x.ai/v1 and labeled
provider: "xai", then delegates everything to it — while injecting xAI’s own
capability inference so Grok model names resolve to the right vision and
reasoning flags.
You get a vendor-neutral ModelContract — the same shape every agent,
workflow, and supervisor already speaks.
Install
Section titled “Install”npm install @warlock.js/ai @warlock.js/ai-xaiyarn add @warlock.js/ai @warlock.js/ai-xaipnpm add @warlock.js/ai @warlock.js/ai-xaiConstruct
Section titled “Construct”XaiSDK is a class holding a long-lived wrapped client. Build it once at boot
and reuse it everywhere — every model it produces shares the same connection
pool, auth, and rate-limit state.
import { XaiSDK } from "@warlock.js/ai-xai";
const xai = new XaiSDK({ apiKey: process.env.XAI_API_KEY! });baseURLdefaults tohttps://api.x.ai/v1(the xAI OpenAI-compatible endpoint). Override only to target a proxy/gateway.providerdefaults to"xai"and flows through toModelContract.provider,AgentReport.model.provider, and logs. Set it to relabel a gateway upstream.- Every other upstream client option (
timeout,maxRetries,defaultHeaders,fetch, …) is forwarded verbatim to the wrapped client.
const xai = new XaiSDK({ apiKey: process.env.XAI_API_KEY!, baseURL: "https://my-gateway.internal/xai", provider: "xai-gateway",});First call
Section titled “First call”Build a model, hand it to an agent, run it. execute() never throws —
failures land in error as a typed AIError.
import { ai } from "@warlock.js/ai";import { XaiSDK } from "@warlock.js/ai-xai";
const xai = new XaiSDK({ apiKey: process.env.XAI_API_KEY! });
const assistant = ai.agent({ model: xai.model({ name: "grok-4" }), systemPrompt: "You are a concise senior TypeScript engineer.",});
const { text, usage, error } = await assistant.execute("Why use generics?");
if (error) { console.warn(error.code, error.category);} else { console.log(text, usage.total);}Use as a model
Section titled “Use as a model”xai.model({ name: "grok-4" }); // vision + reasoning auto-truexai.model({ name: "grok-3-mini" }); // reasoning auto-true, vision falsexai.model({ name: "grok-2-vision" }); // vision auto-truexai.model({ name: "grok-4", temperature: 0.2 }); // sampling controlsxai.model({ name: "grok-3", vision: true }); // explicit capability overrideReturns a ModelContract you pass straight into any agent, workflow, or
supervisor.
Models and capabilities
Section titled “Models and capabilities”This adapter ships xAI’s own name lists — Grok ids don’t match OpenAI’s
gpt-* / o* prefixes. Capabilities are injected per model before delegating;
an explicit value always wins over inference.
| Model | vision | reasoning |
|---|---|---|
grok-4 | true | true (reasoning-first) |
grok-3 | false | false |
grok-3-mini | false | true (the “think” variant) |
grok-2-vision | true | false |
grok-2 | false | false |
vision— inferredtruefor thegrok-4andgrok-2-visionprefixes;falseotherwise.reasoning— inferredtrueforgrok-4andgrok-3-mini; drives whetherreasoning.effortis forwarded asreasoning_efforton the wire.structuredOutput/promptCachingand the image/PDF/audio wire mapping all come from the wrapped OpenAI adapter unchanged.
Override vision, reasoning, or structuredOutput explicitly via
.model({ name, vision?, reasoning?, structuredOutput? }).
Reasoning effort
Section titled “Reasoning effort”grok-4 and grok-3-mini are reasoning-capable. reasoning.effort
("low" | "medium" | "high" | "none") maps verbatim to the
OpenAI-compatible reasoning_effort param. When capabilities.reasoning
is false (e.g.
grok-3), the option is dropped — the adapter never forwards it. Pin
reasoning: true to force it for a custom/gateway id.
const model = xai.model({ name: "grok-4" }); // reasoning auto-trueawait model.complete(messages, { reasoning: { effort: "high" } }); // → reasoning_effort: "high"Vision
Section titled “Vision”Image attachments reach the wire only on a vision-capable model (grok-4,
grok-2-vision); the agent’s modality gate throws otherwise. Mapping to
OpenAI image_url parts is identical to the OpenAI adapter.
const model = xai.model({ name: "grok-4" });
const response = await model.complete([ { role: "user", content: [ { type: "text", text: "What's in this photo?" }, { type: "image", source: { url: "https://example.com/cat.jpg" } }, ], },]);Pricing and usage
Section titled “Pricing and usage”Every response reports token usage (input, output, total,
cachedTokens) — on model.complete() and the streaming done chunk
too. prompt_tokens contains the cached tokens
(inputIncludesCache: true).
xAI has no built-in price table: every model on
xAI’s pricing page (checked
2026-10-17) bills all tokens at a higher long-context rate once the
prompt reaches 200k tokens, which a flat rate cannot express. Attach a
pricing registry — keyed by model name, in USD per million tokens
(input, output, optional cachedInput; xAI has no cache-write
charge). Resolution: per-model pricing > SDK registry > undefined
(no cost computed).
import { ai, totalCost } from "@warlock.js/ai";import { XaiSDK } from "@warlock.js/ai-xai";
const xai = new XaiSDK({ apiKey: process.env.XAI_API_KEY ?? "", pricing: { // Below-200k rates; long prompts cost more. "grok-4.3": { input: 1.25, output: 2.5, cachedInput: 0.2 }, },});
const { usage } = await ai.agent({ model: xai.model({ name: "grok-4.3" }) }).execute("hi");console.log(totalCost(usage.cost)); // undefined when unpricedPrompt caching. xAI’s Chat Completions accepts prompt_cache_key,
used as the x-grok-conv-id cache key
(maximizing cache hits).
Set promptCacheKey on the model — sent while the SDK keeps its default
"xai" provider label:
const model = xai.model({ name: "grok-4.3", promptCacheKey: "conversation:42" });An overloaded endpoint (HTTP 503) surfaces as ProviderOverloadedError
(code: "PROVIDER_OVERLOADED", retryable: true); every provider error
carries status, retryable and billed (false for 401 / 402 / 403,
otherwise "unknown").
Need an offline token estimate first? xai.count(text) returns a fast
character-heuristic approximation — good for budgeting and quota guards,
not for billing.
Embeddings and images
Section titled “Embeddings and images”xAI does not currently expose a public embeddings endpoint.
xai.embedder({...}) is wired for protocol parity, but a call to embed() /
embedMany() fails upstream — point an embedder at a dedicated provider like
OpenAI for vectors.
xai.image({...}) delegates to the wrapped OpenAI Images adapter, which only
recognizes gpt-image-* / dall-e-* ids and rejects anything else at
construction. xAI’s image generation (“Grok Imagine”) is a separate, non-
OpenAI-Images-compatible surface, so use the
OpenAI provider’s image() for OpenAI-Images
generation.
See also
Section titled “See also”- OpenAI provider — the wrapped adapter; full wire / streaming / structured-output / multipart surface applies here.
@warlock.js/ai— the agent, tool, and system-prompt surface these models plug into.- For provider-specific notes and the latest model ids, see the
warlock-js-ai-xaiskill.
Provider body options and token estimates
Section titled “Provider body options and token estimates”This adapter delegates Chat Completions to the OpenAI adapter, so
providerOptions.openai.body adds native Chat request fields. Other provider
keys are ignored. Plain-object fields merge one level deep with neutral options;
caller-provided keys win, while arrays, scalars and null replace the field.
The OpenAI adapter’s protected conversation, tool and transport fields remain
protected here too. Send only fields supported by this provider; do not reuse
OpenAI-only bodies across a mixed-provider fallback chain.
Models from this adapter have no native countTokens method. Check
model.countTokens before using an optional native counting capability;
sdk.count(text) is a separate offline estimate.
Native web search requires Responses
Section titled “Native web search requires Responses”xAI’s official web-search guide
(verified 2026-10-18) sends { type: "web_search" } to POST /v1/responses.
XaiSDK currently wraps Chat Completions, so native web_search and x_search
provider tools are explicitly unsupported and throw InvalidRequestError
before transport, explaining that the vendor’s Responses API is required.
Use xAI’s own Responses SDK for native search. The framework’s
api: "responses" adapter remains direct-OpenAI-only; passing a tool definition
does not change this transport. No xAI hosted-search request count or list fee
is guessed. See compatible native search.