@gullabs/xai 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -43
- package/dist/index.cjs +884 -551
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +233 -42
- package/dist/index.d.ts +233 -42
- package/dist/index.js +884 -553
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,546 @@ async function buildXaiClient(auth) {
|
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
+
var webSearchFlags = {
|
|
34
|
+
enableImageUnderstanding: zod.z.boolean().optional().meta({
|
|
35
|
+
title: "Enable Image Understanding",
|
|
36
|
+
description: "Ask xAI to analyze images found during web search."
|
|
37
|
+
}),
|
|
38
|
+
enableImageSearch: zod.z.boolean().optional().meta({
|
|
39
|
+
title: "Enable Image Search",
|
|
40
|
+
description: "Ask xAI to include image search results."
|
|
41
|
+
})
|
|
42
|
+
};
|
|
43
|
+
var XaiWebSearchToolSchema = zod.z.union([
|
|
44
|
+
zod.z.strictObject({
|
|
45
|
+
type: zod.z.literal("web_search"),
|
|
46
|
+
allowedDomains: zod.z.array(zod.z.string()).max(5),
|
|
47
|
+
...webSearchFlags
|
|
48
|
+
}),
|
|
49
|
+
zod.z.strictObject({
|
|
50
|
+
type: zod.z.literal("web_search"),
|
|
51
|
+
excludedDomains: zod.z.array(zod.z.string()).max(5),
|
|
52
|
+
...webSearchFlags
|
|
53
|
+
}),
|
|
54
|
+
zod.z.strictObject({
|
|
55
|
+
type: zod.z.literal("web_search"),
|
|
56
|
+
...webSearchFlags
|
|
57
|
+
})
|
|
58
|
+
]).meta({
|
|
59
|
+
title: "XaiWebSearchTool",
|
|
60
|
+
description: "xAI web_search server tool. allowedDomains and excludedDomains are mutually exclusive (max 5)."
|
|
61
|
+
});
|
|
62
|
+
var xSearchFlags = {
|
|
63
|
+
fromDate: zod.z.iso.date().optional().meta({
|
|
64
|
+
title: "From Date",
|
|
65
|
+
description: "Inclusive ISO-8601 date lower bound for X search."
|
|
66
|
+
}),
|
|
67
|
+
toDate: zod.z.iso.date().optional().meta({
|
|
68
|
+
title: "To Date",
|
|
69
|
+
description: "Inclusive ISO-8601 date upper bound for X search."
|
|
70
|
+
}),
|
|
71
|
+
enableImageUnderstanding: zod.z.boolean().optional().meta({
|
|
72
|
+
title: "Enable Image Understanding",
|
|
73
|
+
description: "Ask xAI to analyze images in matched posts."
|
|
74
|
+
}),
|
|
75
|
+
enableVideoUnderstanding: zod.z.boolean().optional().meta({
|
|
76
|
+
title: "Enable Video Understanding",
|
|
77
|
+
description: "Ask xAI to analyze videos in matched posts."
|
|
78
|
+
})
|
|
79
|
+
};
|
|
80
|
+
var XaiXSearchToolSchema = zod.z.union([
|
|
81
|
+
zod.z.strictObject({
|
|
82
|
+
type: zod.z.literal("x_search"),
|
|
83
|
+
allowedXHandles: zod.z.array(zod.z.string()).max(20),
|
|
84
|
+
...xSearchFlags
|
|
85
|
+
}),
|
|
86
|
+
zod.z.strictObject({
|
|
87
|
+
type: zod.z.literal("x_search"),
|
|
88
|
+
excludedXHandles: zod.z.array(zod.z.string()).max(20),
|
|
89
|
+
...xSearchFlags
|
|
90
|
+
}),
|
|
91
|
+
zod.z.strictObject({
|
|
92
|
+
type: zod.z.literal("x_search"),
|
|
93
|
+
...xSearchFlags
|
|
94
|
+
})
|
|
95
|
+
]).meta({
|
|
96
|
+
title: "XaiXSearchTool",
|
|
97
|
+
description: "xAI x_search server tool. allowedXHandles and excludedXHandles are mutually exclusive (max 20)."
|
|
98
|
+
});
|
|
99
|
+
var XaiToolsSchema = zod.z.union([
|
|
100
|
+
zod.z.tuple([XaiWebSearchToolSchema]),
|
|
101
|
+
zod.z.tuple([XaiXSearchToolSchema]),
|
|
102
|
+
zod.z.tuple([XaiWebSearchToolSchema, XaiXSearchToolSchema]),
|
|
103
|
+
zod.z.tuple([XaiXSearchToolSchema, XaiWebSearchToolSchema])
|
|
104
|
+
]).meta({
|
|
105
|
+
title: "XaiTools",
|
|
106
|
+
description: "xAI Live Search tools. At most one web_search and at most one x_search."
|
|
107
|
+
});
|
|
108
|
+
var XaiProviderOptionsSchema = zod.z.strictObject({
|
|
109
|
+
promptCacheKey: zod.z.string().min(1).optional().meta({
|
|
110
|
+
title: "Prompt Cache Key",
|
|
111
|
+
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
112
|
+
}),
|
|
113
|
+
tools: XaiToolsSchema.optional().meta({
|
|
114
|
+
title: "Live Search Tools",
|
|
115
|
+
description: "xAI server-side web_search / x_search tools."
|
|
116
|
+
}),
|
|
117
|
+
parallelToolCalls: zod.z.boolean().optional().meta({
|
|
118
|
+
title: "Parallel Tool Calls",
|
|
119
|
+
description: "xAI Responses parallel_tool_calls. Not a generic contract field."
|
|
120
|
+
}),
|
|
121
|
+
toolChoice: zod.z.enum(["auto", "required", "none"]).optional().meta({
|
|
122
|
+
title: "Server Tool Choice",
|
|
123
|
+
description: "xAI Responses `tool_choice` for the server-side search tools; `required` forces at least one search, `none` disables them. Requires `tools`. Cannot be combined with function tools, file attachments or the request-level toolChoice."
|
|
124
|
+
}),
|
|
125
|
+
maxTurns: zod.z.number().int().min(1).optional().meta({
|
|
126
|
+
title: "Max Turns",
|
|
127
|
+
description: "xAI Responses `max_turns`: cap on agentic tool-calling turns for the server-side search tools. Requires `tools`. A turn can run several searches. Not enforced by xAI as of 2026-10-02."
|
|
128
|
+
})
|
|
129
|
+
}).meta({
|
|
130
|
+
title: "xAI Provider Options",
|
|
131
|
+
description: "Allowlisted xAI provider options."
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// src/model-config/grok-4-5.ts
|
|
135
|
+
var Grok45ConfigSchema = zod.z.strictObject({
|
|
136
|
+
temperature: zod.z.number().optional().meta({
|
|
137
|
+
title: "Temperature",
|
|
138
|
+
description: "Sampling temperature forwarded verbatim to grok-4.5."
|
|
139
|
+
}),
|
|
140
|
+
topP: zod.z.number().optional().meta({
|
|
141
|
+
title: "Top P",
|
|
142
|
+
description: "Nucleus sampling parameter forwarded verbatim to grok-4.5."
|
|
143
|
+
}),
|
|
144
|
+
maxOutputTokens: zod.z.number().int().positive().optional().meta({
|
|
145
|
+
title: "Max Output Tokens",
|
|
146
|
+
description: "Maximum output token cap for grok-4.5. No artificial ceiling \u2014 xAI accepts arbitrarily large values (live-verified); truncation surfaces as finishReason:'length', not an error."
|
|
147
|
+
}),
|
|
148
|
+
reasoning: zod.z.strictObject({
|
|
149
|
+
effort: zod.z.enum(["low", "medium", "high"]).meta({
|
|
150
|
+
title: "Reasoning Effort",
|
|
151
|
+
description: 'Reasoning effort for grok-4.5. "low", "medium", and "high" are admitted (live-verified 2026-08-24). "xhigh" is rejected here even though /v1/language-models lists it: the reasoning guide says it is treated as high, and an echo does not prove a distinct level. "none" is rejected. Vendor default when omitted is "high".'
|
|
152
|
+
})
|
|
153
|
+
}).optional().meta({
|
|
154
|
+
title: "Reasoning",
|
|
155
|
+
description: "grok-4.5 effort-level reasoning configuration. No budgetTokens field \u2014 xAI uses level-style reasoning, not token budgets."
|
|
156
|
+
}),
|
|
157
|
+
serviceTier: zod.z.literal("priority").optional().meta({
|
|
158
|
+
title: "Service Tier",
|
|
159
|
+
description: "xAI priority processing for grok-4.5, billed at 2\xD7 on input, cached input, and output tokens. Live-verified 2026-09-25."
|
|
160
|
+
}),
|
|
161
|
+
timeoutMs: zod.z.number().int().positive().optional().meta({
|
|
162
|
+
title: "Timeout",
|
|
163
|
+
description: "Logical request timeout in milliseconds."
|
|
164
|
+
}),
|
|
165
|
+
providerOptions: zod.z.strictObject({
|
|
166
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
167
|
+
}).optional().meta({
|
|
168
|
+
title: "Provider Options",
|
|
169
|
+
description: "Provider-specific options accepted for grok-4.5."
|
|
170
|
+
})
|
|
171
|
+
}).meta({
|
|
172
|
+
title: "Grok45Config",
|
|
173
|
+
description: "Strict Responses API config for model grok-4.5. Level reasoning (low/medium/high), optional priority service tier, tunable sampling, structured output, vision, Live Search tools, priced.",
|
|
174
|
+
examples: [{ reasoning: { effort: "high" } }]
|
|
175
|
+
});
|
|
176
|
+
var Grok46ConfigSchema = zod.z.strictObject({
|
|
177
|
+
temperature: zod.z.number().optional().meta({
|
|
178
|
+
title: "Temperature",
|
|
179
|
+
description: "Sampling temperature forwarded verbatim to grok-4.6."
|
|
180
|
+
}),
|
|
181
|
+
topP: zod.z.number().optional().meta({
|
|
182
|
+
title: "Top P",
|
|
183
|
+
description: "Nucleus sampling parameter forwarded verbatim to grok-4.6."
|
|
184
|
+
}),
|
|
185
|
+
maxOutputTokens: zod.z.number().int().positive().optional().meta({
|
|
186
|
+
title: "Max Output Tokens",
|
|
187
|
+
description: "Maximum output token cap for grok-4.6. No artificial ceiling \u2014 xAI accepts arbitrarily large values; truncation surfaces as finishReason:'length', not an error."
|
|
188
|
+
}),
|
|
189
|
+
reasoning: zod.z.strictObject({
|
|
190
|
+
effort: zod.z.enum(["low", "medium", "high", "xhigh"]).meta({
|
|
191
|
+
title: "Reasoning Effort",
|
|
192
|
+
description: 'Reasoning effort for grok-4.6. Live-verified 2026-08-12: "low", "medium", "high", and "xhigh" are accepted; "none" is rejected. Vendor default when omitted is "high".'
|
|
193
|
+
})
|
|
194
|
+
}).optional().meta({
|
|
195
|
+
title: "Reasoning",
|
|
196
|
+
description: "grok-4.6 effort-level reasoning configuration. No budgetTokens field \u2014 xAI uses level-style reasoning, not token budgets."
|
|
197
|
+
}),
|
|
198
|
+
serviceTier: zod.z.literal("priority").optional().meta({
|
|
199
|
+
title: "Service Tier",
|
|
200
|
+
description: 'xAI priority processing for grok-4.6 (Responses `service_tier: "priority"`). Echo live-verified 2026-08-12. Bills at 2\xD7 after the cache discount (uncached standard-list 2\xD7 confirmed by live ticks; cached/long-context legs follow the official 2\xD7 rule). Omitted requests stay on xAI default. "flex"/"standard"/"batch" are rejected.'
|
|
201
|
+
}),
|
|
202
|
+
timeoutMs: zod.z.number().int().positive().optional().meta({
|
|
203
|
+
title: "Timeout",
|
|
204
|
+
description: "Logical request timeout in milliseconds."
|
|
205
|
+
}),
|
|
206
|
+
providerOptions: zod.z.strictObject({
|
|
207
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
208
|
+
}).optional().meta({
|
|
209
|
+
title: "Provider Options",
|
|
210
|
+
description: "Provider-specific options accepted for grok-4.6."
|
|
211
|
+
})
|
|
212
|
+
}).meta({
|
|
213
|
+
title: "Grok46Config",
|
|
214
|
+
description: "Strict Responses API config for model grok-4.6. Level reasoning (low/medium/high/xhigh), optional priority service tier, tunable sampling, structured output, vision, priced.",
|
|
215
|
+
examples: [{ reasoning: { effort: "high" } }]
|
|
216
|
+
});
|
|
217
|
+
var Grok47ConfigSchema = zod.z.strictObject({
|
|
218
|
+
temperature: zod.z.number().optional().meta({
|
|
219
|
+
title: "Temperature",
|
|
220
|
+
description: "Sampling temperature forwarded verbatim to grok-4.7."
|
|
221
|
+
}),
|
|
222
|
+
topP: zod.z.number().optional().meta({
|
|
223
|
+
title: "Top P",
|
|
224
|
+
description: "Nucleus sampling parameter forwarded verbatim to grok-4.7."
|
|
225
|
+
}),
|
|
226
|
+
maxOutputTokens: zod.z.number().int().positive().optional().meta({
|
|
227
|
+
title: "Max Output Tokens",
|
|
228
|
+
description: "Maximum output token cap for grok-4.7. No artificial ceiling \u2014 xAI accepts arbitrarily large values; truncation surfaces as finishReason:'length', not an error."
|
|
229
|
+
}),
|
|
230
|
+
reasoning: zod.z.strictObject({
|
|
231
|
+
effort: zod.z.enum(["low", "medium", "high", "xhigh"]).meta({
|
|
232
|
+
title: "Reasoning Effort",
|
|
233
|
+
description: 'Reasoning effort for grok-4.7. "low", "medium", "high", and "xhigh" are accepted; "none" is rejected. Vendor default when omitted is "high".'
|
|
234
|
+
})
|
|
235
|
+
}).optional().meta({
|
|
236
|
+
title: "Reasoning",
|
|
237
|
+
description: "grok-4.7 effort-level reasoning configuration. No budgetTokens field \u2014 xAI uses level-style reasoning, not token budgets."
|
|
238
|
+
}),
|
|
239
|
+
serviceTier: zod.z.literal("priority").optional().meta({
|
|
240
|
+
title: "Service Tier",
|
|
241
|
+
description: 'xAI priority processing for grok-4.7 (Responses `service_tier: "priority"`). Bills at 2\xD7 after the cache discount (uncached standard-list 2\xD7 cached/long-context legs follow the official 2\xD7 rule). Omitted requests stay on xAI default. "flex"/"standard"/"batch" are rejected.'
|
|
242
|
+
}),
|
|
243
|
+
timeoutMs: zod.z.number().int().positive().optional().meta({
|
|
244
|
+
title: "Timeout",
|
|
245
|
+
description: "Logical request timeout in milliseconds."
|
|
246
|
+
}),
|
|
247
|
+
providerOptions: zod.z.strictObject({
|
|
248
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
249
|
+
}).optional().meta({
|
|
250
|
+
title: "Provider Options",
|
|
251
|
+
description: "Provider-specific options accepted for grok-4.7."
|
|
252
|
+
})
|
|
253
|
+
}).meta({
|
|
254
|
+
title: "Grok47Config",
|
|
255
|
+
description: "Strict Responses API config for model grok-4.7. Level reasoning (low/medium/high/xhigh), optional priority service tier, tunable sampling, structured output, vision, priced.",
|
|
256
|
+
examples: [{ reasoning: { effort: "high" } }]
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// src/models.ts
|
|
260
|
+
var grok45ModelDescriptor = {
|
|
261
|
+
model: "grok-4.5",
|
|
262
|
+
provider: "xai",
|
|
263
|
+
pricingFamily: "grok-4.5",
|
|
264
|
+
capabilities: {
|
|
265
|
+
reasoning: true,
|
|
266
|
+
reasoningApi: "level",
|
|
267
|
+
admittedReasoningEfforts: ["low", "medium", "high"],
|
|
268
|
+
structuredOutput: true,
|
|
269
|
+
nativeStructuredOutput: true,
|
|
270
|
+
vision: true,
|
|
271
|
+
audioInput: false,
|
|
272
|
+
sampling: "tunable",
|
|
273
|
+
caching: { explicit: false, minTokens: 0 },
|
|
274
|
+
grounding: true,
|
|
275
|
+
structuredOutputWithTools: true,
|
|
276
|
+
functionCalling: true,
|
|
277
|
+
serviceTiers: ["priority"]
|
|
278
|
+
},
|
|
279
|
+
configSchema: Grok45ConfigSchema,
|
|
280
|
+
configJsonSchema: core.toConfigJsonSchema(Grok45ConfigSchema),
|
|
281
|
+
validateConfig: core.zodToStandardSchema(Grok45ConfigSchema)
|
|
282
|
+
};
|
|
283
|
+
var grok46ModelDescriptor = {
|
|
284
|
+
model: "grok-4.6",
|
|
285
|
+
provider: "xai",
|
|
286
|
+
pricingFamily: "grok-4.6",
|
|
287
|
+
capabilities: {
|
|
288
|
+
reasoning: true,
|
|
289
|
+
reasoningApi: "level",
|
|
290
|
+
admittedReasoningEfforts: ["low", "medium", "high", "xhigh"],
|
|
291
|
+
structuredOutput: true,
|
|
292
|
+
nativeStructuredOutput: true,
|
|
293
|
+
vision: true,
|
|
294
|
+
audioInput: false,
|
|
295
|
+
sampling: "tunable",
|
|
296
|
+
caching: { explicit: false, minTokens: 0 },
|
|
297
|
+
grounding: true,
|
|
298
|
+
structuredOutputWithTools: true,
|
|
299
|
+
functionCalling: true,
|
|
300
|
+
serviceTiers: ["priority"]
|
|
301
|
+
},
|
|
302
|
+
configSchema: Grok46ConfigSchema,
|
|
303
|
+
configJsonSchema: core.toConfigJsonSchema(Grok46ConfigSchema),
|
|
304
|
+
validateConfig: core.zodToStandardSchema(Grok46ConfigSchema)
|
|
305
|
+
};
|
|
306
|
+
var grok47ModelDescriptor = {
|
|
307
|
+
model: "grok-4.7",
|
|
308
|
+
provider: "xai",
|
|
309
|
+
pricingFamily: "grok-4.7",
|
|
310
|
+
capabilities: {
|
|
311
|
+
reasoning: true,
|
|
312
|
+
reasoningApi: "level",
|
|
313
|
+
admittedReasoningEfforts: ["low", "medium", "high", "xhigh"],
|
|
314
|
+
structuredOutput: true,
|
|
315
|
+
nativeStructuredOutput: true,
|
|
316
|
+
vision: true,
|
|
317
|
+
audioInput: false,
|
|
318
|
+
sampling: "tunable",
|
|
319
|
+
caching: { explicit: false, minTokens: 0 },
|
|
320
|
+
grounding: true,
|
|
321
|
+
structuredOutputWithTools: true,
|
|
322
|
+
functionCalling: true,
|
|
323
|
+
statelessReasoningReplay: true,
|
|
324
|
+
serviceTiers: ["priority"]
|
|
325
|
+
},
|
|
326
|
+
configSchema: Grok47ConfigSchema,
|
|
327
|
+
configJsonSchema: core.toConfigJsonSchema(Grok47ConfigSchema),
|
|
328
|
+
validateConfig: core.zodToStandardSchema(Grok47ConfigSchema)
|
|
329
|
+
};
|
|
330
|
+
var xaiModelDescriptors = [
|
|
331
|
+
grok45ModelDescriptor,
|
|
332
|
+
grok46ModelDescriptor,
|
|
333
|
+
grok47ModelDescriptor
|
|
334
|
+
];
|
|
335
|
+
var xaiRegistry = core.createModelRegistry(xaiModelDescriptors);
|
|
336
|
+
var JSON_SCHEMA_TYPES = /* @__PURE__ */ new Set([
|
|
337
|
+
"string",
|
|
338
|
+
"number",
|
|
339
|
+
"integer",
|
|
340
|
+
"boolean",
|
|
341
|
+
"object",
|
|
342
|
+
"array",
|
|
343
|
+
"null"
|
|
344
|
+
]);
|
|
345
|
+
var SINGLE_SCHEMA_KEYWORDS = [
|
|
346
|
+
"additionalProperties",
|
|
347
|
+
"additionalItems",
|
|
348
|
+
"items",
|
|
349
|
+
"contains",
|
|
350
|
+
"not",
|
|
351
|
+
"if",
|
|
352
|
+
"then",
|
|
353
|
+
"else",
|
|
354
|
+
"propertyNames",
|
|
355
|
+
"unevaluatedProperties",
|
|
356
|
+
"unevaluatedItems",
|
|
357
|
+
"contentSchema"
|
|
358
|
+
];
|
|
359
|
+
var SCHEMA_ARRAY_KEYWORDS = ["items", "prefixItems", "anyOf", "oneOf", "allOf"];
|
|
360
|
+
var SCHEMA_MAP_KEYWORDS = [
|
|
361
|
+
"properties",
|
|
362
|
+
"patternProperties",
|
|
363
|
+
"dependentSchemas",
|
|
364
|
+
// Draft-07 `dependencies`: schema-valued entries are walked; array-valued
|
|
365
|
+
// entries are property-name lists and are skipped by the object check.
|
|
366
|
+
"dependencies",
|
|
367
|
+
"$defs",
|
|
368
|
+
"definitions"
|
|
369
|
+
];
|
|
370
|
+
function isPlainObject(value) {
|
|
371
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
372
|
+
}
|
|
373
|
+
function badSchema(message) {
|
|
374
|
+
return new core.LlmError(message, { kind: "bad_request", retryable: false, provider: "xai" });
|
|
375
|
+
}
|
|
376
|
+
function assertNode(node, path) {
|
|
377
|
+
const at = path.length > 0 ? path : "<root>";
|
|
378
|
+
if ("nullable" in node) {
|
|
379
|
+
throw badSchema(
|
|
380
|
+
`xAI structured output takes standard JSON Schema, which has no \`nullable\` keyword (found at \`${at}\`). xAI ignores it, so the field would be required and non-null. List 'null' in \`type\` instead, e.g. type: ['string', 'null'].`
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
const type = node["type"];
|
|
384
|
+
if (type !== void 0) {
|
|
385
|
+
const names = Array.isArray(type) ? type : [type];
|
|
386
|
+
for (const name of names) {
|
|
387
|
+
if (typeof name !== "string" || !JSON_SCHEMA_TYPES.has(name)) {
|
|
388
|
+
throw badSchema(
|
|
389
|
+
`xAI structured output takes standard JSON Schema; \`type\` ${JSON.stringify(
|
|
390
|
+
name
|
|
391
|
+
)} at \`${at}\` is not a JSON Schema type. Use lowercase string, number, integer, boolean, object, array or null.`
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const child = (segment) => path.length > 0 ? `${path}.${segment}` : segment;
|
|
397
|
+
for (const keyword of SCHEMA_MAP_KEYWORDS) {
|
|
398
|
+
const map = node[keyword];
|
|
399
|
+
if (!isPlainObject(map)) continue;
|
|
400
|
+
for (const [key, member] of Object.entries(map)) {
|
|
401
|
+
if (isPlainObject(member)) assertNode(member, child(`${keyword}.${key}`));
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
for (const keyword of SCHEMA_ARRAY_KEYWORDS) {
|
|
405
|
+
const members = node[keyword];
|
|
406
|
+
if (!Array.isArray(members)) continue;
|
|
407
|
+
members.forEach((member, index) => {
|
|
408
|
+
if (isPlainObject(member)) assertNode(member, child(`${keyword}[${index}]`));
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
for (const keyword of SINGLE_SCHEMA_KEYWORDS) {
|
|
412
|
+
const member = node[keyword];
|
|
413
|
+
if (isPlainObject(member)) assertNode(member, child(keyword));
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
function assertXaiOutputJsonSchema(schema) {
|
|
417
|
+
if (isPlainObject(schema)) assertNode(schema, "");
|
|
418
|
+
}
|
|
419
|
+
var xaiPricingVersion = "xai-2026-09-25";
|
|
420
|
+
var XAI_TOOL_RATE_MICRO_USD = {
|
|
421
|
+
web_search_calls: 5e3,
|
|
422
|
+
x_posts_fetched: 5e3,
|
|
423
|
+
x_users_fetched: 1e4
|
|
424
|
+
};
|
|
425
|
+
var XAI_TOOL_COUNTER_KEYS = [
|
|
426
|
+
"web_search_calls",
|
|
427
|
+
"x_posts_fetched",
|
|
428
|
+
"x_users_fetched"
|
|
429
|
+
];
|
|
430
|
+
var X_SEARCH_ITEM_COUNTERS = ["x_posts_fetched", "x_users_fetched"];
|
|
431
|
+
var LONG_CONTEXT_THRESHOLD = 2e5;
|
|
432
|
+
var XAI_PRICING = Object.freeze({
|
|
433
|
+
// ── grok-4.5 ── $2.00/$6.00 (<200k), $4.00/$12.00 (>=200k); cached $0.30/$0.60
|
|
434
|
+
"grok-4.5": {
|
|
435
|
+
inputPerM: 2e6,
|
|
436
|
+
cachedPerM: 3e5,
|
|
437
|
+
outputPerM: 6e6,
|
|
438
|
+
gt200k: {
|
|
439
|
+
inputPerM: 4e6,
|
|
440
|
+
cachedPerM: 6e5,
|
|
441
|
+
outputPerM: 12e6
|
|
442
|
+
},
|
|
443
|
+
priorityFactor: 2
|
|
444
|
+
},
|
|
445
|
+
// ── grok-4.6 ── $2.00/$6.00 (<200k), $4.00/$12.00 (>=200k); cached $0.50/$1.00
|
|
446
|
+
"grok-4.6": {
|
|
447
|
+
inputPerM: 2e6,
|
|
448
|
+
cachedPerM: 5e5,
|
|
449
|
+
outputPerM: 6e6,
|
|
450
|
+
gt200k: {
|
|
451
|
+
inputPerM: 4e6,
|
|
452
|
+
cachedPerM: 1e6,
|
|
453
|
+
outputPerM: 12e6
|
|
454
|
+
},
|
|
455
|
+
// Confirmed 2026-08-12 by fixture 12 cost_in_usd_ticks (2× list).
|
|
456
|
+
priorityFactor: 2
|
|
457
|
+
},
|
|
458
|
+
// ── grok-4.7 ── $2.00/$0.50/$6.00 (<200k), $4.00/$1.00/$12.00 (≥200k); priority 2×
|
|
459
|
+
"grok-4.7": {
|
|
460
|
+
inputPerM: 2e6,
|
|
461
|
+
cachedPerM: 5e5,
|
|
462
|
+
outputPerM: 6e6,
|
|
463
|
+
gt200k: {
|
|
464
|
+
inputPerM: 4e6,
|
|
465
|
+
cachedPerM: 1e6,
|
|
466
|
+
outputPerM: 12e6
|
|
467
|
+
},
|
|
468
|
+
priorityFactor: 2
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
function lookupRates(model) {
|
|
472
|
+
return Object.hasOwn(XAI_PRICING, model) ? XAI_PRICING[model] : void 0;
|
|
473
|
+
}
|
|
474
|
+
var lookupConcreteRates = (model, tier) => {
|
|
475
|
+
const rates = lookupRates(model);
|
|
476
|
+
if (rates === void 0) return void 0;
|
|
477
|
+
if (tier === void 0 || tier === "default") return rates;
|
|
478
|
+
if (tier === "priority" && rates.priorityFactor !== void 0) {
|
|
479
|
+
return scaleRates(rates, rates.priorityFactor);
|
|
480
|
+
}
|
|
481
|
+
return void 0;
|
|
482
|
+
};
|
|
483
|
+
function selectXaiRates(rates, grossInputTokens) {
|
|
484
|
+
if (rates.gt200k !== void 0 && grossInputTokens >= LONG_CONTEXT_THRESHOLD) {
|
|
485
|
+
return rates.gt200k;
|
|
486
|
+
}
|
|
487
|
+
return {
|
|
488
|
+
inputPerM: rates.inputPerM,
|
|
489
|
+
cachedPerM: rates.cachedPerM,
|
|
490
|
+
outputPerM: rates.outputPerM
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
function scaleRates(rates, factor) {
|
|
494
|
+
const scaled = {
|
|
495
|
+
inputPerM: rates.inputPerM * factor,
|
|
496
|
+
cachedPerM: rates.cachedPerM * factor,
|
|
497
|
+
outputPerM: rates.outputPerM * factor
|
|
498
|
+
};
|
|
499
|
+
if (rates.gt200k !== void 0) {
|
|
500
|
+
scaled.gt200k = {
|
|
501
|
+
inputPerM: rates.gt200k.inputPerM * factor,
|
|
502
|
+
cachedPerM: rates.gt200k.cachedPerM * factor,
|
|
503
|
+
outputPerM: rates.gt200k.outputPerM * factor
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
return scaled;
|
|
507
|
+
}
|
|
508
|
+
function computeXaiCost(model, usage, tier) {
|
|
509
|
+
const listed = lookupConcreteRates(model, tier);
|
|
510
|
+
if (listed === void 0) {
|
|
511
|
+
return core.computeCost(model, usage, tier, lookupConcreteRates, xaiPricingVersion);
|
|
512
|
+
}
|
|
513
|
+
const band = selectXaiRates(listed, usage.inputTokens);
|
|
514
|
+
const bandLookup = () => ({
|
|
515
|
+
inputPerM: band.inputPerM,
|
|
516
|
+
cachedPerM: band.cachedPerM,
|
|
517
|
+
outputPerM: band.outputPerM
|
|
518
|
+
});
|
|
519
|
+
const tokenCost = core.computeCost(model, usage, void 0, bandLookup, xaiPricingVersion);
|
|
520
|
+
const inputCost = tokenCost.details.input;
|
|
521
|
+
const cachedCost = tokenCost.details.cached;
|
|
522
|
+
const outputCost = tokenCost.details.output;
|
|
523
|
+
const serverToolsRequested = usage.details["server_tools_requested"] === 1;
|
|
524
|
+
const xSearchRequested = usage.details["x_search_requested"] === 1;
|
|
525
|
+
const missingXSearchCounter = xSearchRequested && X_SEARCH_ITEM_COUNTERS.some((key) => typeof usage.details[key] !== "number");
|
|
526
|
+
if (missingXSearchCounter || usage.details["server_tools_missing"] === 1) {
|
|
527
|
+
return {
|
|
528
|
+
microUsd: null,
|
|
529
|
+
usd: null,
|
|
530
|
+
pricingVersion: xaiPricingVersion,
|
|
531
|
+
confidence: "estimated",
|
|
532
|
+
details: { input: 0, cached: 0, output: 0, tools: 0 },
|
|
533
|
+
unpricedReason: missingXSearchCounter ? "x_search usage is missing x_posts_fetched or x_users_fetched; refusing to bill a per-call estimate." : "Server tool usage is missing a required counter; refusing to guess a tool cost."
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
const attachmentUnpinned = usage.details["attachment_search_unpinned"] === 1;
|
|
537
|
+
const missingWebCounter = serverToolsRequested && !xSearchRequested && !attachmentUnpinned && !XAI_TOOL_COUNTER_KEYS.some((key) => key in usage.details);
|
|
538
|
+
const toolsCost = missingWebCounter ? 0 : XAI_TOOL_COUNTER_KEYS.reduce((sum, key) => {
|
|
539
|
+
const count = usage.details[key];
|
|
540
|
+
if (typeof count !== "number" || count <= 0) return sum;
|
|
541
|
+
return sum + Math.round(count * XAI_TOOL_RATE_MICRO_USD[key]);
|
|
542
|
+
}, 0);
|
|
543
|
+
const microUsd = inputCost + cachedCost + outputCost + toolsCost;
|
|
544
|
+
return {
|
|
545
|
+
microUsd,
|
|
546
|
+
usd: microUsd / 1e6,
|
|
547
|
+
pricingVersion: xaiPricingVersion,
|
|
548
|
+
confidence: missingWebCounter || attachmentUnpinned ? "estimated" : "exact",
|
|
549
|
+
details: {
|
|
550
|
+
input: inputCost,
|
|
551
|
+
cached: cachedCost,
|
|
552
|
+
output: outputCost,
|
|
553
|
+
tools: toolsCost
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
function xaiPricingSource() {
|
|
558
|
+
return {
|
|
559
|
+
version: xaiPricingVersion,
|
|
560
|
+
price(model, usage, tier) {
|
|
561
|
+
return computeXaiCost(model, usage, tier);
|
|
562
|
+
},
|
|
563
|
+
hasModel(model) {
|
|
564
|
+
return lookupRates(model) !== void 0;
|
|
565
|
+
},
|
|
566
|
+
listModels() {
|
|
567
|
+
return Object.keys(XAI_PRICING);
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// src/adapter.ts
|
|
33
573
|
function isPlainRecord(value) {
|
|
34
574
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
35
575
|
}
|
|
@@ -92,7 +632,14 @@ function mapPart(p) {
|
|
|
92
632
|
return core.assertNever(p);
|
|
93
633
|
}
|
|
94
634
|
}
|
|
95
|
-
var XAI_PROVIDER_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
635
|
+
var XAI_PROVIDER_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
636
|
+
"promptCacheKey",
|
|
637
|
+
"tools",
|
|
638
|
+
"parallelToolCalls",
|
|
639
|
+
"toolChoice",
|
|
640
|
+
"maxTurns"
|
|
641
|
+
]);
|
|
642
|
+
var XAI_SERVER_TOOL_CHOICES = /* @__PURE__ */ new Set(["auto", "required", "none"]);
|
|
96
643
|
function mapXaiProviderOptions(xaiOpts, model) {
|
|
97
644
|
if (xaiOpts === void 0) {
|
|
98
645
|
return {};
|
|
@@ -107,7 +654,7 @@ function mapXaiProviderOptions(xaiOpts, model) {
|
|
|
107
654
|
throw badXaiRequest(
|
|
108
655
|
`providerOptions.xai contains unsupported keys [${unknownKeys.join(
|
|
109
656
|
", "
|
|
110
|
-
)}] for model "${model}". Allowed keys: promptCacheKey, tools, parallelToolCalls.`
|
|
657
|
+
)}] for model "${model}". Allowed keys: promptCacheKey, tools, parallelToolCalls, toolChoice, maxTurns.`
|
|
111
658
|
);
|
|
112
659
|
}
|
|
113
660
|
const mapped = {};
|
|
@@ -130,8 +677,47 @@ function mapXaiProviderOptions(xaiOpts, model) {
|
|
|
130
677
|
}
|
|
131
678
|
mapped.parallelToolCalls = xaiOpts["parallelToolCalls"];
|
|
132
679
|
}
|
|
680
|
+
const toolChoice = xaiOpts["toolChoice"];
|
|
681
|
+
if (toolChoice !== void 0) {
|
|
682
|
+
if (typeof toolChoice !== "string" || !XAI_SERVER_TOOL_CHOICES.has(toolChoice)) {
|
|
683
|
+
throw badXaiRequest(
|
|
684
|
+
`providerOptions.xai.toolChoice must be "auto", "required" or "none" for model "${model}".`
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
if (mapped.tools === void 0 || mapped.tools.length === 0) {
|
|
688
|
+
throw badXaiRequest(
|
|
689
|
+
`providerOptions.xai.toolChoice requires a non-empty providerOptions.xai.tools for model "${model}".`
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
mapped.toolChoice = toolChoice;
|
|
693
|
+
}
|
|
694
|
+
const maxTurns = xaiOpts["maxTurns"];
|
|
695
|
+
if (maxTurns !== void 0) {
|
|
696
|
+
if (typeof maxTurns !== "number" || !Number.isInteger(maxTurns) || maxTurns < 1) {
|
|
697
|
+
throw badXaiRequest(
|
|
698
|
+
`providerOptions.xai.maxTurns must be an integer >= 1 for model "${model}".`
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
if (mapped.tools === void 0 || mapped.tools.length === 0) {
|
|
702
|
+
throw badXaiRequest(
|
|
703
|
+
`providerOptions.xai.maxTurns requires a non-empty providerOptions.xai.tools for model "${model}".`
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
mapped.maxTurns = maxTurns;
|
|
707
|
+
}
|
|
133
708
|
return mapped;
|
|
134
709
|
}
|
|
710
|
+
function parseXaiReplayState(value, model) {
|
|
711
|
+
if (value === void 0) return void 0;
|
|
712
|
+
if (!isPlainRecord(value) || value["model"] !== model || !Array.isArray(value["input"]) || value["input"].length === 0 || value["input"].some(
|
|
713
|
+
(item) => !isPlainRecord(item) || typeof item["type"] !== "string" && typeof item["role"] !== "string"
|
|
714
|
+
)) {
|
|
715
|
+
throw badXaiRequest(
|
|
716
|
+
`transientProviderState must contain the full xAI wire input for model "${model}".`
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
return value;
|
|
720
|
+
}
|
|
135
721
|
function mapXaiSearchTools(tools, model) {
|
|
136
722
|
if (!Array.isArray(tools)) {
|
|
137
723
|
throw badXaiRequest(
|
|
@@ -338,9 +924,53 @@ function xaiAdapter(opts) {
|
|
|
338
924
|
}
|
|
339
925
|
const warnings = [];
|
|
340
926
|
const model = req.model;
|
|
927
|
+
if (req.modelDescriptor !== void 0 && (req.modelDescriptor.model !== model || req.modelDescriptor.provider !== "xai")) {
|
|
928
|
+
throw badXaiRequest(`Mismatched xAI model descriptor for "${model}".`);
|
|
929
|
+
}
|
|
930
|
+
if (xaiRegistry.resolve("xai", model)?.capabilities?.statelessReasoningReplay === true && req.modelDescriptor?.capabilities?.statelessReasoningReplay !== true) {
|
|
931
|
+
throw badXaiRequest(
|
|
932
|
+
`A matching xAI model descriptor with statelessReasoningReplay is required for "${model}".`
|
|
933
|
+
);
|
|
934
|
+
}
|
|
341
935
|
const genConfig = req.config;
|
|
342
|
-
const
|
|
936
|
+
const xaiProviderConfig = mapXaiProviderOptions(
|
|
937
|
+
genConfig.providerOptions?.["xai"],
|
|
938
|
+
model
|
|
939
|
+
);
|
|
940
|
+
const replayRequired = req.modelDescriptor?.capabilities?.statelessReasoningReplay === true;
|
|
941
|
+
const replayState = parseXaiReplayState(req.transientProviderState, model);
|
|
942
|
+
if (replayState !== void 0 && !replayRequired) {
|
|
943
|
+
throw badXaiRequest(
|
|
944
|
+
`transientProviderState requires a statelessReasoningReplay model descriptor for "${model}".`
|
|
945
|
+
);
|
|
946
|
+
}
|
|
947
|
+
if (replayState !== void 0 && req.messages.length === 0) {
|
|
948
|
+
throw badXaiRequest(
|
|
949
|
+
`Stateless conversation replay for model "${model}" requires new messages to append.`
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
const input = [...replayState?.input ?? []];
|
|
953
|
+
const replayCallIds = new Set(
|
|
954
|
+
replayState?.input.filter((item) => isPlainRecord(item) && item["type"] === "function_call").map((item) => isPlainRecord(item) ? item["call_id"] : void 0).filter((id) => typeof id === "string") ?? []
|
|
955
|
+
);
|
|
956
|
+
const replayedResultIds = new Set(
|
|
957
|
+
replayState?.input.filter(
|
|
958
|
+
(item) => isPlainRecord(item) && item["type"] === "function_call_output"
|
|
959
|
+
).map((item) => isPlainRecord(item) ? item["call_id"] : void 0).filter((id) => typeof id === "string") ?? []
|
|
960
|
+
);
|
|
343
961
|
for (const msg of req.messages) {
|
|
962
|
+
if (replayState !== void 0 && msg.role === "assistant") {
|
|
963
|
+
throw badXaiRequest(
|
|
964
|
+
`New messages for model "${model}" cannot contain assistant history when transientProviderState is supplied.`
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
if (replayRequired && replayState === void 0 && msg.parts.some(
|
|
968
|
+
(part) => part.kind === "tool-call" || part.kind === "tool-result"
|
|
969
|
+
)) {
|
|
970
|
+
throw badXaiRequest(
|
|
971
|
+
`Function-call history for model "${model}" requires transientProviderState from the prior result.`
|
|
972
|
+
);
|
|
973
|
+
}
|
|
344
974
|
const contentParts = [];
|
|
345
975
|
for (const part of msg.parts) {
|
|
346
976
|
if (part.kind === "tool-call") {
|
|
@@ -359,6 +989,12 @@ function xaiAdapter(opts) {
|
|
|
359
989
|
continue;
|
|
360
990
|
}
|
|
361
991
|
if (part.kind === "tool-result") {
|
|
992
|
+
if (replayState !== void 0 && (!replayCallIds.has(part.toolCallId) || replayedResultIds.has(part.toolCallId))) {
|
|
993
|
+
throw badXaiRequest(
|
|
994
|
+
`Tool result "${part.toolCallId}" must match an unanswered function call in transientProviderState.`
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
replayedResultIds.add(part.toolCallId);
|
|
362
998
|
if (contentParts.length > 0) {
|
|
363
999
|
input.push({
|
|
364
1000
|
role: msg.role === "assistant" ? "assistant" : "user",
|
|
@@ -421,9 +1057,9 @@ function xaiAdapter(opts) {
|
|
|
421
1057
|
}
|
|
422
1058
|
if (reasoning.effort !== void 0) {
|
|
423
1059
|
const effort = reasoning.effort;
|
|
424
|
-
if (effort === "none") {
|
|
1060
|
+
if (effort === "none" || effort === "max") {
|
|
425
1061
|
throw badXaiRequest(
|
|
426
|
-
`reasoning.effort "
|
|
1062
|
+
`reasoning.effort "${effort}" is not supported for xai model "${model}".`
|
|
427
1063
|
);
|
|
428
1064
|
}
|
|
429
1065
|
const admitted = req.modelDescriptor?.capabilities?.admittedReasoningEfforts;
|
|
@@ -438,20 +1074,19 @@ function xaiAdapter(opts) {
|
|
|
438
1074
|
const structuredOutputRequested = req.outputJsonSchema !== void 0;
|
|
439
1075
|
if (structuredOutputRequested) {
|
|
440
1076
|
const schema = req.outputJsonSchema;
|
|
1077
|
+
assertXaiOutputJsonSchema(schema);
|
|
441
1078
|
const name = isPlainRecord(schema) && typeof schema["title"] === "string" && schema["title"].length > 0 ? schema["title"] : "structured_output";
|
|
442
1079
|
params.text = {
|
|
443
1080
|
format: { type: "json_schema", name, schema, strict: true }
|
|
444
1081
|
};
|
|
445
1082
|
}
|
|
446
|
-
const xaiProviderConfig = mapXaiProviderOptions(
|
|
447
|
-
genConfig.providerOptions?.["xai"],
|
|
448
|
-
model
|
|
449
|
-
);
|
|
450
1083
|
if (xaiProviderConfig.promptCacheKey !== void 0) {
|
|
451
1084
|
params.prompt_cache_key = xaiProviderConfig.promptCacheKey;
|
|
452
1085
|
}
|
|
453
|
-
const hasFileRef =
|
|
454
|
-
(
|
|
1086
|
+
const hasFileRef = input.some(
|
|
1087
|
+
(item) => isPlainRecord(item) && Array.isArray(item["content"]) && item["content"].some(
|
|
1088
|
+
(part) => isPlainRecord(part) && part["type"] === "input_file"
|
|
1089
|
+
)
|
|
455
1090
|
);
|
|
456
1091
|
const searchTools = xaiProviderConfig.tools;
|
|
457
1092
|
if (searchTools !== void 0) {
|
|
@@ -460,7 +1095,33 @@ function xaiAdapter(opts) {
|
|
|
460
1095
|
`providerOptions.xai.tools requires capabilities.grounding on the model descriptor for "${model}".`
|
|
461
1096
|
);
|
|
462
1097
|
}
|
|
1098
|
+
if (structuredOutputRequested && req.modelDescriptor.capabilities.structuredOutputWithTools !== true) {
|
|
1099
|
+
throw badXaiRequest(
|
|
1100
|
+
`Structured output with providerOptions.xai.tools is not supported for model "${model}".`
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
463
1103
|
params.tools = searchTools;
|
|
1104
|
+
if (xaiProviderConfig.toolChoice !== void 0) {
|
|
1105
|
+
if (req.toolChoice !== void 0) {
|
|
1106
|
+
throw badXaiRequest(
|
|
1107
|
+
`providerOptions.xai.toolChoice and toolChoice cannot both be set for model "${model}"; xAI accepts one tool_choice per request.`
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
if (req.tools !== void 0 && req.tools.length > 0) {
|
|
1111
|
+
throw badXaiRequest(
|
|
1112
|
+
`providerOptions.xai.toolChoice applies to the server-side search tools only and cannot be combined with function tools for model "${model}".`
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
if (hasFileRef) {
|
|
1116
|
+
throw badXaiRequest(
|
|
1117
|
+
`providerOptions.xai.toolChoice cannot be combined with file attachments for model "${model}"; xAI's implicit attachment_search would count as the tool call.`
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
params.tool_choice = xaiProviderConfig.toolChoice;
|
|
1121
|
+
}
|
|
1122
|
+
if (xaiProviderConfig.maxTurns !== void 0) {
|
|
1123
|
+
params.max_turns = xaiProviderConfig.maxTurns;
|
|
1124
|
+
}
|
|
464
1125
|
}
|
|
465
1126
|
if (req.tools !== void 0 && req.tools.length > 0) {
|
|
466
1127
|
if (req.modelDescriptor?.capabilities?.functionCalling !== true) {
|
|
@@ -546,8 +1207,12 @@ function xaiAdapter(opts) {
|
|
|
546
1207
|
const finishReason = mapFinishReason(response);
|
|
547
1208
|
const expectedToolCounters = expectedServerToolCounters(
|
|
548
1209
|
xaiProviderConfig.tools);
|
|
549
|
-
|
|
1210
|
+
const noServerToolRan = response.usage["num_server_side_tools_used"] === 0 && response.usage["server_side_tool_usage_details"] === void 0;
|
|
1211
|
+
if ((expectedToolCounters.length > 0 || hasFileRef) && !noServerToolRan) {
|
|
550
1212
|
usage.details["server_tools_requested"] = 1;
|
|
1213
|
+
if (xaiProviderConfig.tools?.some((tool) => tool["type"] === "x_search") === true) {
|
|
1214
|
+
usage.details["x_search_requested"] = 1;
|
|
1215
|
+
}
|
|
551
1216
|
const missing = expectedToolCounters.filter((key) => !(key in usage.details));
|
|
552
1217
|
if (missing.length > 0) {
|
|
553
1218
|
usage.details["server_tools_missing"] = 1;
|
|
@@ -555,7 +1220,7 @@ function xaiAdapter(opts) {
|
|
|
555
1220
|
type: "other",
|
|
556
1221
|
message: `xai: server tools were requested but usage is missing counters [${missing.join(
|
|
557
1222
|
", "
|
|
558
|
-
)}];
|
|
1223
|
+
)}]; the call is unpriced.`
|
|
559
1224
|
});
|
|
560
1225
|
}
|
|
561
1226
|
if (hasFileRef) {
|
|
@@ -575,6 +1240,14 @@ function xaiAdapter(opts) {
|
|
|
575
1240
|
if (isPlainRecord(response.metadata)) {
|
|
576
1241
|
providerMeta["metadata"] = response.metadata;
|
|
577
1242
|
}
|
|
1243
|
+
let transientProviderState;
|
|
1244
|
+
if (replayRequired) {
|
|
1245
|
+
const state = {
|
|
1246
|
+
model,
|
|
1247
|
+
input: [...params.input, ...response.output]
|
|
1248
|
+
};
|
|
1249
|
+
transientProviderState = state;
|
|
1250
|
+
}
|
|
578
1251
|
const servedServiceTier = typeof response.service_tier === "string" && response.service_tier.length > 0 ? response.service_tier : void 0;
|
|
579
1252
|
const result = {
|
|
580
1253
|
model: response.model,
|
|
@@ -587,6 +1260,7 @@ function xaiAdapter(opts) {
|
|
|
587
1260
|
...rawStructured !== void 0 ? { rawStructured } : {},
|
|
588
1261
|
...servedServiceTier !== void 0 ? { servedServiceTier } : {},
|
|
589
1262
|
...Object.keys(providerMeta).length > 0 ? { providerMetadata: providerMeta } : {},
|
|
1263
|
+
...transientProviderState !== void 0 ? { transientProviderState } : {},
|
|
590
1264
|
...citations.length > 0 ? { citations } : {},
|
|
591
1265
|
...toolCalls.length > 0 ? { toolCalls, finishReason: "tool_calls" } : {}
|
|
592
1266
|
};
|
|
@@ -658,13 +1332,12 @@ function xaiAdapter(opts) {
|
|
|
658
1332
|
};
|
|
659
1333
|
}
|
|
660
1334
|
var WEB_SEARCH_COUNTER = "web_search_calls";
|
|
661
|
-
var X_SEARCH_COUNTER = "x_search_calls";
|
|
662
1335
|
function expectedServerToolCounters(tools, _hasFileRef) {
|
|
663
1336
|
const keys = [];
|
|
664
1337
|
if (tools !== void 0) {
|
|
665
1338
|
for (const tool of tools) {
|
|
666
1339
|
if (tool["type"] === "web_search") keys.push(WEB_SEARCH_COUNTER);
|
|
667
|
-
if (tool["type"] === "x_search") keys.push(
|
|
1340
|
+
if (tool["type"] === "x_search") keys.push(...X_SEARCH_ITEM_COUNTERS);
|
|
668
1341
|
}
|
|
669
1342
|
}
|
|
670
1343
|
return keys;
|
|
@@ -989,553 +1662,211 @@ var XaiFileStore = class {
|
|
|
989
1662
|
this.filesUrl(),
|
|
990
1663
|
this.requestInit("POST", {
|
|
991
1664
|
body: form,
|
|
992
|
-
...signal !== void 0 ? { signal } : {}
|
|
993
|
-
})
|
|
994
|
-
);
|
|
995
|
-
} catch (e) {
|
|
996
|
-
if (signal?.aborted === true) {
|
|
997
|
-
throw new core.LlmError("xAI file upload aborted", {
|
|
998
|
-
kind: "aborted",
|
|
999
|
-
retryable: false,
|
|
1000
|
-
provider: "xai"
|
|
1001
|
-
});
|
|
1002
|
-
}
|
|
1003
|
-
throw classifyStoreError(e);
|
|
1004
|
-
}
|
|
1005
|
-
if (!res.ok) {
|
|
1006
|
-
try {
|
|
1007
|
-
await throwHttpFailure(res);
|
|
1008
|
-
} catch (e) {
|
|
1009
|
-
throw classifyStoreError(e);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
let json;
|
|
1013
|
-
try {
|
|
1014
|
-
json = await res.json();
|
|
1015
|
-
} catch (e) {
|
|
1016
|
-
throw new core.LlmError("xAI file upload returned non-JSON body", {
|
|
1017
|
-
kind: "server",
|
|
1018
|
-
retryable: false,
|
|
1019
|
-
provider: "xai",
|
|
1020
|
-
cause: e
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
return makeHandle(json);
|
|
1024
|
-
}
|
|
1025
|
-
async get(fileId, signal) {
|
|
1026
|
-
if (typeof fileId !== "string" || fileId.trim() === "") {
|
|
1027
|
-
throw badRequest("fileId must be a non-empty string.");
|
|
1028
|
-
}
|
|
1029
|
-
let res;
|
|
1030
|
-
try {
|
|
1031
|
-
res = await this.fetchImpl(
|
|
1032
|
-
this.filesUrl(fileId),
|
|
1033
|
-
this.requestInit("GET", signal !== void 0 ? { signal } : {})
|
|
1034
|
-
);
|
|
1035
|
-
} catch (e) {
|
|
1036
|
-
if (signal?.aborted === true) {
|
|
1037
|
-
throw new core.LlmError("xAI file get aborted", {
|
|
1038
|
-
kind: "aborted",
|
|
1039
|
-
retryable: false,
|
|
1040
|
-
provider: "xai"
|
|
1041
|
-
});
|
|
1042
|
-
}
|
|
1043
|
-
throw classifyStoreError(e);
|
|
1044
|
-
}
|
|
1045
|
-
if (res.status === 404) {
|
|
1046
|
-
throw notFoundError(fileId, "get");
|
|
1047
|
-
}
|
|
1048
|
-
if (!res.ok) {
|
|
1049
|
-
try {
|
|
1050
|
-
await throwHttpFailure(res);
|
|
1051
|
-
} catch (e) {
|
|
1052
|
-
throw classifyStoreError(e);
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
const json = await res.json();
|
|
1056
|
-
return makeHandle(json);
|
|
1057
|
-
}
|
|
1058
|
-
async list(opts = {}, signal) {
|
|
1059
|
-
if (opts.limit !== void 0) {
|
|
1060
|
-
if (typeof opts.limit !== "number" || !Number.isInteger(opts.limit) || opts.limit < 1 || opts.limit > 100) {
|
|
1061
|
-
throw badRequest("list.limit must be an integer in [1, 100].");
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
const params = new URLSearchParams();
|
|
1065
|
-
if (opts.limit !== void 0) params.set("limit", String(opts.limit));
|
|
1066
|
-
if (opts.order !== void 0) params.set("order", opts.order);
|
|
1067
|
-
if (opts.sortBy !== void 0) params.set("sort_by", opts.sortBy);
|
|
1068
|
-
if (opts.paginationToken !== void 0) {
|
|
1069
|
-
params.set("pagination_token", opts.paginationToken);
|
|
1070
|
-
}
|
|
1071
|
-
const qs = params.toString();
|
|
1072
|
-
const url = qs.length > 0 ? `${this.filesUrl()}?${qs}` : this.filesUrl();
|
|
1073
|
-
let res;
|
|
1074
|
-
try {
|
|
1075
|
-
res = await this.fetchImpl(
|
|
1076
|
-
url,
|
|
1077
|
-
this.requestInit("GET", signal !== void 0 ? { signal } : {})
|
|
1078
|
-
);
|
|
1079
|
-
} catch (e) {
|
|
1080
|
-
if (signal?.aborted === true) {
|
|
1081
|
-
throw new core.LlmError("xAI file list aborted", {
|
|
1082
|
-
kind: "aborted",
|
|
1083
|
-
retryable: false,
|
|
1084
|
-
provider: "xai"
|
|
1085
|
-
});
|
|
1086
|
-
}
|
|
1087
|
-
throw classifyStoreError(e);
|
|
1088
|
-
}
|
|
1089
|
-
if (!res.ok) {
|
|
1090
|
-
try {
|
|
1091
|
-
await throwHttpFailure(res);
|
|
1092
|
-
} catch (e) {
|
|
1093
|
-
throw classifyStoreError(e);
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
const json = await res.json();
|
|
1097
|
-
const files = Array.isArray(json.data) ? json.data.map((f) => makeHandle(f)) : [];
|
|
1098
|
-
const result = { files };
|
|
1099
|
-
if (typeof json.pagination_token === "string" && json.pagination_token.length > 0) {
|
|
1100
|
-
result.paginationToken = json.pagination_token;
|
|
1101
|
-
}
|
|
1102
|
-
return result;
|
|
1103
|
-
}
|
|
1104
|
-
/**
|
|
1105
|
-
* Delete a file. Idempotent: HTTP 404 → success.
|
|
1106
|
-
*
|
|
1107
|
-
* Default (`failClosed` omitted/false): non-404 errors go to `onDeleteError`
|
|
1108
|
-
* and resolve (P5 fail-open). With `failClosed: true`, non-404 errors throw
|
|
1109
|
-
* typed `LlmError` and `onDeleteError` is not called.
|
|
1110
|
-
*
|
|
1111
|
-
* Empty/blank ids always throw `bad_request` (caller fault).
|
|
1112
|
-
*/
|
|
1113
|
-
async delete(fileIdOrHandle, opts) {
|
|
1114
|
-
const fileId = resolveFileId(fileIdOrHandle);
|
|
1115
|
-
if (typeof fileId !== "string" || fileId.trim() === "") {
|
|
1116
|
-
throw badRequest("fileId must be a non-empty string.");
|
|
1117
|
-
}
|
|
1118
|
-
const failClosed = opts?.failClosed === true;
|
|
1119
|
-
const signal = opts?.signal;
|
|
1120
|
-
try {
|
|
1121
|
-
const res = await this.fetchImpl(
|
|
1122
|
-
this.filesUrl(fileId),
|
|
1123
|
-
this.requestInit("DELETE", signal !== void 0 ? { signal } : {})
|
|
1124
|
-
);
|
|
1125
|
-
if (res.status === 404) {
|
|
1126
|
-
return;
|
|
1127
|
-
}
|
|
1128
|
-
if (!res.ok) {
|
|
1129
|
-
await throwHttpFailure(res);
|
|
1130
|
-
}
|
|
1131
|
-
} catch (err) {
|
|
1132
|
-
if (isNotFoundError(err)) {
|
|
1133
|
-
return;
|
|
1134
|
-
}
|
|
1135
|
-
const classified = signal?.aborted === true && !(err instanceof core.LlmError) ? new core.LlmError("xAI file delete aborted", {
|
|
1136
|
-
kind: "aborted",
|
|
1137
|
-
retryable: false,
|
|
1138
|
-
provider: "xai",
|
|
1139
|
-
cause: err
|
|
1140
|
-
}) : classifyStoreError(err);
|
|
1141
|
-
if (failClosed) {
|
|
1142
|
-
throw classified;
|
|
1143
|
-
}
|
|
1144
|
-
this.onDeleteError(fileId, classified);
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
/**
|
|
1148
|
-
* Delete many files.
|
|
1149
|
-
*
|
|
1150
|
-
* Fail-open (default): `Promise.allSettled` — each failure → `onDeleteError`.
|
|
1151
|
-
* Fail-closed: `Promise.all` — first throw rejects; in-flight siblings are
|
|
1152
|
-
* not cancelled (partial deletes may already have succeeded at the provider).
|
|
1153
|
-
* Prefer per-id delete + host DB mark when gating durable release state.
|
|
1154
|
-
*/
|
|
1155
|
-
async deleteAll(ids, opts) {
|
|
1156
|
-
if (opts?.failClosed === true) {
|
|
1157
|
-
await Promise.all(ids.map((id) => this.delete(id, opts)));
|
|
1158
|
-
return;
|
|
1159
|
-
}
|
|
1160
|
-
await Promise.allSettled(ids.map((id) => this.delete(id, opts)));
|
|
1161
|
-
}
|
|
1162
|
-
/** Download raw file bytes. */
|
|
1163
|
-
async getContent(fileId, signal) {
|
|
1164
|
-
if (typeof fileId !== "string" || fileId.trim() === "") {
|
|
1165
|
-
throw badRequest("fileId must be a non-empty string.");
|
|
1166
|
-
}
|
|
1167
|
-
let res;
|
|
1168
|
-
try {
|
|
1169
|
-
res = await this.fetchImpl(
|
|
1170
|
-
this.filesUrl(`${fileId}/content`),
|
|
1171
|
-
this.requestInit("GET", signal !== void 0 ? { signal } : {})
|
|
1665
|
+
...signal !== void 0 ? { signal } : {}
|
|
1666
|
+
})
|
|
1172
1667
|
);
|
|
1173
1668
|
} catch (e) {
|
|
1174
1669
|
if (signal?.aborted === true) {
|
|
1175
|
-
throw new core.LlmError("xAI file
|
|
1176
|
-
kind: "aborted",
|
|
1177
|
-
retryable: false,
|
|
1178
|
-
provider: "xai"
|
|
1179
|
-
});
|
|
1180
|
-
}
|
|
1181
|
-
throw classifyStoreError(e);
|
|
1182
|
-
}
|
|
1183
|
-
if (res.status === 404) {
|
|
1184
|
-
throw notFoundError(fileId, "getContent");
|
|
1185
|
-
}
|
|
1186
|
-
if (!res.ok) {
|
|
1187
|
-
try {
|
|
1188
|
-
await throwHttpFailure(res);
|
|
1189
|
-
} catch (e) {
|
|
1190
|
-
throw classifyStoreError(e);
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
const buf = await res.arrayBuffer();
|
|
1194
|
-
return new Uint8Array(buf);
|
|
1195
|
-
}
|
|
1196
|
-
};
|
|
1197
|
-
var webSearchFlags = {
|
|
1198
|
-
enableImageUnderstanding: zod.z.boolean().optional().meta({
|
|
1199
|
-
title: "Enable Image Understanding",
|
|
1200
|
-
description: "Ask xAI to analyze images found during web search."
|
|
1201
|
-
}),
|
|
1202
|
-
enableImageSearch: zod.z.boolean().optional().meta({
|
|
1203
|
-
title: "Enable Image Search",
|
|
1204
|
-
description: "Ask xAI to include image search results."
|
|
1205
|
-
})
|
|
1206
|
-
};
|
|
1207
|
-
var XaiWebSearchToolSchema = zod.z.union([
|
|
1208
|
-
zod.z.strictObject({
|
|
1209
|
-
type: zod.z.literal("web_search"),
|
|
1210
|
-
allowedDomains: zod.z.array(zod.z.string()).max(5),
|
|
1211
|
-
...webSearchFlags
|
|
1212
|
-
}),
|
|
1213
|
-
zod.z.strictObject({
|
|
1214
|
-
type: zod.z.literal("web_search"),
|
|
1215
|
-
excludedDomains: zod.z.array(zod.z.string()).max(5),
|
|
1216
|
-
...webSearchFlags
|
|
1217
|
-
}),
|
|
1218
|
-
zod.z.strictObject({
|
|
1219
|
-
type: zod.z.literal("web_search"),
|
|
1220
|
-
...webSearchFlags
|
|
1221
|
-
})
|
|
1222
|
-
]).meta({
|
|
1223
|
-
title: "XaiWebSearchTool",
|
|
1224
|
-
description: "xAI web_search server tool. allowedDomains and excludedDomains are mutually exclusive (max 5)."
|
|
1225
|
-
});
|
|
1226
|
-
var xSearchFlags = {
|
|
1227
|
-
fromDate: zod.z.iso.date().optional().meta({
|
|
1228
|
-
title: "From Date",
|
|
1229
|
-
description: "Inclusive ISO-8601 date lower bound for X search."
|
|
1230
|
-
}),
|
|
1231
|
-
toDate: zod.z.iso.date().optional().meta({
|
|
1232
|
-
title: "To Date",
|
|
1233
|
-
description: "Inclusive ISO-8601 date upper bound for X search."
|
|
1234
|
-
}),
|
|
1235
|
-
enableImageUnderstanding: zod.z.boolean().optional().meta({
|
|
1236
|
-
title: "Enable Image Understanding",
|
|
1237
|
-
description: "Ask xAI to analyze images in matched posts."
|
|
1238
|
-
}),
|
|
1239
|
-
enableVideoUnderstanding: zod.z.boolean().optional().meta({
|
|
1240
|
-
title: "Enable Video Understanding",
|
|
1241
|
-
description: "Ask xAI to analyze videos in matched posts."
|
|
1242
|
-
})
|
|
1243
|
-
};
|
|
1244
|
-
var XaiXSearchToolSchema = zod.z.union([
|
|
1245
|
-
zod.z.strictObject({
|
|
1246
|
-
type: zod.z.literal("x_search"),
|
|
1247
|
-
allowedXHandles: zod.z.array(zod.z.string()).max(20),
|
|
1248
|
-
...xSearchFlags
|
|
1249
|
-
}),
|
|
1250
|
-
zod.z.strictObject({
|
|
1251
|
-
type: zod.z.literal("x_search"),
|
|
1252
|
-
excludedXHandles: zod.z.array(zod.z.string()).max(20),
|
|
1253
|
-
...xSearchFlags
|
|
1254
|
-
}),
|
|
1255
|
-
zod.z.strictObject({
|
|
1256
|
-
type: zod.z.literal("x_search"),
|
|
1257
|
-
...xSearchFlags
|
|
1258
|
-
})
|
|
1259
|
-
]).meta({
|
|
1260
|
-
title: "XaiXSearchTool",
|
|
1261
|
-
description: "xAI x_search server tool. allowedXHandles and excludedXHandles are mutually exclusive (max 20)."
|
|
1262
|
-
});
|
|
1263
|
-
var XaiToolsSchema = zod.z.union([
|
|
1264
|
-
zod.z.tuple([XaiWebSearchToolSchema]),
|
|
1265
|
-
zod.z.tuple([XaiXSearchToolSchema]),
|
|
1266
|
-
zod.z.tuple([XaiWebSearchToolSchema, XaiXSearchToolSchema]),
|
|
1267
|
-
zod.z.tuple([XaiXSearchToolSchema, XaiWebSearchToolSchema])
|
|
1268
|
-
]).meta({
|
|
1269
|
-
title: "XaiTools",
|
|
1270
|
-
description: "xAI Live Search tools. At most one web_search and at most one x_search."
|
|
1271
|
-
});
|
|
1272
|
-
var XaiProviderOptionsSchema = zod.z.strictObject({
|
|
1273
|
-
promptCacheKey: zod.z.string().min(1).optional().meta({
|
|
1274
|
-
title: "Prompt Cache Key",
|
|
1275
|
-
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
1276
|
-
}),
|
|
1277
|
-
tools: XaiToolsSchema.optional().meta({
|
|
1278
|
-
title: "Live Search Tools",
|
|
1279
|
-
description: "xAI server-side web_search / x_search tools."
|
|
1280
|
-
}),
|
|
1281
|
-
parallelToolCalls: zod.z.boolean().optional().meta({
|
|
1282
|
-
title: "Parallel Tool Calls",
|
|
1283
|
-
description: "xAI Responses parallel_tool_calls. Not a generic contract field."
|
|
1284
|
-
})
|
|
1285
|
-
}).meta({
|
|
1286
|
-
title: "xAI Provider Options",
|
|
1287
|
-
description: "Allowlisted xAI provider options."
|
|
1288
|
-
});
|
|
1289
|
-
|
|
1290
|
-
// src/model-config/grok-4-5.ts
|
|
1291
|
-
var Grok45ConfigSchema = zod.z.strictObject({
|
|
1292
|
-
temperature: zod.z.number().optional().meta({
|
|
1293
|
-
title: "Temperature",
|
|
1294
|
-
description: "Sampling temperature forwarded verbatim to grok-4.5."
|
|
1295
|
-
}),
|
|
1296
|
-
topP: zod.z.number().optional().meta({
|
|
1297
|
-
title: "Top P",
|
|
1298
|
-
description: "Nucleus sampling parameter forwarded verbatim to grok-4.5."
|
|
1299
|
-
}),
|
|
1300
|
-
maxOutputTokens: zod.z.number().int().positive().optional().meta({
|
|
1301
|
-
title: "Max Output Tokens",
|
|
1302
|
-
description: "Maximum output token cap for grok-4.5. No artificial ceiling \u2014 xAI accepts arbitrarily large values (live-verified); truncation surfaces as finishReason:'length', not an error."
|
|
1303
|
-
}),
|
|
1304
|
-
reasoning: zod.z.strictObject({
|
|
1305
|
-
effort: zod.z.enum(["low", "medium", "high"]).meta({
|
|
1306
|
-
title: "Reasoning Effort",
|
|
1307
|
-
description: 'Reasoning effort for grok-4.5. "low", "medium", and "high" are admitted (live-verified 2026-08-24); "none"/"xhigh" are rejected by the live API. Vendor default when omitted is "high".'
|
|
1308
|
-
})
|
|
1309
|
-
}).optional().meta({
|
|
1310
|
-
title: "Reasoning",
|
|
1311
|
-
description: "grok-4.5 effort-level reasoning configuration. No budgetTokens field \u2014 xAI uses level-style reasoning, not token budgets."
|
|
1312
|
-
}),
|
|
1313
|
-
timeoutMs: zod.z.number().int().positive().optional().meta({
|
|
1314
|
-
title: "Timeout",
|
|
1315
|
-
description: "Logical request timeout in milliseconds."
|
|
1316
|
-
}),
|
|
1317
|
-
providerOptions: zod.z.strictObject({
|
|
1318
|
-
xai: XaiProviderOptionsSchema.optional()
|
|
1319
|
-
}).optional().meta({
|
|
1320
|
-
title: "Provider Options",
|
|
1321
|
-
description: "Provider-specific options accepted for grok-4.5."
|
|
1322
|
-
})
|
|
1323
|
-
}).meta({
|
|
1324
|
-
title: "Grok45Config",
|
|
1325
|
-
description: "Strict Responses API config for model grok-4.5. Level reasoning (low/medium/high), tunable sampling, no service tiers, structured output, vision, Live Search tools, priced.",
|
|
1326
|
-
examples: [{ reasoning: { effort: "high" } }]
|
|
1327
|
-
});
|
|
1328
|
-
var Grok46ConfigSchema = zod.z.strictObject({
|
|
1329
|
-
temperature: zod.z.number().optional().meta({
|
|
1330
|
-
title: "Temperature",
|
|
1331
|
-
description: "Sampling temperature forwarded verbatim to grok-4.6."
|
|
1332
|
-
}),
|
|
1333
|
-
topP: zod.z.number().optional().meta({
|
|
1334
|
-
title: "Top P",
|
|
1335
|
-
description: "Nucleus sampling parameter forwarded verbatim to grok-4.6."
|
|
1336
|
-
}),
|
|
1337
|
-
maxOutputTokens: zod.z.number().int().positive().optional().meta({
|
|
1338
|
-
title: "Max Output Tokens",
|
|
1339
|
-
description: "Maximum output token cap for grok-4.6. No artificial ceiling \u2014 xAI accepts arbitrarily large values; truncation surfaces as finishReason:'length', not an error."
|
|
1340
|
-
}),
|
|
1341
|
-
reasoning: zod.z.strictObject({
|
|
1342
|
-
effort: zod.z.enum(["low", "medium", "high", "xhigh"]).meta({
|
|
1343
|
-
title: "Reasoning Effort",
|
|
1344
|
-
description: 'Reasoning effort for grok-4.6. Live-verified 2026-08-12: "low", "medium", "high", and "xhigh" are accepted; "none" is rejected. Vendor default when omitted is "high".'
|
|
1345
|
-
})
|
|
1346
|
-
}).optional().meta({
|
|
1347
|
-
title: "Reasoning",
|
|
1348
|
-
description: "grok-4.6 effort-level reasoning configuration. No budgetTokens field \u2014 xAI uses level-style reasoning, not token budgets."
|
|
1349
|
-
}),
|
|
1350
|
-
serviceTier: zod.z.literal("priority").optional().meta({
|
|
1351
|
-
title: "Service Tier",
|
|
1352
|
-
description: 'xAI priority processing for grok-4.6 (Responses `service_tier: "priority"`). Echo live-verified 2026-08-12. Bills at 2\xD7 after the cache discount (uncached standard-list 2\xD7 confirmed by live ticks; cached/long-context legs follow the official 2\xD7 rule). Omitted requests stay on xAI default. "flex"/"standard"/"batch" are rejected.'
|
|
1353
|
-
}),
|
|
1354
|
-
timeoutMs: zod.z.number().int().positive().optional().meta({
|
|
1355
|
-
title: "Timeout",
|
|
1356
|
-
description: "Logical request timeout in milliseconds."
|
|
1357
|
-
}),
|
|
1358
|
-
providerOptions: zod.z.strictObject({
|
|
1359
|
-
xai: XaiProviderOptionsSchema.optional()
|
|
1360
|
-
}).optional().meta({
|
|
1361
|
-
title: "Provider Options",
|
|
1362
|
-
description: "Provider-specific options accepted for grok-4.6."
|
|
1363
|
-
})
|
|
1364
|
-
}).meta({
|
|
1365
|
-
title: "Grok46Config",
|
|
1366
|
-
description: "Strict Responses API config for model grok-4.6. Level reasoning (low/medium/high/xhigh), optional priority service tier, tunable sampling, structured output, vision, priced.",
|
|
1367
|
-
examples: [{ reasoning: { effort: "high" } }]
|
|
1368
|
-
});
|
|
1369
|
-
|
|
1370
|
-
// src/models.ts
|
|
1371
|
-
var grok45ModelDescriptor = {
|
|
1372
|
-
model: "grok-4.5",
|
|
1373
|
-
provider: "xai",
|
|
1374
|
-
pricingFamily: "grok-4.5",
|
|
1375
|
-
capabilities: {
|
|
1376
|
-
reasoning: true,
|
|
1377
|
-
reasoningApi: "level",
|
|
1378
|
-
admittedReasoningEfforts: ["low", "medium", "high"],
|
|
1379
|
-
structuredOutput: true,
|
|
1380
|
-
nativeStructuredOutput: true,
|
|
1381
|
-
vision: true,
|
|
1382
|
-
audioInput: false,
|
|
1383
|
-
sampling: "tunable",
|
|
1384
|
-
caching: { explicit: false, minTokens: 0 },
|
|
1385
|
-
grounding: true,
|
|
1386
|
-
functionCalling: true
|
|
1387
|
-
// No serviceTiers key — grok-4.5 has no admitted service-tier vocabulary.
|
|
1388
|
-
},
|
|
1389
|
-
configSchema: Grok45ConfigSchema,
|
|
1390
|
-
configJsonSchema: core.toConfigJsonSchema(Grok45ConfigSchema),
|
|
1391
|
-
validateConfig: core.zodToStandardSchema(Grok45ConfigSchema)
|
|
1392
|
-
};
|
|
1393
|
-
var grok46ModelDescriptor = {
|
|
1394
|
-
model: "grok-4.6",
|
|
1395
|
-
provider: "xai",
|
|
1396
|
-
pricingFamily: "grok-4.6",
|
|
1397
|
-
capabilities: {
|
|
1398
|
-
reasoning: true,
|
|
1399
|
-
reasoningApi: "level",
|
|
1400
|
-
admittedReasoningEfforts: ["low", "medium", "high", "xhigh"],
|
|
1401
|
-
structuredOutput: true,
|
|
1402
|
-
nativeStructuredOutput: true,
|
|
1403
|
-
vision: true,
|
|
1404
|
-
audioInput: false,
|
|
1405
|
-
sampling: "tunable",
|
|
1406
|
-
caching: { explicit: false, minTokens: 0 },
|
|
1407
|
-
grounding: true,
|
|
1408
|
-
functionCalling: true,
|
|
1409
|
-
serviceTiers: ["priority"]
|
|
1410
|
-
},
|
|
1411
|
-
configSchema: Grok46ConfigSchema,
|
|
1412
|
-
configJsonSchema: core.toConfigJsonSchema(Grok46ConfigSchema),
|
|
1413
|
-
validateConfig: core.zodToStandardSchema(Grok46ConfigSchema)
|
|
1414
|
-
};
|
|
1415
|
-
var xaiModelDescriptors = [
|
|
1416
|
-
grok45ModelDescriptor,
|
|
1417
|
-
grok46ModelDescriptor
|
|
1418
|
-
];
|
|
1419
|
-
var xaiRegistry = core.createModelRegistry(xaiModelDescriptors);
|
|
1420
|
-
|
|
1421
|
-
// src/pricing.ts
|
|
1422
|
-
var xaiPricingVersion = "xai-2026-08-24";
|
|
1423
|
-
var XAI_TOOL_RATE_MICRO_USD = {
|
|
1424
|
-
web_search_calls: 5e3,
|
|
1425
|
-
x_search_calls: 5e3
|
|
1426
|
-
};
|
|
1427
|
-
var XAI_TOOL_COUNTER_KEYS = ["web_search_calls", "x_search_calls"];
|
|
1428
|
-
var XAI_PRICING = Object.freeze({
|
|
1429
|
-
// ── grok-4.5 ── $2.00/$6.00 (≤200k), $4.00/$12.00 (>200k); cached $0.30/$0.60
|
|
1430
|
-
"grok-4.5": {
|
|
1431
|
-
inputPerM: 2e6,
|
|
1432
|
-
cachedPerM: 3e5,
|
|
1433
|
-
outputPerM: 6e6,
|
|
1434
|
-
gt200k: {
|
|
1435
|
-
inputPerM: 4e6,
|
|
1436
|
-
cachedPerM: 6e5,
|
|
1437
|
-
outputPerM: 12e6
|
|
1670
|
+
throw new core.LlmError("xAI file upload aborted", {
|
|
1671
|
+
kind: "aborted",
|
|
1672
|
+
retryable: false,
|
|
1673
|
+
provider: "xai"
|
|
1674
|
+
});
|
|
1675
|
+
}
|
|
1676
|
+
throw classifyStoreError(e);
|
|
1438
1677
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1678
|
+
if (!res.ok) {
|
|
1679
|
+
try {
|
|
1680
|
+
await throwHttpFailure(res);
|
|
1681
|
+
} catch (e) {
|
|
1682
|
+
throw classifyStoreError(e);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
let json;
|
|
1686
|
+
try {
|
|
1687
|
+
json = await res.json();
|
|
1688
|
+
} catch (e) {
|
|
1689
|
+
throw new core.LlmError("xAI file upload returned non-JSON body", {
|
|
1690
|
+
kind: "server",
|
|
1691
|
+
retryable: false,
|
|
1692
|
+
provider: "xai",
|
|
1693
|
+
cause: e
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
return makeHandle(json);
|
|
1452
1697
|
}
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1698
|
+
async get(fileId, signal) {
|
|
1699
|
+
if (typeof fileId !== "string" || fileId.trim() === "") {
|
|
1700
|
+
throw badRequest("fileId must be a non-empty string.");
|
|
1701
|
+
}
|
|
1702
|
+
let res;
|
|
1703
|
+
try {
|
|
1704
|
+
res = await this.fetchImpl(
|
|
1705
|
+
this.filesUrl(fileId),
|
|
1706
|
+
this.requestInit("GET", signal !== void 0 ? { signal } : {})
|
|
1707
|
+
);
|
|
1708
|
+
} catch (e) {
|
|
1709
|
+
if (signal?.aborted === true) {
|
|
1710
|
+
throw new core.LlmError("xAI file get aborted", {
|
|
1711
|
+
kind: "aborted",
|
|
1712
|
+
retryable: false,
|
|
1713
|
+
provider: "xai"
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
throw classifyStoreError(e);
|
|
1717
|
+
}
|
|
1718
|
+
if (res.status === 404) {
|
|
1719
|
+
throw notFoundError(fileId, "get");
|
|
1720
|
+
}
|
|
1721
|
+
if (!res.ok) {
|
|
1722
|
+
try {
|
|
1723
|
+
await throwHttpFailure(res);
|
|
1724
|
+
} catch (e) {
|
|
1725
|
+
throw classifyStoreError(e);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
const json = await res.json();
|
|
1729
|
+
return makeHandle(json);
|
|
1461
1730
|
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1731
|
+
async list(opts = {}, signal) {
|
|
1732
|
+
if (opts.limit !== void 0) {
|
|
1733
|
+
if (typeof opts.limit !== "number" || !Number.isInteger(opts.limit) || opts.limit < 1 || opts.limit > 100) {
|
|
1734
|
+
throw badRequest("list.limit must be an integer in [1, 100].");
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
const params = new URLSearchParams();
|
|
1738
|
+
if (opts.limit !== void 0) params.set("limit", String(opts.limit));
|
|
1739
|
+
if (opts.order !== void 0) params.set("order", opts.order);
|
|
1740
|
+
if (opts.sortBy !== void 0) params.set("sort_by", opts.sortBy);
|
|
1741
|
+
if (opts.paginationToken !== void 0) {
|
|
1742
|
+
params.set("pagination_token", opts.paginationToken);
|
|
1743
|
+
}
|
|
1744
|
+
const qs = params.toString();
|
|
1745
|
+
const url = qs.length > 0 ? `${this.filesUrl()}?${qs}` : this.filesUrl();
|
|
1746
|
+
let res;
|
|
1747
|
+
try {
|
|
1748
|
+
res = await this.fetchImpl(
|
|
1749
|
+
url,
|
|
1750
|
+
this.requestInit("GET", signal !== void 0 ? { signal } : {})
|
|
1751
|
+
);
|
|
1752
|
+
} catch (e) {
|
|
1753
|
+
if (signal?.aborted === true) {
|
|
1754
|
+
throw new core.LlmError("xAI file list aborted", {
|
|
1755
|
+
kind: "aborted",
|
|
1756
|
+
retryable: false,
|
|
1757
|
+
provider: "xai"
|
|
1758
|
+
});
|
|
1759
|
+
}
|
|
1760
|
+
throw classifyStoreError(e);
|
|
1761
|
+
}
|
|
1762
|
+
if (!res.ok) {
|
|
1763
|
+
try {
|
|
1764
|
+
await throwHttpFailure(res);
|
|
1765
|
+
} catch (e) {
|
|
1766
|
+
throw classifyStoreError(e);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
const json = await res.json();
|
|
1770
|
+
const files = Array.isArray(json.data) ? json.data.map((f) => makeHandle(f)) : [];
|
|
1771
|
+
const result = { files };
|
|
1772
|
+
if (typeof json.pagination_token === "string" && json.pagination_token.length > 0) {
|
|
1773
|
+
result.paginationToken = json.pagination_token;
|
|
1774
|
+
}
|
|
1775
|
+
return result;
|
|
1479
1776
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1777
|
+
/**
|
|
1778
|
+
* Delete a file. Idempotent: HTTP 404 → success.
|
|
1779
|
+
*
|
|
1780
|
+
* Default (`failClosed` omitted/false): non-404 errors go to `onDeleteError`
|
|
1781
|
+
* and resolve (P5 fail-open). With `failClosed: true`, non-404 errors throw
|
|
1782
|
+
* typed `LlmError` and `onDeleteError` is not called.
|
|
1783
|
+
*
|
|
1784
|
+
* Empty/blank ids always throw `bad_request` (caller fault).
|
|
1785
|
+
*/
|
|
1786
|
+
async delete(fileIdOrHandle, opts) {
|
|
1787
|
+
const fileId = resolveFileId(fileIdOrHandle);
|
|
1788
|
+
if (typeof fileId !== "string" || fileId.trim() === "") {
|
|
1789
|
+
throw badRequest("fileId must be a non-empty string.");
|
|
1790
|
+
}
|
|
1791
|
+
const failClosed = opts?.failClosed === true;
|
|
1792
|
+
const signal = opts?.signal;
|
|
1793
|
+
try {
|
|
1794
|
+
const res = await this.fetchImpl(
|
|
1795
|
+
this.filesUrl(fileId),
|
|
1796
|
+
this.requestInit("DELETE", signal !== void 0 ? { signal } : {})
|
|
1797
|
+
);
|
|
1798
|
+
if (res.status === 404) {
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
if (!res.ok) {
|
|
1802
|
+
await throwHttpFailure(res);
|
|
1803
|
+
}
|
|
1804
|
+
} catch (err) {
|
|
1805
|
+
if (isNotFoundError(err)) {
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
const classified = signal?.aborted === true && !(err instanceof core.LlmError) ? new core.LlmError("xAI file delete aborted", {
|
|
1809
|
+
kind: "aborted",
|
|
1810
|
+
retryable: false,
|
|
1811
|
+
provider: "xai",
|
|
1812
|
+
cause: err
|
|
1813
|
+
}) : classifyStoreError(err);
|
|
1814
|
+
if (failClosed) {
|
|
1815
|
+
throw classified;
|
|
1816
|
+
}
|
|
1817
|
+
this.onDeleteError(fileId, classified);
|
|
1493
1818
|
}
|
|
1494
1819
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
const count = usage.details[key];
|
|
1508
|
-
if (typeof count !== "number" || count <= 0) return sum;
|
|
1509
|
-
return sum + Math.round(count * XAI_TOOL_RATE_MICRO_USD[key]);
|
|
1510
|
-
}, 0);
|
|
1511
|
-
const microUsd = inputCost + cachedCost + outputCost + toolsCost;
|
|
1512
|
-
return {
|
|
1513
|
-
microUsd,
|
|
1514
|
-
usd: microUsd / 1e6,
|
|
1515
|
-
pricingVersion: xaiPricingVersion,
|
|
1516
|
-
confidence: missingRequestedCounters || attachmentUnpinned ? "estimated" : "exact",
|
|
1517
|
-
details: {
|
|
1518
|
-
input: inputCost,
|
|
1519
|
-
cached: cachedCost,
|
|
1520
|
-
output: outputCost,
|
|
1521
|
-
tools: toolsCost
|
|
1820
|
+
/**
|
|
1821
|
+
* Delete many files.
|
|
1822
|
+
*
|
|
1823
|
+
* Fail-open (default): `Promise.allSettled` — each failure → `onDeleteError`.
|
|
1824
|
+
* Fail-closed: `Promise.all` — first throw rejects; in-flight siblings are
|
|
1825
|
+
* not cancelled (partial deletes may already have succeeded at the provider).
|
|
1826
|
+
* Prefer per-id delete + host DB mark when gating durable release state.
|
|
1827
|
+
*/
|
|
1828
|
+
async deleteAll(ids, opts) {
|
|
1829
|
+
if (opts?.failClosed === true) {
|
|
1830
|
+
await Promise.all(ids.map((id) => this.delete(id, opts)));
|
|
1831
|
+
return;
|
|
1522
1832
|
}
|
|
1523
|
-
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
return computeXaiCost(model, usage, tier);
|
|
1530
|
-
},
|
|
1531
|
-
hasModel(model) {
|
|
1532
|
-
return lookupRates(model) !== void 0;
|
|
1533
|
-
},
|
|
1534
|
-
listModels() {
|
|
1535
|
-
return Object.keys(XAI_PRICING);
|
|
1833
|
+
await Promise.allSettled(ids.map((id) => this.delete(id, opts)));
|
|
1834
|
+
}
|
|
1835
|
+
/** Download raw file bytes. */
|
|
1836
|
+
async getContent(fileId, signal) {
|
|
1837
|
+
if (typeof fileId !== "string" || fileId.trim() === "") {
|
|
1838
|
+
throw badRequest("fileId must be a non-empty string.");
|
|
1536
1839
|
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1840
|
+
let res;
|
|
1841
|
+
try {
|
|
1842
|
+
res = await this.fetchImpl(
|
|
1843
|
+
this.filesUrl(`${fileId}/content`),
|
|
1844
|
+
this.requestInit("GET", signal !== void 0 ? { signal } : {})
|
|
1845
|
+
);
|
|
1846
|
+
} catch (e) {
|
|
1847
|
+
if (signal?.aborted === true) {
|
|
1848
|
+
throw new core.LlmError("xAI file content download aborted", {
|
|
1849
|
+
kind: "aborted",
|
|
1850
|
+
retryable: false,
|
|
1851
|
+
provider: "xai"
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1854
|
+
throw classifyStoreError(e);
|
|
1855
|
+
}
|
|
1856
|
+
if (res.status === 404) {
|
|
1857
|
+
throw notFoundError(fileId, "getContent");
|
|
1858
|
+
}
|
|
1859
|
+
if (!res.ok) {
|
|
1860
|
+
try {
|
|
1861
|
+
await throwHttpFailure(res);
|
|
1862
|
+
} catch (e) {
|
|
1863
|
+
throw classifyStoreError(e);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
const buf = await res.arrayBuffer();
|
|
1867
|
+
return new Uint8Array(buf);
|
|
1868
|
+
}
|
|
1869
|
+
};
|
|
1539
1870
|
|
|
1540
1871
|
// src/provider.ts
|
|
1541
1872
|
function xaiProvider(opts) {
|
|
@@ -1548,6 +1879,7 @@ function xaiProvider(opts) {
|
|
|
1548
1879
|
|
|
1549
1880
|
exports.Grok45ConfigSchema = Grok45ConfigSchema;
|
|
1550
1881
|
exports.Grok46ConfigSchema = Grok46ConfigSchema;
|
|
1882
|
+
exports.Grok47ConfigSchema = Grok47ConfigSchema;
|
|
1551
1883
|
exports.XAI_FILES_DEFAULT_BASE_URL = XAI_FILES_DEFAULT_BASE_URL;
|
|
1552
1884
|
exports.XAI_FILE_MAX_BYTES = XAI_FILE_MAX_BYTES;
|
|
1553
1885
|
exports.XAI_FILE_TTL_MAX_SECONDS = XAI_FILE_TTL_MAX_SECONDS;
|
|
@@ -1560,6 +1892,7 @@ exports.classifyXaiError = classifyXaiError;
|
|
|
1560
1892
|
exports.computeXaiCost = computeXaiCost;
|
|
1561
1893
|
exports.grok45ModelDescriptor = grok45ModelDescriptor;
|
|
1562
1894
|
exports.grok46ModelDescriptor = grok46ModelDescriptor;
|
|
1895
|
+
exports.grok47ModelDescriptor = grok47ModelDescriptor;
|
|
1563
1896
|
exports.requireApiKey = requireApiKey;
|
|
1564
1897
|
exports.xaiAdapter = xaiAdapter;
|
|
1565
1898
|
exports.xaiModelDescriptors = xaiModelDescriptors;
|