@gullabs/xai 0.7.0 → 0.8.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/LICENSE +84 -73
- package/README.md +74 -39
- package/dist/index.cjs +730 -548
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +200 -42
- package/dist/index.d.ts +200 -42
- package/dist/index.js +730 -550
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { zodToStandardSchema, toConfigJsonSchema, createModelRegistry, LlmError, classifyError, assertNever, redactSecrets } from '@gullabs/core';
|
|
1
|
+
import { zodToStandardSchema, toConfigJsonSchema, createModelRegistry, LlmError, computeCost, classifyError, assertNever, redactSecrets } from '@gullabs/core';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
// src/client.ts
|
|
@@ -28,6 +28,453 @@ async function buildXaiClient(auth) {
|
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
+
var webSearchFlags = {
|
|
32
|
+
enableImageUnderstanding: z.boolean().optional().meta({
|
|
33
|
+
title: "Enable Image Understanding",
|
|
34
|
+
description: "Ask xAI to analyze images found during web search."
|
|
35
|
+
}),
|
|
36
|
+
enableImageSearch: z.boolean().optional().meta({
|
|
37
|
+
title: "Enable Image Search",
|
|
38
|
+
description: "Ask xAI to include image search results."
|
|
39
|
+
})
|
|
40
|
+
};
|
|
41
|
+
var XaiWebSearchToolSchema = z.union([
|
|
42
|
+
z.strictObject({
|
|
43
|
+
type: z.literal("web_search"),
|
|
44
|
+
allowedDomains: z.array(z.string()).max(5),
|
|
45
|
+
...webSearchFlags
|
|
46
|
+
}),
|
|
47
|
+
z.strictObject({
|
|
48
|
+
type: z.literal("web_search"),
|
|
49
|
+
excludedDomains: z.array(z.string()).max(5),
|
|
50
|
+
...webSearchFlags
|
|
51
|
+
}),
|
|
52
|
+
z.strictObject({
|
|
53
|
+
type: z.literal("web_search"),
|
|
54
|
+
...webSearchFlags
|
|
55
|
+
})
|
|
56
|
+
]).meta({
|
|
57
|
+
title: "XaiWebSearchTool",
|
|
58
|
+
description: "xAI web_search server tool. allowedDomains and excludedDomains are mutually exclusive (max 5)."
|
|
59
|
+
});
|
|
60
|
+
var xSearchFlags = {
|
|
61
|
+
fromDate: z.iso.date().optional().meta({
|
|
62
|
+
title: "From Date",
|
|
63
|
+
description: "Inclusive ISO-8601 date lower bound for X search."
|
|
64
|
+
}),
|
|
65
|
+
toDate: z.iso.date().optional().meta({
|
|
66
|
+
title: "To Date",
|
|
67
|
+
description: "Inclusive ISO-8601 date upper bound for X search."
|
|
68
|
+
}),
|
|
69
|
+
enableImageUnderstanding: z.boolean().optional().meta({
|
|
70
|
+
title: "Enable Image Understanding",
|
|
71
|
+
description: "Ask xAI to analyze images in matched posts."
|
|
72
|
+
}),
|
|
73
|
+
enableVideoUnderstanding: z.boolean().optional().meta({
|
|
74
|
+
title: "Enable Video Understanding",
|
|
75
|
+
description: "Ask xAI to analyze videos in matched posts."
|
|
76
|
+
})
|
|
77
|
+
};
|
|
78
|
+
var XaiXSearchToolSchema = z.union([
|
|
79
|
+
z.strictObject({
|
|
80
|
+
type: z.literal("x_search"),
|
|
81
|
+
allowedXHandles: z.array(z.string()).max(20),
|
|
82
|
+
...xSearchFlags
|
|
83
|
+
}),
|
|
84
|
+
z.strictObject({
|
|
85
|
+
type: z.literal("x_search"),
|
|
86
|
+
excludedXHandles: z.array(z.string()).max(20),
|
|
87
|
+
...xSearchFlags
|
|
88
|
+
}),
|
|
89
|
+
z.strictObject({
|
|
90
|
+
type: z.literal("x_search"),
|
|
91
|
+
...xSearchFlags
|
|
92
|
+
})
|
|
93
|
+
]).meta({
|
|
94
|
+
title: "XaiXSearchTool",
|
|
95
|
+
description: "xAI x_search server tool. allowedXHandles and excludedXHandles are mutually exclusive (max 20)."
|
|
96
|
+
});
|
|
97
|
+
var XaiToolsSchema = z.union([
|
|
98
|
+
z.tuple([XaiWebSearchToolSchema]),
|
|
99
|
+
z.tuple([XaiXSearchToolSchema]),
|
|
100
|
+
z.tuple([XaiWebSearchToolSchema, XaiXSearchToolSchema]),
|
|
101
|
+
z.tuple([XaiXSearchToolSchema, XaiWebSearchToolSchema])
|
|
102
|
+
]).meta({
|
|
103
|
+
title: "XaiTools",
|
|
104
|
+
description: "xAI Live Search tools. At most one web_search and at most one x_search."
|
|
105
|
+
});
|
|
106
|
+
var XaiProviderOptionsSchema = z.strictObject({
|
|
107
|
+
promptCacheKey: z.string().min(1).optional().meta({
|
|
108
|
+
title: "Prompt Cache Key",
|
|
109
|
+
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
110
|
+
}),
|
|
111
|
+
tools: XaiToolsSchema.optional().meta({
|
|
112
|
+
title: "Live Search Tools",
|
|
113
|
+
description: "xAI server-side web_search / x_search tools."
|
|
114
|
+
}),
|
|
115
|
+
parallelToolCalls: z.boolean().optional().meta({
|
|
116
|
+
title: "Parallel Tool Calls",
|
|
117
|
+
description: "xAI Responses parallel_tool_calls. Not a generic contract field."
|
|
118
|
+
})
|
|
119
|
+
}).meta({
|
|
120
|
+
title: "xAI Provider Options",
|
|
121
|
+
description: "Allowlisted xAI provider options."
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// src/model-config/grok-4-5.ts
|
|
125
|
+
var Grok45ConfigSchema = z.strictObject({
|
|
126
|
+
temperature: z.number().optional().meta({
|
|
127
|
+
title: "Temperature",
|
|
128
|
+
description: "Sampling temperature forwarded verbatim to grok-4.5."
|
|
129
|
+
}),
|
|
130
|
+
topP: z.number().optional().meta({
|
|
131
|
+
title: "Top P",
|
|
132
|
+
description: "Nucleus sampling parameter forwarded verbatim to grok-4.5."
|
|
133
|
+
}),
|
|
134
|
+
maxOutputTokens: z.number().int().positive().optional().meta({
|
|
135
|
+
title: "Max Output Tokens",
|
|
136
|
+
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."
|
|
137
|
+
}),
|
|
138
|
+
reasoning: z.strictObject({
|
|
139
|
+
effort: z.enum(["low", "medium", "high"]).meta({
|
|
140
|
+
title: "Reasoning Effort",
|
|
141
|
+
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".'
|
|
142
|
+
})
|
|
143
|
+
}).optional().meta({
|
|
144
|
+
title: "Reasoning",
|
|
145
|
+
description: "grok-4.5 effort-level reasoning configuration. No budgetTokens field \u2014 xAI uses level-style reasoning, not token budgets."
|
|
146
|
+
}),
|
|
147
|
+
serviceTier: z.literal("priority").optional().meta({
|
|
148
|
+
title: "Service Tier",
|
|
149
|
+
description: "xAI priority processing for grok-4.5, billed at 2\xD7 on input, cached input, and output tokens. Live-verified 2026-09-25."
|
|
150
|
+
}),
|
|
151
|
+
timeoutMs: z.number().int().positive().optional().meta({
|
|
152
|
+
title: "Timeout",
|
|
153
|
+
description: "Logical request timeout in milliseconds."
|
|
154
|
+
}),
|
|
155
|
+
providerOptions: z.strictObject({
|
|
156
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
157
|
+
}).optional().meta({
|
|
158
|
+
title: "Provider Options",
|
|
159
|
+
description: "Provider-specific options accepted for grok-4.5."
|
|
160
|
+
})
|
|
161
|
+
}).meta({
|
|
162
|
+
title: "Grok45Config",
|
|
163
|
+
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.",
|
|
164
|
+
examples: [{ reasoning: { effort: "high" } }]
|
|
165
|
+
});
|
|
166
|
+
var Grok46ConfigSchema = z.strictObject({
|
|
167
|
+
temperature: z.number().optional().meta({
|
|
168
|
+
title: "Temperature",
|
|
169
|
+
description: "Sampling temperature forwarded verbatim to grok-4.6."
|
|
170
|
+
}),
|
|
171
|
+
topP: z.number().optional().meta({
|
|
172
|
+
title: "Top P",
|
|
173
|
+
description: "Nucleus sampling parameter forwarded verbatim to grok-4.6."
|
|
174
|
+
}),
|
|
175
|
+
maxOutputTokens: z.number().int().positive().optional().meta({
|
|
176
|
+
title: "Max Output Tokens",
|
|
177
|
+
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."
|
|
178
|
+
}),
|
|
179
|
+
reasoning: z.strictObject({
|
|
180
|
+
effort: z.enum(["low", "medium", "high", "xhigh"]).meta({
|
|
181
|
+
title: "Reasoning Effort",
|
|
182
|
+
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".'
|
|
183
|
+
})
|
|
184
|
+
}).optional().meta({
|
|
185
|
+
title: "Reasoning",
|
|
186
|
+
description: "grok-4.6 effort-level reasoning configuration. No budgetTokens field \u2014 xAI uses level-style reasoning, not token budgets."
|
|
187
|
+
}),
|
|
188
|
+
serviceTier: z.literal("priority").optional().meta({
|
|
189
|
+
title: "Service Tier",
|
|
190
|
+
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.'
|
|
191
|
+
}),
|
|
192
|
+
timeoutMs: z.number().int().positive().optional().meta({
|
|
193
|
+
title: "Timeout",
|
|
194
|
+
description: "Logical request timeout in milliseconds."
|
|
195
|
+
}),
|
|
196
|
+
providerOptions: z.strictObject({
|
|
197
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
198
|
+
}).optional().meta({
|
|
199
|
+
title: "Provider Options",
|
|
200
|
+
description: "Provider-specific options accepted for grok-4.6."
|
|
201
|
+
})
|
|
202
|
+
}).meta({
|
|
203
|
+
title: "Grok46Config",
|
|
204
|
+
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.",
|
|
205
|
+
examples: [{ reasoning: { effort: "high" } }]
|
|
206
|
+
});
|
|
207
|
+
var Grok47ConfigSchema = z.strictObject({
|
|
208
|
+
temperature: z.number().optional().meta({
|
|
209
|
+
title: "Temperature",
|
|
210
|
+
description: "Sampling temperature forwarded verbatim to grok-4.7."
|
|
211
|
+
}),
|
|
212
|
+
topP: z.number().optional().meta({
|
|
213
|
+
title: "Top P",
|
|
214
|
+
description: "Nucleus sampling parameter forwarded verbatim to grok-4.7."
|
|
215
|
+
}),
|
|
216
|
+
maxOutputTokens: z.number().int().positive().optional().meta({
|
|
217
|
+
title: "Max Output Tokens",
|
|
218
|
+
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."
|
|
219
|
+
}),
|
|
220
|
+
reasoning: z.strictObject({
|
|
221
|
+
effort: z.enum(["low", "medium", "high", "xhigh"]).meta({
|
|
222
|
+
title: "Reasoning Effort",
|
|
223
|
+
description: 'Reasoning effort for grok-4.7. "low", "medium", "high", and "xhigh" are accepted; "none" is rejected. Vendor default when omitted is "high".'
|
|
224
|
+
})
|
|
225
|
+
}).optional().meta({
|
|
226
|
+
title: "Reasoning",
|
|
227
|
+
description: "grok-4.7 effort-level reasoning configuration. No budgetTokens field \u2014 xAI uses level-style reasoning, not token budgets."
|
|
228
|
+
}),
|
|
229
|
+
serviceTier: z.literal("priority").optional().meta({
|
|
230
|
+
title: "Service Tier",
|
|
231
|
+
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.'
|
|
232
|
+
}),
|
|
233
|
+
timeoutMs: z.number().int().positive().optional().meta({
|
|
234
|
+
title: "Timeout",
|
|
235
|
+
description: "Logical request timeout in milliseconds."
|
|
236
|
+
}),
|
|
237
|
+
providerOptions: z.strictObject({
|
|
238
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
239
|
+
}).optional().meta({
|
|
240
|
+
title: "Provider Options",
|
|
241
|
+
description: "Provider-specific options accepted for grok-4.7."
|
|
242
|
+
})
|
|
243
|
+
}).meta({
|
|
244
|
+
title: "Grok47Config",
|
|
245
|
+
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.",
|
|
246
|
+
examples: [{ reasoning: { effort: "high" } }]
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// src/models.ts
|
|
250
|
+
var grok45ModelDescriptor = {
|
|
251
|
+
model: "grok-4.5",
|
|
252
|
+
provider: "xai",
|
|
253
|
+
pricingFamily: "grok-4.5",
|
|
254
|
+
capabilities: {
|
|
255
|
+
reasoning: true,
|
|
256
|
+
reasoningApi: "level",
|
|
257
|
+
admittedReasoningEfforts: ["low", "medium", "high"],
|
|
258
|
+
structuredOutput: true,
|
|
259
|
+
nativeStructuredOutput: true,
|
|
260
|
+
vision: true,
|
|
261
|
+
audioInput: false,
|
|
262
|
+
sampling: "tunable",
|
|
263
|
+
caching: { explicit: false, minTokens: 0 },
|
|
264
|
+
grounding: true,
|
|
265
|
+
functionCalling: true,
|
|
266
|
+
serviceTiers: ["priority"]
|
|
267
|
+
},
|
|
268
|
+
configSchema: Grok45ConfigSchema,
|
|
269
|
+
configJsonSchema: toConfigJsonSchema(Grok45ConfigSchema),
|
|
270
|
+
validateConfig: zodToStandardSchema(Grok45ConfigSchema)
|
|
271
|
+
};
|
|
272
|
+
var grok46ModelDescriptor = {
|
|
273
|
+
model: "grok-4.6",
|
|
274
|
+
provider: "xai",
|
|
275
|
+
pricingFamily: "grok-4.6",
|
|
276
|
+
capabilities: {
|
|
277
|
+
reasoning: true,
|
|
278
|
+
reasoningApi: "level",
|
|
279
|
+
admittedReasoningEfforts: ["low", "medium", "high", "xhigh"],
|
|
280
|
+
structuredOutput: true,
|
|
281
|
+
nativeStructuredOutput: true,
|
|
282
|
+
vision: true,
|
|
283
|
+
audioInput: false,
|
|
284
|
+
sampling: "tunable",
|
|
285
|
+
caching: { explicit: false, minTokens: 0 },
|
|
286
|
+
grounding: true,
|
|
287
|
+
structuredOutputWithTools: true,
|
|
288
|
+
functionCalling: true,
|
|
289
|
+
serviceTiers: ["priority"]
|
|
290
|
+
},
|
|
291
|
+
configSchema: Grok46ConfigSchema,
|
|
292
|
+
configJsonSchema: toConfigJsonSchema(Grok46ConfigSchema),
|
|
293
|
+
validateConfig: zodToStandardSchema(Grok46ConfigSchema)
|
|
294
|
+
};
|
|
295
|
+
var grok47ModelDescriptor = {
|
|
296
|
+
model: "grok-4.7",
|
|
297
|
+
provider: "xai",
|
|
298
|
+
pricingFamily: "grok-4.7",
|
|
299
|
+
capabilities: {
|
|
300
|
+
reasoning: true,
|
|
301
|
+
reasoningApi: "level",
|
|
302
|
+
admittedReasoningEfforts: ["low", "medium", "high", "xhigh"],
|
|
303
|
+
structuredOutput: true,
|
|
304
|
+
nativeStructuredOutput: true,
|
|
305
|
+
vision: true,
|
|
306
|
+
audioInput: false,
|
|
307
|
+
sampling: "tunable",
|
|
308
|
+
caching: { explicit: false, minTokens: 0 },
|
|
309
|
+
grounding: true,
|
|
310
|
+
functionCalling: true,
|
|
311
|
+
statelessReasoningReplay: true,
|
|
312
|
+
serviceTiers: ["priority"]
|
|
313
|
+
},
|
|
314
|
+
configSchema: Grok47ConfigSchema,
|
|
315
|
+
configJsonSchema: toConfigJsonSchema(Grok47ConfigSchema),
|
|
316
|
+
validateConfig: zodToStandardSchema(Grok47ConfigSchema)
|
|
317
|
+
};
|
|
318
|
+
var xaiModelDescriptors = [
|
|
319
|
+
grok45ModelDescriptor,
|
|
320
|
+
grok46ModelDescriptor,
|
|
321
|
+
grok47ModelDescriptor
|
|
322
|
+
];
|
|
323
|
+
var xaiRegistry = createModelRegistry(xaiModelDescriptors);
|
|
324
|
+
var xaiPricingVersion = "xai-2026-09-25";
|
|
325
|
+
var XAI_TOOL_RATE_MICRO_USD = {
|
|
326
|
+
web_search_calls: 5e3,
|
|
327
|
+
x_posts_fetched: 5e3,
|
|
328
|
+
x_users_fetched: 1e4
|
|
329
|
+
};
|
|
330
|
+
var XAI_TOOL_COUNTER_KEYS = [
|
|
331
|
+
"web_search_calls",
|
|
332
|
+
"x_posts_fetched",
|
|
333
|
+
"x_users_fetched"
|
|
334
|
+
];
|
|
335
|
+
var X_SEARCH_ITEM_COUNTERS = ["x_posts_fetched", "x_users_fetched"];
|
|
336
|
+
var LONG_CONTEXT_THRESHOLD = 2e5;
|
|
337
|
+
var XAI_PRICING = Object.freeze({
|
|
338
|
+
// ── grok-4.5 ── $2.00/$6.00 (<200k), $4.00/$12.00 (>=200k); cached $0.30/$0.60
|
|
339
|
+
"grok-4.5": {
|
|
340
|
+
inputPerM: 2e6,
|
|
341
|
+
cachedPerM: 3e5,
|
|
342
|
+
outputPerM: 6e6,
|
|
343
|
+
gt200k: {
|
|
344
|
+
inputPerM: 4e6,
|
|
345
|
+
cachedPerM: 6e5,
|
|
346
|
+
outputPerM: 12e6
|
|
347
|
+
},
|
|
348
|
+
priorityFactor: 2
|
|
349
|
+
},
|
|
350
|
+
// ── grok-4.6 ── $2.00/$6.00 (<200k), $4.00/$12.00 (>=200k); cached $0.50/$1.00
|
|
351
|
+
"grok-4.6": {
|
|
352
|
+
inputPerM: 2e6,
|
|
353
|
+
cachedPerM: 5e5,
|
|
354
|
+
outputPerM: 6e6,
|
|
355
|
+
gt200k: {
|
|
356
|
+
inputPerM: 4e6,
|
|
357
|
+
cachedPerM: 1e6,
|
|
358
|
+
outputPerM: 12e6
|
|
359
|
+
},
|
|
360
|
+
// Confirmed 2026-08-12 by fixture 12 cost_in_usd_ticks (2× list).
|
|
361
|
+
priorityFactor: 2
|
|
362
|
+
},
|
|
363
|
+
// ── grok-4.7 ── $2.00/$0.50/$6.00 (<200k), $4.00/$1.00/$12.00 (≥200k); priority 2×
|
|
364
|
+
"grok-4.7": {
|
|
365
|
+
inputPerM: 2e6,
|
|
366
|
+
cachedPerM: 5e5,
|
|
367
|
+
outputPerM: 6e6,
|
|
368
|
+
gt200k: {
|
|
369
|
+
inputPerM: 4e6,
|
|
370
|
+
cachedPerM: 1e6,
|
|
371
|
+
outputPerM: 12e6
|
|
372
|
+
},
|
|
373
|
+
priorityFactor: 2
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
function lookupRates(model) {
|
|
377
|
+
return Object.hasOwn(XAI_PRICING, model) ? XAI_PRICING[model] : void 0;
|
|
378
|
+
}
|
|
379
|
+
var lookupConcreteRates = (model, tier) => {
|
|
380
|
+
const rates = lookupRates(model);
|
|
381
|
+
if (rates === void 0) return void 0;
|
|
382
|
+
if (tier === void 0 || tier === "default") return rates;
|
|
383
|
+
if (tier === "priority" && rates.priorityFactor !== void 0) {
|
|
384
|
+
return scaleRates(rates, rates.priorityFactor);
|
|
385
|
+
}
|
|
386
|
+
return void 0;
|
|
387
|
+
};
|
|
388
|
+
function selectXaiRates(rates, grossInputTokens) {
|
|
389
|
+
if (rates.gt200k !== void 0 && grossInputTokens >= LONG_CONTEXT_THRESHOLD) {
|
|
390
|
+
return rates.gt200k;
|
|
391
|
+
}
|
|
392
|
+
return {
|
|
393
|
+
inputPerM: rates.inputPerM,
|
|
394
|
+
cachedPerM: rates.cachedPerM,
|
|
395
|
+
outputPerM: rates.outputPerM
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function scaleRates(rates, factor) {
|
|
399
|
+
const scaled = {
|
|
400
|
+
inputPerM: rates.inputPerM * factor,
|
|
401
|
+
cachedPerM: rates.cachedPerM * factor,
|
|
402
|
+
outputPerM: rates.outputPerM * factor
|
|
403
|
+
};
|
|
404
|
+
if (rates.gt200k !== void 0) {
|
|
405
|
+
scaled.gt200k = {
|
|
406
|
+
inputPerM: rates.gt200k.inputPerM * factor,
|
|
407
|
+
cachedPerM: rates.gt200k.cachedPerM * factor,
|
|
408
|
+
outputPerM: rates.gt200k.outputPerM * factor
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
return scaled;
|
|
412
|
+
}
|
|
413
|
+
function computeXaiCost(model, usage, tier) {
|
|
414
|
+
const listed = lookupConcreteRates(model, tier);
|
|
415
|
+
if (listed === void 0) {
|
|
416
|
+
return computeCost(model, usage, tier, lookupConcreteRates, xaiPricingVersion);
|
|
417
|
+
}
|
|
418
|
+
const band = selectXaiRates(listed, usage.inputTokens);
|
|
419
|
+
const bandLookup = () => ({
|
|
420
|
+
inputPerM: band.inputPerM,
|
|
421
|
+
cachedPerM: band.cachedPerM,
|
|
422
|
+
outputPerM: band.outputPerM
|
|
423
|
+
});
|
|
424
|
+
const tokenCost = computeCost(model, usage, void 0, bandLookup, xaiPricingVersion);
|
|
425
|
+
const inputCost = tokenCost.details.input;
|
|
426
|
+
const cachedCost = tokenCost.details.cached;
|
|
427
|
+
const outputCost = tokenCost.details.output;
|
|
428
|
+
const serverToolsRequested = usage.details["server_tools_requested"] === 1;
|
|
429
|
+
const xSearchRequested = usage.details["x_search_requested"] === 1;
|
|
430
|
+
const missingXSearchCounter = xSearchRequested && X_SEARCH_ITEM_COUNTERS.some((key) => typeof usage.details[key] !== "number");
|
|
431
|
+
if (missingXSearchCounter || usage.details["server_tools_missing"] === 1) {
|
|
432
|
+
return {
|
|
433
|
+
microUsd: null,
|
|
434
|
+
usd: null,
|
|
435
|
+
pricingVersion: xaiPricingVersion,
|
|
436
|
+
confidence: "estimated",
|
|
437
|
+
details: { input: 0, cached: 0, output: 0, tools: 0 },
|
|
438
|
+
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."
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
const attachmentUnpinned = usage.details["attachment_search_unpinned"] === 1;
|
|
442
|
+
const missingWebCounter = serverToolsRequested && !xSearchRequested && !attachmentUnpinned && !XAI_TOOL_COUNTER_KEYS.some((key) => key in usage.details);
|
|
443
|
+
const toolsCost = missingWebCounter ? 0 : XAI_TOOL_COUNTER_KEYS.reduce((sum, key) => {
|
|
444
|
+
const count = usage.details[key];
|
|
445
|
+
if (typeof count !== "number" || count <= 0) return sum;
|
|
446
|
+
return sum + Math.round(count * XAI_TOOL_RATE_MICRO_USD[key]);
|
|
447
|
+
}, 0);
|
|
448
|
+
const microUsd = inputCost + cachedCost + outputCost + toolsCost;
|
|
449
|
+
return {
|
|
450
|
+
microUsd,
|
|
451
|
+
usd: microUsd / 1e6,
|
|
452
|
+
pricingVersion: xaiPricingVersion,
|
|
453
|
+
confidence: missingWebCounter || attachmentUnpinned ? "estimated" : "exact",
|
|
454
|
+
details: {
|
|
455
|
+
input: inputCost,
|
|
456
|
+
cached: cachedCost,
|
|
457
|
+
output: outputCost,
|
|
458
|
+
tools: toolsCost
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function xaiPricingSource() {
|
|
463
|
+
return {
|
|
464
|
+
version: xaiPricingVersion,
|
|
465
|
+
price(model, usage, tier) {
|
|
466
|
+
return computeXaiCost(model, usage, tier);
|
|
467
|
+
},
|
|
468
|
+
hasModel(model) {
|
|
469
|
+
return lookupRates(model) !== void 0;
|
|
470
|
+
},
|
|
471
|
+
listModels() {
|
|
472
|
+
return Object.keys(XAI_PRICING);
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/adapter.ts
|
|
31
478
|
function isPlainRecord(value) {
|
|
32
479
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33
480
|
}
|
|
@@ -130,6 +577,17 @@ function mapXaiProviderOptions(xaiOpts, model) {
|
|
|
130
577
|
}
|
|
131
578
|
return mapped;
|
|
132
579
|
}
|
|
580
|
+
function parseXaiReplayState(value, model) {
|
|
581
|
+
if (value === void 0) return void 0;
|
|
582
|
+
if (!isPlainRecord(value) || value["model"] !== model || !Array.isArray(value["input"]) || value["input"].length === 0 || value["input"].some(
|
|
583
|
+
(item) => !isPlainRecord(item) || typeof item["type"] !== "string" && typeof item["role"] !== "string"
|
|
584
|
+
)) {
|
|
585
|
+
throw badXaiRequest(
|
|
586
|
+
`transientProviderState must contain the full xAI wire input for model "${model}".`
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
return value;
|
|
590
|
+
}
|
|
133
591
|
function mapXaiSearchTools(tools, model) {
|
|
134
592
|
if (!Array.isArray(tools)) {
|
|
135
593
|
throw badXaiRequest(
|
|
@@ -336,9 +794,53 @@ function xaiAdapter(opts) {
|
|
|
336
794
|
}
|
|
337
795
|
const warnings = [];
|
|
338
796
|
const model = req.model;
|
|
797
|
+
if (req.modelDescriptor !== void 0 && (req.modelDescriptor.model !== model || req.modelDescriptor.provider !== "xai")) {
|
|
798
|
+
throw badXaiRequest(`Mismatched xAI model descriptor for "${model}".`);
|
|
799
|
+
}
|
|
800
|
+
if (xaiRegistry.resolve("xai", model)?.capabilities?.statelessReasoningReplay === true && req.modelDescriptor?.capabilities?.statelessReasoningReplay !== true) {
|
|
801
|
+
throw badXaiRequest(
|
|
802
|
+
`A matching xAI model descriptor with statelessReasoningReplay is required for "${model}".`
|
|
803
|
+
);
|
|
804
|
+
}
|
|
339
805
|
const genConfig = req.config;
|
|
340
|
-
const
|
|
806
|
+
const xaiProviderConfig = mapXaiProviderOptions(
|
|
807
|
+
genConfig.providerOptions?.["xai"],
|
|
808
|
+
model
|
|
809
|
+
);
|
|
810
|
+
const replayRequired = req.modelDescriptor?.capabilities?.statelessReasoningReplay === true;
|
|
811
|
+
const replayState = parseXaiReplayState(req.transientProviderState, model);
|
|
812
|
+
if (replayState !== void 0 && !replayRequired) {
|
|
813
|
+
throw badXaiRequest(
|
|
814
|
+
`transientProviderState requires a statelessReasoningReplay model descriptor for "${model}".`
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
if (replayState !== void 0 && req.messages.length === 0) {
|
|
818
|
+
throw badXaiRequest(
|
|
819
|
+
`Stateless conversation replay for model "${model}" requires new messages to append.`
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
const input = [...replayState?.input ?? []];
|
|
823
|
+
const replayCallIds = new Set(
|
|
824
|
+
replayState?.input.filter((item) => isPlainRecord(item) && item["type"] === "function_call").map((item) => isPlainRecord(item) ? item["call_id"] : void 0).filter((id) => typeof id === "string") ?? []
|
|
825
|
+
);
|
|
826
|
+
const replayedResultIds = new Set(
|
|
827
|
+
replayState?.input.filter(
|
|
828
|
+
(item) => isPlainRecord(item) && item["type"] === "function_call_output"
|
|
829
|
+
).map((item) => isPlainRecord(item) ? item["call_id"] : void 0).filter((id) => typeof id === "string") ?? []
|
|
830
|
+
);
|
|
341
831
|
for (const msg of req.messages) {
|
|
832
|
+
if (replayState !== void 0 && msg.role === "assistant") {
|
|
833
|
+
throw badXaiRequest(
|
|
834
|
+
`New messages for model "${model}" cannot contain assistant history when transientProviderState is supplied.`
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
if (replayRequired && replayState === void 0 && msg.parts.some(
|
|
838
|
+
(part) => part.kind === "tool-call" || part.kind === "tool-result"
|
|
839
|
+
)) {
|
|
840
|
+
throw badXaiRequest(
|
|
841
|
+
`Function-call history for model "${model}" requires transientProviderState from the prior result.`
|
|
842
|
+
);
|
|
843
|
+
}
|
|
342
844
|
const contentParts = [];
|
|
343
845
|
for (const part of msg.parts) {
|
|
344
846
|
if (part.kind === "tool-call") {
|
|
@@ -357,6 +859,12 @@ function xaiAdapter(opts) {
|
|
|
357
859
|
continue;
|
|
358
860
|
}
|
|
359
861
|
if (part.kind === "tool-result") {
|
|
862
|
+
if (replayState !== void 0 && (!replayCallIds.has(part.toolCallId) || replayedResultIds.has(part.toolCallId))) {
|
|
863
|
+
throw badXaiRequest(
|
|
864
|
+
`Tool result "${part.toolCallId}" must match an unanswered function call in transientProviderState.`
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
replayedResultIds.add(part.toolCallId);
|
|
360
868
|
if (contentParts.length > 0) {
|
|
361
869
|
input.push({
|
|
362
870
|
role: msg.role === "assistant" ? "assistant" : "user",
|
|
@@ -419,9 +927,9 @@ function xaiAdapter(opts) {
|
|
|
419
927
|
}
|
|
420
928
|
if (reasoning.effort !== void 0) {
|
|
421
929
|
const effort = reasoning.effort;
|
|
422
|
-
if (effort === "none") {
|
|
930
|
+
if (effort === "none" || effort === "max") {
|
|
423
931
|
throw badXaiRequest(
|
|
424
|
-
`reasoning.effort "
|
|
932
|
+
`reasoning.effort "${effort}" is not supported for xai model "${model}".`
|
|
425
933
|
);
|
|
426
934
|
}
|
|
427
935
|
const admitted = req.modelDescriptor?.capabilities?.admittedReasoningEfforts;
|
|
@@ -441,15 +949,13 @@ function xaiAdapter(opts) {
|
|
|
441
949
|
format: { type: "json_schema", name, schema, strict: true }
|
|
442
950
|
};
|
|
443
951
|
}
|
|
444
|
-
const xaiProviderConfig = mapXaiProviderOptions(
|
|
445
|
-
genConfig.providerOptions?.["xai"],
|
|
446
|
-
model
|
|
447
|
-
);
|
|
448
952
|
if (xaiProviderConfig.promptCacheKey !== void 0) {
|
|
449
953
|
params.prompt_cache_key = xaiProviderConfig.promptCacheKey;
|
|
450
954
|
}
|
|
451
|
-
const hasFileRef =
|
|
452
|
-
(
|
|
955
|
+
const hasFileRef = input.some(
|
|
956
|
+
(item) => isPlainRecord(item) && Array.isArray(item["content"]) && item["content"].some(
|
|
957
|
+
(part) => isPlainRecord(part) && part["type"] === "input_file"
|
|
958
|
+
)
|
|
453
959
|
);
|
|
454
960
|
const searchTools = xaiProviderConfig.tools;
|
|
455
961
|
if (searchTools !== void 0) {
|
|
@@ -458,6 +964,11 @@ function xaiAdapter(opts) {
|
|
|
458
964
|
`providerOptions.xai.tools requires capabilities.grounding on the model descriptor for "${model}".`
|
|
459
965
|
);
|
|
460
966
|
}
|
|
967
|
+
if (structuredOutputRequested && req.modelDescriptor.capabilities.structuredOutputWithTools !== true) {
|
|
968
|
+
throw badXaiRequest(
|
|
969
|
+
`Structured output with providerOptions.xai.tools is not supported for model "${model}".`
|
|
970
|
+
);
|
|
971
|
+
}
|
|
461
972
|
params.tools = searchTools;
|
|
462
973
|
}
|
|
463
974
|
if (req.tools !== void 0 && req.tools.length > 0) {
|
|
@@ -546,6 +1057,9 @@ function xaiAdapter(opts) {
|
|
|
546
1057
|
xaiProviderConfig.tools);
|
|
547
1058
|
if (expectedToolCounters.length > 0 || hasFileRef) {
|
|
548
1059
|
usage.details["server_tools_requested"] = 1;
|
|
1060
|
+
if (xaiProviderConfig.tools?.some((tool) => tool["type"] === "x_search") === true) {
|
|
1061
|
+
usage.details["x_search_requested"] = 1;
|
|
1062
|
+
}
|
|
549
1063
|
const missing = expectedToolCounters.filter((key) => !(key in usage.details));
|
|
550
1064
|
if (missing.length > 0) {
|
|
551
1065
|
usage.details["server_tools_missing"] = 1;
|
|
@@ -553,7 +1067,7 @@ function xaiAdapter(opts) {
|
|
|
553
1067
|
type: "other",
|
|
554
1068
|
message: `xai: server tools were requested but usage is missing counters [${missing.join(
|
|
555
1069
|
", "
|
|
556
|
-
)}];
|
|
1070
|
+
)}]; the call is unpriced.`
|
|
557
1071
|
});
|
|
558
1072
|
}
|
|
559
1073
|
if (hasFileRef) {
|
|
@@ -573,6 +1087,14 @@ function xaiAdapter(opts) {
|
|
|
573
1087
|
if (isPlainRecord(response.metadata)) {
|
|
574
1088
|
providerMeta["metadata"] = response.metadata;
|
|
575
1089
|
}
|
|
1090
|
+
let transientProviderState;
|
|
1091
|
+
if (replayRequired) {
|
|
1092
|
+
const state = {
|
|
1093
|
+
model,
|
|
1094
|
+
input: [...params.input, ...response.output]
|
|
1095
|
+
};
|
|
1096
|
+
transientProviderState = state;
|
|
1097
|
+
}
|
|
576
1098
|
const servedServiceTier = typeof response.service_tier === "string" && response.service_tier.length > 0 ? response.service_tier : void 0;
|
|
577
1099
|
const result = {
|
|
578
1100
|
model: response.model,
|
|
@@ -585,6 +1107,7 @@ function xaiAdapter(opts) {
|
|
|
585
1107
|
...rawStructured !== void 0 ? { rawStructured } : {},
|
|
586
1108
|
...servedServiceTier !== void 0 ? { servedServiceTier } : {},
|
|
587
1109
|
...Object.keys(providerMeta).length > 0 ? { providerMetadata: providerMeta } : {},
|
|
1110
|
+
...transientProviderState !== void 0 ? { transientProviderState } : {},
|
|
588
1111
|
...citations.length > 0 ? { citations } : {},
|
|
589
1112
|
...toolCalls.length > 0 ? { toolCalls, finishReason: "tool_calls" } : {}
|
|
590
1113
|
};
|
|
@@ -656,13 +1179,12 @@ function xaiAdapter(opts) {
|
|
|
656
1179
|
};
|
|
657
1180
|
}
|
|
658
1181
|
var WEB_SEARCH_COUNTER = "web_search_calls";
|
|
659
|
-
var X_SEARCH_COUNTER = "x_search_calls";
|
|
660
1182
|
function expectedServerToolCounters(tools, _hasFileRef) {
|
|
661
1183
|
const keys = [];
|
|
662
1184
|
if (tools !== void 0) {
|
|
663
1185
|
for (const tool of tools) {
|
|
664
1186
|
if (tool["type"] === "web_search") keys.push(WEB_SEARCH_COUNTER);
|
|
665
|
-
if (tool["type"] === "x_search") keys.push(
|
|
1187
|
+
if (tool["type"] === "x_search") keys.push(...X_SEARCH_ITEM_COUNTERS);
|
|
666
1188
|
}
|
|
667
1189
|
}
|
|
668
1190
|
return keys;
|
|
@@ -987,553 +1509,211 @@ var XaiFileStore = class {
|
|
|
987
1509
|
this.filesUrl(),
|
|
988
1510
|
this.requestInit("POST", {
|
|
989
1511
|
body: form,
|
|
990
|
-
...signal !== void 0 ? { signal } : {}
|
|
991
|
-
})
|
|
992
|
-
);
|
|
993
|
-
} catch (e) {
|
|
994
|
-
if (signal?.aborted === true) {
|
|
995
|
-
throw new LlmError("xAI file upload aborted", {
|
|
996
|
-
kind: "aborted",
|
|
997
|
-
retryable: false,
|
|
998
|
-
provider: "xai"
|
|
999
|
-
});
|
|
1000
|
-
}
|
|
1001
|
-
throw classifyStoreError(e);
|
|
1002
|
-
}
|
|
1003
|
-
if (!res.ok) {
|
|
1004
|
-
try {
|
|
1005
|
-
await throwHttpFailure(res);
|
|
1006
|
-
} catch (e) {
|
|
1007
|
-
throw classifyStoreError(e);
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
let json;
|
|
1011
|
-
try {
|
|
1012
|
-
json = await res.json();
|
|
1013
|
-
} catch (e) {
|
|
1014
|
-
throw new LlmError("xAI file upload returned non-JSON body", {
|
|
1015
|
-
kind: "server",
|
|
1016
|
-
retryable: false,
|
|
1017
|
-
provider: "xai",
|
|
1018
|
-
cause: e
|
|
1019
|
-
});
|
|
1020
|
-
}
|
|
1021
|
-
return makeHandle(json);
|
|
1022
|
-
}
|
|
1023
|
-
async get(fileId, signal) {
|
|
1024
|
-
if (typeof fileId !== "string" || fileId.trim() === "") {
|
|
1025
|
-
throw badRequest("fileId must be a non-empty string.");
|
|
1026
|
-
}
|
|
1027
|
-
let res;
|
|
1028
|
-
try {
|
|
1029
|
-
res = await this.fetchImpl(
|
|
1030
|
-
this.filesUrl(fileId),
|
|
1031
|
-
this.requestInit("GET", signal !== void 0 ? { signal } : {})
|
|
1032
|
-
);
|
|
1033
|
-
} catch (e) {
|
|
1034
|
-
if (signal?.aborted === true) {
|
|
1035
|
-
throw new LlmError("xAI file get aborted", {
|
|
1036
|
-
kind: "aborted",
|
|
1037
|
-
retryable: false,
|
|
1038
|
-
provider: "xai"
|
|
1039
|
-
});
|
|
1040
|
-
}
|
|
1041
|
-
throw classifyStoreError(e);
|
|
1042
|
-
}
|
|
1043
|
-
if (res.status === 404) {
|
|
1044
|
-
throw notFoundError(fileId, "get");
|
|
1045
|
-
}
|
|
1046
|
-
if (!res.ok) {
|
|
1047
|
-
try {
|
|
1048
|
-
await throwHttpFailure(res);
|
|
1049
|
-
} catch (e) {
|
|
1050
|
-
throw classifyStoreError(e);
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
const json = await res.json();
|
|
1054
|
-
return makeHandle(json);
|
|
1055
|
-
}
|
|
1056
|
-
async list(opts = {}, signal) {
|
|
1057
|
-
if (opts.limit !== void 0) {
|
|
1058
|
-
if (typeof opts.limit !== "number" || !Number.isInteger(opts.limit) || opts.limit < 1 || opts.limit > 100) {
|
|
1059
|
-
throw badRequest("list.limit must be an integer in [1, 100].");
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
const params = new URLSearchParams();
|
|
1063
|
-
if (opts.limit !== void 0) params.set("limit", String(opts.limit));
|
|
1064
|
-
if (opts.order !== void 0) params.set("order", opts.order);
|
|
1065
|
-
if (opts.sortBy !== void 0) params.set("sort_by", opts.sortBy);
|
|
1066
|
-
if (opts.paginationToken !== void 0) {
|
|
1067
|
-
params.set("pagination_token", opts.paginationToken);
|
|
1068
|
-
}
|
|
1069
|
-
const qs = params.toString();
|
|
1070
|
-
const url = qs.length > 0 ? `${this.filesUrl()}?${qs}` : this.filesUrl();
|
|
1071
|
-
let res;
|
|
1072
|
-
try {
|
|
1073
|
-
res = await this.fetchImpl(
|
|
1074
|
-
url,
|
|
1075
|
-
this.requestInit("GET", signal !== void 0 ? { signal } : {})
|
|
1076
|
-
);
|
|
1077
|
-
} catch (e) {
|
|
1078
|
-
if (signal?.aborted === true) {
|
|
1079
|
-
throw new LlmError("xAI file list aborted", {
|
|
1080
|
-
kind: "aborted",
|
|
1081
|
-
retryable: false,
|
|
1082
|
-
provider: "xai"
|
|
1083
|
-
});
|
|
1084
|
-
}
|
|
1085
|
-
throw classifyStoreError(e);
|
|
1086
|
-
}
|
|
1087
|
-
if (!res.ok) {
|
|
1088
|
-
try {
|
|
1089
|
-
await throwHttpFailure(res);
|
|
1090
|
-
} catch (e) {
|
|
1091
|
-
throw classifyStoreError(e);
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
const json = await res.json();
|
|
1095
|
-
const files = Array.isArray(json.data) ? json.data.map((f) => makeHandle(f)) : [];
|
|
1096
|
-
const result = { files };
|
|
1097
|
-
if (typeof json.pagination_token === "string" && json.pagination_token.length > 0) {
|
|
1098
|
-
result.paginationToken = json.pagination_token;
|
|
1099
|
-
}
|
|
1100
|
-
return result;
|
|
1101
|
-
}
|
|
1102
|
-
/**
|
|
1103
|
-
* Delete a file. Idempotent: HTTP 404 → success.
|
|
1104
|
-
*
|
|
1105
|
-
* Default (`failClosed` omitted/false): non-404 errors go to `onDeleteError`
|
|
1106
|
-
* and resolve (P5 fail-open). With `failClosed: true`, non-404 errors throw
|
|
1107
|
-
* typed `LlmError` and `onDeleteError` is not called.
|
|
1108
|
-
*
|
|
1109
|
-
* Empty/blank ids always throw `bad_request` (caller fault).
|
|
1110
|
-
*/
|
|
1111
|
-
async delete(fileIdOrHandle, opts) {
|
|
1112
|
-
const fileId = resolveFileId(fileIdOrHandle);
|
|
1113
|
-
if (typeof fileId !== "string" || fileId.trim() === "") {
|
|
1114
|
-
throw badRequest("fileId must be a non-empty string.");
|
|
1115
|
-
}
|
|
1116
|
-
const failClosed = opts?.failClosed === true;
|
|
1117
|
-
const signal = opts?.signal;
|
|
1118
|
-
try {
|
|
1119
|
-
const res = await this.fetchImpl(
|
|
1120
|
-
this.filesUrl(fileId),
|
|
1121
|
-
this.requestInit("DELETE", signal !== void 0 ? { signal } : {})
|
|
1122
|
-
);
|
|
1123
|
-
if (res.status === 404) {
|
|
1124
|
-
return;
|
|
1125
|
-
}
|
|
1126
|
-
if (!res.ok) {
|
|
1127
|
-
await throwHttpFailure(res);
|
|
1128
|
-
}
|
|
1129
|
-
} catch (err) {
|
|
1130
|
-
if (isNotFoundError(err)) {
|
|
1131
|
-
return;
|
|
1132
|
-
}
|
|
1133
|
-
const classified = signal?.aborted === true && !(err instanceof LlmError) ? new LlmError("xAI file delete aborted", {
|
|
1134
|
-
kind: "aborted",
|
|
1135
|
-
retryable: false,
|
|
1136
|
-
provider: "xai",
|
|
1137
|
-
cause: err
|
|
1138
|
-
}) : classifyStoreError(err);
|
|
1139
|
-
if (failClosed) {
|
|
1140
|
-
throw classified;
|
|
1141
|
-
}
|
|
1142
|
-
this.onDeleteError(fileId, classified);
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
/**
|
|
1146
|
-
* Delete many files.
|
|
1147
|
-
*
|
|
1148
|
-
* Fail-open (default): `Promise.allSettled` — each failure → `onDeleteError`.
|
|
1149
|
-
* Fail-closed: `Promise.all` — first throw rejects; in-flight siblings are
|
|
1150
|
-
* not cancelled (partial deletes may already have succeeded at the provider).
|
|
1151
|
-
* Prefer per-id delete + host DB mark when gating durable release state.
|
|
1152
|
-
*/
|
|
1153
|
-
async deleteAll(ids, opts) {
|
|
1154
|
-
if (opts?.failClosed === true) {
|
|
1155
|
-
await Promise.all(ids.map((id) => this.delete(id, opts)));
|
|
1156
|
-
return;
|
|
1157
|
-
}
|
|
1158
|
-
await Promise.allSettled(ids.map((id) => this.delete(id, opts)));
|
|
1159
|
-
}
|
|
1160
|
-
/** Download raw file bytes. */
|
|
1161
|
-
async getContent(fileId, signal) {
|
|
1162
|
-
if (typeof fileId !== "string" || fileId.trim() === "") {
|
|
1163
|
-
throw badRequest("fileId must be a non-empty string.");
|
|
1164
|
-
}
|
|
1165
|
-
let res;
|
|
1166
|
-
try {
|
|
1167
|
-
res = await this.fetchImpl(
|
|
1168
|
-
this.filesUrl(`${fileId}/content`),
|
|
1169
|
-
this.requestInit("GET", signal !== void 0 ? { signal } : {})
|
|
1512
|
+
...signal !== void 0 ? { signal } : {}
|
|
1513
|
+
})
|
|
1170
1514
|
);
|
|
1171
1515
|
} catch (e) {
|
|
1172
1516
|
if (signal?.aborted === true) {
|
|
1173
|
-
throw new LlmError("xAI file
|
|
1174
|
-
kind: "aborted",
|
|
1175
|
-
retryable: false,
|
|
1176
|
-
provider: "xai"
|
|
1177
|
-
});
|
|
1178
|
-
}
|
|
1179
|
-
throw classifyStoreError(e);
|
|
1180
|
-
}
|
|
1181
|
-
if (res.status === 404) {
|
|
1182
|
-
throw notFoundError(fileId, "getContent");
|
|
1183
|
-
}
|
|
1184
|
-
if (!res.ok) {
|
|
1185
|
-
try {
|
|
1186
|
-
await throwHttpFailure(res);
|
|
1187
|
-
} catch (e) {
|
|
1188
|
-
throw classifyStoreError(e);
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
const buf = await res.arrayBuffer();
|
|
1192
|
-
return new Uint8Array(buf);
|
|
1193
|
-
}
|
|
1194
|
-
};
|
|
1195
|
-
var webSearchFlags = {
|
|
1196
|
-
enableImageUnderstanding: z.boolean().optional().meta({
|
|
1197
|
-
title: "Enable Image Understanding",
|
|
1198
|
-
description: "Ask xAI to analyze images found during web search."
|
|
1199
|
-
}),
|
|
1200
|
-
enableImageSearch: z.boolean().optional().meta({
|
|
1201
|
-
title: "Enable Image Search",
|
|
1202
|
-
description: "Ask xAI to include image search results."
|
|
1203
|
-
})
|
|
1204
|
-
};
|
|
1205
|
-
var XaiWebSearchToolSchema = z.union([
|
|
1206
|
-
z.strictObject({
|
|
1207
|
-
type: z.literal("web_search"),
|
|
1208
|
-
allowedDomains: z.array(z.string()).max(5),
|
|
1209
|
-
...webSearchFlags
|
|
1210
|
-
}),
|
|
1211
|
-
z.strictObject({
|
|
1212
|
-
type: z.literal("web_search"),
|
|
1213
|
-
excludedDomains: z.array(z.string()).max(5),
|
|
1214
|
-
...webSearchFlags
|
|
1215
|
-
}),
|
|
1216
|
-
z.strictObject({
|
|
1217
|
-
type: z.literal("web_search"),
|
|
1218
|
-
...webSearchFlags
|
|
1219
|
-
})
|
|
1220
|
-
]).meta({
|
|
1221
|
-
title: "XaiWebSearchTool",
|
|
1222
|
-
description: "xAI web_search server tool. allowedDomains and excludedDomains are mutually exclusive (max 5)."
|
|
1223
|
-
});
|
|
1224
|
-
var xSearchFlags = {
|
|
1225
|
-
fromDate: z.iso.date().optional().meta({
|
|
1226
|
-
title: "From Date",
|
|
1227
|
-
description: "Inclusive ISO-8601 date lower bound for X search."
|
|
1228
|
-
}),
|
|
1229
|
-
toDate: z.iso.date().optional().meta({
|
|
1230
|
-
title: "To Date",
|
|
1231
|
-
description: "Inclusive ISO-8601 date upper bound for X search."
|
|
1232
|
-
}),
|
|
1233
|
-
enableImageUnderstanding: z.boolean().optional().meta({
|
|
1234
|
-
title: "Enable Image Understanding",
|
|
1235
|
-
description: "Ask xAI to analyze images in matched posts."
|
|
1236
|
-
}),
|
|
1237
|
-
enableVideoUnderstanding: z.boolean().optional().meta({
|
|
1238
|
-
title: "Enable Video Understanding",
|
|
1239
|
-
description: "Ask xAI to analyze videos in matched posts."
|
|
1240
|
-
})
|
|
1241
|
-
};
|
|
1242
|
-
var XaiXSearchToolSchema = z.union([
|
|
1243
|
-
z.strictObject({
|
|
1244
|
-
type: z.literal("x_search"),
|
|
1245
|
-
allowedXHandles: z.array(z.string()).max(20),
|
|
1246
|
-
...xSearchFlags
|
|
1247
|
-
}),
|
|
1248
|
-
z.strictObject({
|
|
1249
|
-
type: z.literal("x_search"),
|
|
1250
|
-
excludedXHandles: z.array(z.string()).max(20),
|
|
1251
|
-
...xSearchFlags
|
|
1252
|
-
}),
|
|
1253
|
-
z.strictObject({
|
|
1254
|
-
type: z.literal("x_search"),
|
|
1255
|
-
...xSearchFlags
|
|
1256
|
-
})
|
|
1257
|
-
]).meta({
|
|
1258
|
-
title: "XaiXSearchTool",
|
|
1259
|
-
description: "xAI x_search server tool. allowedXHandles and excludedXHandles are mutually exclusive (max 20)."
|
|
1260
|
-
});
|
|
1261
|
-
var XaiToolsSchema = z.union([
|
|
1262
|
-
z.tuple([XaiWebSearchToolSchema]),
|
|
1263
|
-
z.tuple([XaiXSearchToolSchema]),
|
|
1264
|
-
z.tuple([XaiWebSearchToolSchema, XaiXSearchToolSchema]),
|
|
1265
|
-
z.tuple([XaiXSearchToolSchema, XaiWebSearchToolSchema])
|
|
1266
|
-
]).meta({
|
|
1267
|
-
title: "XaiTools",
|
|
1268
|
-
description: "xAI Live Search tools. At most one web_search and at most one x_search."
|
|
1269
|
-
});
|
|
1270
|
-
var XaiProviderOptionsSchema = z.strictObject({
|
|
1271
|
-
promptCacheKey: z.string().min(1).optional().meta({
|
|
1272
|
-
title: "Prompt Cache Key",
|
|
1273
|
-
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
1274
|
-
}),
|
|
1275
|
-
tools: XaiToolsSchema.optional().meta({
|
|
1276
|
-
title: "Live Search Tools",
|
|
1277
|
-
description: "xAI server-side web_search / x_search tools."
|
|
1278
|
-
}),
|
|
1279
|
-
parallelToolCalls: z.boolean().optional().meta({
|
|
1280
|
-
title: "Parallel Tool Calls",
|
|
1281
|
-
description: "xAI Responses parallel_tool_calls. Not a generic contract field."
|
|
1282
|
-
})
|
|
1283
|
-
}).meta({
|
|
1284
|
-
title: "xAI Provider Options",
|
|
1285
|
-
description: "Allowlisted xAI provider options."
|
|
1286
|
-
});
|
|
1287
|
-
|
|
1288
|
-
// src/model-config/grok-4-5.ts
|
|
1289
|
-
var Grok45ConfigSchema = z.strictObject({
|
|
1290
|
-
temperature: z.number().optional().meta({
|
|
1291
|
-
title: "Temperature",
|
|
1292
|
-
description: "Sampling temperature forwarded verbatim to grok-4.5."
|
|
1293
|
-
}),
|
|
1294
|
-
topP: z.number().optional().meta({
|
|
1295
|
-
title: "Top P",
|
|
1296
|
-
description: "Nucleus sampling parameter forwarded verbatim to grok-4.5."
|
|
1297
|
-
}),
|
|
1298
|
-
maxOutputTokens: z.number().int().positive().optional().meta({
|
|
1299
|
-
title: "Max Output Tokens",
|
|
1300
|
-
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."
|
|
1301
|
-
}),
|
|
1302
|
-
reasoning: z.strictObject({
|
|
1303
|
-
effort: z.enum(["low", "medium", "high"]).meta({
|
|
1304
|
-
title: "Reasoning Effort",
|
|
1305
|
-
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".'
|
|
1306
|
-
})
|
|
1307
|
-
}).optional().meta({
|
|
1308
|
-
title: "Reasoning",
|
|
1309
|
-
description: "grok-4.5 effort-level reasoning configuration. No budgetTokens field \u2014 xAI uses level-style reasoning, not token budgets."
|
|
1310
|
-
}),
|
|
1311
|
-
timeoutMs: z.number().int().positive().optional().meta({
|
|
1312
|
-
title: "Timeout",
|
|
1313
|
-
description: "Logical request timeout in milliseconds."
|
|
1314
|
-
}),
|
|
1315
|
-
providerOptions: z.strictObject({
|
|
1316
|
-
xai: XaiProviderOptionsSchema.optional()
|
|
1317
|
-
}).optional().meta({
|
|
1318
|
-
title: "Provider Options",
|
|
1319
|
-
description: "Provider-specific options accepted for grok-4.5."
|
|
1320
|
-
})
|
|
1321
|
-
}).meta({
|
|
1322
|
-
title: "Grok45Config",
|
|
1323
|
-
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.",
|
|
1324
|
-
examples: [{ reasoning: { effort: "high" } }]
|
|
1325
|
-
});
|
|
1326
|
-
var Grok46ConfigSchema = z.strictObject({
|
|
1327
|
-
temperature: z.number().optional().meta({
|
|
1328
|
-
title: "Temperature",
|
|
1329
|
-
description: "Sampling temperature forwarded verbatim to grok-4.6."
|
|
1330
|
-
}),
|
|
1331
|
-
topP: z.number().optional().meta({
|
|
1332
|
-
title: "Top P",
|
|
1333
|
-
description: "Nucleus sampling parameter forwarded verbatim to grok-4.6."
|
|
1334
|
-
}),
|
|
1335
|
-
maxOutputTokens: z.number().int().positive().optional().meta({
|
|
1336
|
-
title: "Max Output Tokens",
|
|
1337
|
-
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."
|
|
1338
|
-
}),
|
|
1339
|
-
reasoning: z.strictObject({
|
|
1340
|
-
effort: z.enum(["low", "medium", "high", "xhigh"]).meta({
|
|
1341
|
-
title: "Reasoning Effort",
|
|
1342
|
-
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".'
|
|
1343
|
-
})
|
|
1344
|
-
}).optional().meta({
|
|
1345
|
-
title: "Reasoning",
|
|
1346
|
-
description: "grok-4.6 effort-level reasoning configuration. No budgetTokens field \u2014 xAI uses level-style reasoning, not token budgets."
|
|
1347
|
-
}),
|
|
1348
|
-
serviceTier: z.literal("priority").optional().meta({
|
|
1349
|
-
title: "Service Tier",
|
|
1350
|
-
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.'
|
|
1351
|
-
}),
|
|
1352
|
-
timeoutMs: z.number().int().positive().optional().meta({
|
|
1353
|
-
title: "Timeout",
|
|
1354
|
-
description: "Logical request timeout in milliseconds."
|
|
1355
|
-
}),
|
|
1356
|
-
providerOptions: z.strictObject({
|
|
1357
|
-
xai: XaiProviderOptionsSchema.optional()
|
|
1358
|
-
}).optional().meta({
|
|
1359
|
-
title: "Provider Options",
|
|
1360
|
-
description: "Provider-specific options accepted for grok-4.6."
|
|
1361
|
-
})
|
|
1362
|
-
}).meta({
|
|
1363
|
-
title: "Grok46Config",
|
|
1364
|
-
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.",
|
|
1365
|
-
examples: [{ reasoning: { effort: "high" } }]
|
|
1366
|
-
});
|
|
1367
|
-
|
|
1368
|
-
// src/models.ts
|
|
1369
|
-
var grok45ModelDescriptor = {
|
|
1370
|
-
model: "grok-4.5",
|
|
1371
|
-
provider: "xai",
|
|
1372
|
-
pricingFamily: "grok-4.5",
|
|
1373
|
-
capabilities: {
|
|
1374
|
-
reasoning: true,
|
|
1375
|
-
reasoningApi: "level",
|
|
1376
|
-
admittedReasoningEfforts: ["low", "medium", "high"],
|
|
1377
|
-
structuredOutput: true,
|
|
1378
|
-
nativeStructuredOutput: true,
|
|
1379
|
-
vision: true,
|
|
1380
|
-
audioInput: false,
|
|
1381
|
-
sampling: "tunable",
|
|
1382
|
-
caching: { explicit: false, minTokens: 0 },
|
|
1383
|
-
grounding: true,
|
|
1384
|
-
functionCalling: true
|
|
1385
|
-
// No serviceTiers key — grok-4.5 has no admitted service-tier vocabulary.
|
|
1386
|
-
},
|
|
1387
|
-
configSchema: Grok45ConfigSchema,
|
|
1388
|
-
configJsonSchema: toConfigJsonSchema(Grok45ConfigSchema),
|
|
1389
|
-
validateConfig: zodToStandardSchema(Grok45ConfigSchema)
|
|
1390
|
-
};
|
|
1391
|
-
var grok46ModelDescriptor = {
|
|
1392
|
-
model: "grok-4.6",
|
|
1393
|
-
provider: "xai",
|
|
1394
|
-
pricingFamily: "grok-4.6",
|
|
1395
|
-
capabilities: {
|
|
1396
|
-
reasoning: true,
|
|
1397
|
-
reasoningApi: "level",
|
|
1398
|
-
admittedReasoningEfforts: ["low", "medium", "high", "xhigh"],
|
|
1399
|
-
structuredOutput: true,
|
|
1400
|
-
nativeStructuredOutput: true,
|
|
1401
|
-
vision: true,
|
|
1402
|
-
audioInput: false,
|
|
1403
|
-
sampling: "tunable",
|
|
1404
|
-
caching: { explicit: false, minTokens: 0 },
|
|
1405
|
-
grounding: true,
|
|
1406
|
-
functionCalling: true,
|
|
1407
|
-
serviceTiers: ["priority"]
|
|
1408
|
-
},
|
|
1409
|
-
configSchema: Grok46ConfigSchema,
|
|
1410
|
-
configJsonSchema: toConfigJsonSchema(Grok46ConfigSchema),
|
|
1411
|
-
validateConfig: zodToStandardSchema(Grok46ConfigSchema)
|
|
1412
|
-
};
|
|
1413
|
-
var xaiModelDescriptors = [
|
|
1414
|
-
grok45ModelDescriptor,
|
|
1415
|
-
grok46ModelDescriptor
|
|
1416
|
-
];
|
|
1417
|
-
var xaiRegistry = createModelRegistry(xaiModelDescriptors);
|
|
1418
|
-
|
|
1419
|
-
// src/pricing.ts
|
|
1420
|
-
var xaiPricingVersion = "xai-2026-08-24";
|
|
1421
|
-
var XAI_TOOL_RATE_MICRO_USD = {
|
|
1422
|
-
web_search_calls: 5e3,
|
|
1423
|
-
x_search_calls: 5e3
|
|
1424
|
-
};
|
|
1425
|
-
var XAI_TOOL_COUNTER_KEYS = ["web_search_calls", "x_search_calls"];
|
|
1426
|
-
var XAI_PRICING = Object.freeze({
|
|
1427
|
-
// ── grok-4.5 ── $2.00/$6.00 (≤200k), $4.00/$12.00 (>200k); cached $0.30/$0.60
|
|
1428
|
-
"grok-4.5": {
|
|
1429
|
-
inputPerM: 2e6,
|
|
1430
|
-
cachedPerM: 3e5,
|
|
1431
|
-
outputPerM: 6e6,
|
|
1432
|
-
gt200k: {
|
|
1433
|
-
inputPerM: 4e6,
|
|
1434
|
-
cachedPerM: 6e5,
|
|
1435
|
-
outputPerM: 12e6
|
|
1517
|
+
throw new LlmError("xAI file upload aborted", {
|
|
1518
|
+
kind: "aborted",
|
|
1519
|
+
retryable: false,
|
|
1520
|
+
provider: "xai"
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
throw classifyStoreError(e);
|
|
1436
1524
|
}
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
|
|
1525
|
+
if (!res.ok) {
|
|
1526
|
+
try {
|
|
1527
|
+
await throwHttpFailure(res);
|
|
1528
|
+
} catch (e) {
|
|
1529
|
+
throw classifyStoreError(e);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
let json;
|
|
1533
|
+
try {
|
|
1534
|
+
json = await res.json();
|
|
1535
|
+
} catch (e) {
|
|
1536
|
+
throw new LlmError("xAI file upload returned non-JSON body", {
|
|
1537
|
+
kind: "server",
|
|
1538
|
+
retryable: false,
|
|
1539
|
+
provider: "xai",
|
|
1540
|
+
cause: e
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
return makeHandle(json);
|
|
1450
1544
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1545
|
+
async get(fileId, signal) {
|
|
1546
|
+
if (typeof fileId !== "string" || fileId.trim() === "") {
|
|
1547
|
+
throw badRequest("fileId must be a non-empty string.");
|
|
1548
|
+
}
|
|
1549
|
+
let res;
|
|
1550
|
+
try {
|
|
1551
|
+
res = await this.fetchImpl(
|
|
1552
|
+
this.filesUrl(fileId),
|
|
1553
|
+
this.requestInit("GET", signal !== void 0 ? { signal } : {})
|
|
1554
|
+
);
|
|
1555
|
+
} catch (e) {
|
|
1556
|
+
if (signal?.aborted === true) {
|
|
1557
|
+
throw new LlmError("xAI file get aborted", {
|
|
1558
|
+
kind: "aborted",
|
|
1559
|
+
retryable: false,
|
|
1560
|
+
provider: "xai"
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
throw classifyStoreError(e);
|
|
1564
|
+
}
|
|
1565
|
+
if (res.status === 404) {
|
|
1566
|
+
throw notFoundError(fileId, "get");
|
|
1567
|
+
}
|
|
1568
|
+
if (!res.ok) {
|
|
1569
|
+
try {
|
|
1570
|
+
await throwHttpFailure(res);
|
|
1571
|
+
} catch (e) {
|
|
1572
|
+
throw classifyStoreError(e);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
const json = await res.json();
|
|
1576
|
+
return makeHandle(json);
|
|
1459
1577
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1578
|
+
async list(opts = {}, signal) {
|
|
1579
|
+
if (opts.limit !== void 0) {
|
|
1580
|
+
if (typeof opts.limit !== "number" || !Number.isInteger(opts.limit) || opts.limit < 1 || opts.limit > 100) {
|
|
1581
|
+
throw badRequest("list.limit must be an integer in [1, 100].");
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
const params = new URLSearchParams();
|
|
1585
|
+
if (opts.limit !== void 0) params.set("limit", String(opts.limit));
|
|
1586
|
+
if (opts.order !== void 0) params.set("order", opts.order);
|
|
1587
|
+
if (opts.sortBy !== void 0) params.set("sort_by", opts.sortBy);
|
|
1588
|
+
if (opts.paginationToken !== void 0) {
|
|
1589
|
+
params.set("pagination_token", opts.paginationToken);
|
|
1590
|
+
}
|
|
1591
|
+
const qs = params.toString();
|
|
1592
|
+
const url = qs.length > 0 ? `${this.filesUrl()}?${qs}` : this.filesUrl();
|
|
1593
|
+
let res;
|
|
1594
|
+
try {
|
|
1595
|
+
res = await this.fetchImpl(
|
|
1596
|
+
url,
|
|
1597
|
+
this.requestInit("GET", signal !== void 0 ? { signal } : {})
|
|
1598
|
+
);
|
|
1599
|
+
} catch (e) {
|
|
1600
|
+
if (signal?.aborted === true) {
|
|
1601
|
+
throw new LlmError("xAI file list aborted", {
|
|
1602
|
+
kind: "aborted",
|
|
1603
|
+
retryable: false,
|
|
1604
|
+
provider: "xai"
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
throw classifyStoreError(e);
|
|
1608
|
+
}
|
|
1609
|
+
if (!res.ok) {
|
|
1610
|
+
try {
|
|
1611
|
+
await throwHttpFailure(res);
|
|
1612
|
+
} catch (e) {
|
|
1613
|
+
throw classifyStoreError(e);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
const json = await res.json();
|
|
1617
|
+
const files = Array.isArray(json.data) ? json.data.map((f) => makeHandle(f)) : [];
|
|
1618
|
+
const result = { files };
|
|
1619
|
+
if (typeof json.pagination_token === "string" && json.pagination_token.length > 0) {
|
|
1620
|
+
result.paginationToken = json.pagination_token;
|
|
1621
|
+
}
|
|
1622
|
+
return result;
|
|
1477
1623
|
}
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1624
|
+
/**
|
|
1625
|
+
* Delete a file. Idempotent: HTTP 404 → success.
|
|
1626
|
+
*
|
|
1627
|
+
* Default (`failClosed` omitted/false): non-404 errors go to `onDeleteError`
|
|
1628
|
+
* and resolve (P5 fail-open). With `failClosed: true`, non-404 errors throw
|
|
1629
|
+
* typed `LlmError` and `onDeleteError` is not called.
|
|
1630
|
+
*
|
|
1631
|
+
* Empty/blank ids always throw `bad_request` (caller fault).
|
|
1632
|
+
*/
|
|
1633
|
+
async delete(fileIdOrHandle, opts) {
|
|
1634
|
+
const fileId = resolveFileId(fileIdOrHandle);
|
|
1635
|
+
if (typeof fileId !== "string" || fileId.trim() === "") {
|
|
1636
|
+
throw badRequest("fileId must be a non-empty string.");
|
|
1637
|
+
}
|
|
1638
|
+
const failClosed = opts?.failClosed === true;
|
|
1639
|
+
const signal = opts?.signal;
|
|
1640
|
+
try {
|
|
1641
|
+
const res = await this.fetchImpl(
|
|
1642
|
+
this.filesUrl(fileId),
|
|
1643
|
+
this.requestInit("DELETE", signal !== void 0 ? { signal } : {})
|
|
1644
|
+
);
|
|
1645
|
+
if (res.status === 404) {
|
|
1646
|
+
return;
|
|
1647
|
+
}
|
|
1648
|
+
if (!res.ok) {
|
|
1649
|
+
await throwHttpFailure(res);
|
|
1650
|
+
}
|
|
1651
|
+
} catch (err) {
|
|
1652
|
+
if (isNotFoundError(err)) {
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1655
|
+
const classified = signal?.aborted === true && !(err instanceof LlmError) ? new LlmError("xAI file delete aborted", {
|
|
1656
|
+
kind: "aborted",
|
|
1657
|
+
retryable: false,
|
|
1658
|
+
provider: "xai",
|
|
1659
|
+
cause: err
|
|
1660
|
+
}) : classifyStoreError(err);
|
|
1661
|
+
if (failClosed) {
|
|
1662
|
+
throw classified;
|
|
1663
|
+
}
|
|
1664
|
+
this.onDeleteError(fileId, classified);
|
|
1491
1665
|
}
|
|
1492
1666
|
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
const count = usage.details[key];
|
|
1506
|
-
if (typeof count !== "number" || count <= 0) return sum;
|
|
1507
|
-
return sum + Math.round(count * XAI_TOOL_RATE_MICRO_USD[key]);
|
|
1508
|
-
}, 0);
|
|
1509
|
-
const microUsd = inputCost + cachedCost + outputCost + toolsCost;
|
|
1510
|
-
return {
|
|
1511
|
-
microUsd,
|
|
1512
|
-
usd: microUsd / 1e6,
|
|
1513
|
-
pricingVersion: xaiPricingVersion,
|
|
1514
|
-
confidence: missingRequestedCounters || attachmentUnpinned ? "estimated" : "exact",
|
|
1515
|
-
details: {
|
|
1516
|
-
input: inputCost,
|
|
1517
|
-
cached: cachedCost,
|
|
1518
|
-
output: outputCost,
|
|
1519
|
-
tools: toolsCost
|
|
1667
|
+
/**
|
|
1668
|
+
* Delete many files.
|
|
1669
|
+
*
|
|
1670
|
+
* Fail-open (default): `Promise.allSettled` — each failure → `onDeleteError`.
|
|
1671
|
+
* Fail-closed: `Promise.all` — first throw rejects; in-flight siblings are
|
|
1672
|
+
* not cancelled (partial deletes may already have succeeded at the provider).
|
|
1673
|
+
* Prefer per-id delete + host DB mark when gating durable release state.
|
|
1674
|
+
*/
|
|
1675
|
+
async deleteAll(ids, opts) {
|
|
1676
|
+
if (opts?.failClosed === true) {
|
|
1677
|
+
await Promise.all(ids.map((id) => this.delete(id, opts)));
|
|
1678
|
+
return;
|
|
1520
1679
|
}
|
|
1521
|
-
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
return computeXaiCost(model, usage, tier);
|
|
1528
|
-
},
|
|
1529
|
-
hasModel(model) {
|
|
1530
|
-
return lookupRates(model) !== void 0;
|
|
1531
|
-
},
|
|
1532
|
-
listModels() {
|
|
1533
|
-
return Object.keys(XAI_PRICING);
|
|
1680
|
+
await Promise.allSettled(ids.map((id) => this.delete(id, opts)));
|
|
1681
|
+
}
|
|
1682
|
+
/** Download raw file bytes. */
|
|
1683
|
+
async getContent(fileId, signal) {
|
|
1684
|
+
if (typeof fileId !== "string" || fileId.trim() === "") {
|
|
1685
|
+
throw badRequest("fileId must be a non-empty string.");
|
|
1534
1686
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1687
|
+
let res;
|
|
1688
|
+
try {
|
|
1689
|
+
res = await this.fetchImpl(
|
|
1690
|
+
this.filesUrl(`${fileId}/content`),
|
|
1691
|
+
this.requestInit("GET", signal !== void 0 ? { signal } : {})
|
|
1692
|
+
);
|
|
1693
|
+
} catch (e) {
|
|
1694
|
+
if (signal?.aborted === true) {
|
|
1695
|
+
throw new LlmError("xAI file content download aborted", {
|
|
1696
|
+
kind: "aborted",
|
|
1697
|
+
retryable: false,
|
|
1698
|
+
provider: "xai"
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
throw classifyStoreError(e);
|
|
1702
|
+
}
|
|
1703
|
+
if (res.status === 404) {
|
|
1704
|
+
throw notFoundError(fileId, "getContent");
|
|
1705
|
+
}
|
|
1706
|
+
if (!res.ok) {
|
|
1707
|
+
try {
|
|
1708
|
+
await throwHttpFailure(res);
|
|
1709
|
+
} catch (e) {
|
|
1710
|
+
throw classifyStoreError(e);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
const buf = await res.arrayBuffer();
|
|
1714
|
+
return new Uint8Array(buf);
|
|
1715
|
+
}
|
|
1716
|
+
};
|
|
1537
1717
|
|
|
1538
1718
|
// src/provider.ts
|
|
1539
1719
|
function xaiProvider(opts) {
|
|
@@ -1544,6 +1724,6 @@ function xaiProvider(opts) {
|
|
|
1544
1724
|
};
|
|
1545
1725
|
}
|
|
1546
1726
|
|
|
1547
|
-
export { Grok45ConfigSchema, Grok46ConfigSchema, XAI_FILES_DEFAULT_BASE_URL, XAI_FILE_MAX_BYTES, XAI_FILE_TTL_MAX_SECONDS, XAI_FILE_TTL_MIN_SECONDS, XAI_PRICING, XAI_TOOL_RATE_MICRO_USD, XaiFileStore, buildXaiClient, classifyXaiError, computeXaiCost, grok45ModelDescriptor, grok46ModelDescriptor, requireApiKey, xaiAdapter, xaiModelDescriptors, xaiPricingSource, xaiPricingVersion, xaiProvider, xaiRegistry };
|
|
1727
|
+
export { Grok45ConfigSchema, Grok46ConfigSchema, Grok47ConfigSchema, XAI_FILES_DEFAULT_BASE_URL, XAI_FILE_MAX_BYTES, XAI_FILE_TTL_MAX_SECONDS, XAI_FILE_TTL_MIN_SECONDS, XAI_PRICING, XAI_TOOL_RATE_MICRO_USD, XaiFileStore, buildXaiClient, classifyXaiError, computeXaiCost, grok45ModelDescriptor, grok46ModelDescriptor, grok47ModelDescriptor, requireApiKey, xaiAdapter, xaiModelDescriptors, xaiPricingSource, xaiPricingVersion, xaiProvider, xaiRegistry };
|
|
1548
1728
|
//# sourceMappingURL=index.js.map
|
|
1549
1729
|
//# sourceMappingURL=index.js.map
|