@gullabs/xai 0.5.0 → 0.6.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 +12 -5
- package/dist/index.cjs +479 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +310 -29
- package/dist/index.d.ts +310 -29
- package/dist/index.js +480 -41
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -33,6 +33,12 @@ async function buildXaiClient(auth) {
|
|
|
33
33
|
function isPlainRecord(value) {
|
|
34
34
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
35
35
|
}
|
|
36
|
+
function isXaiMessageItem(item) {
|
|
37
|
+
return item.type === "message" && Array.isArray(item.content);
|
|
38
|
+
}
|
|
39
|
+
function isXaiReasoningItem(item) {
|
|
40
|
+
return item.type === "reasoning" && Array.isArray(item.summary);
|
|
41
|
+
}
|
|
36
42
|
function badXaiRequest(message) {
|
|
37
43
|
return new core.LlmError(message, { kind: "bad_request", retryable: false });
|
|
38
44
|
}
|
|
@@ -77,10 +83,16 @@ function mapPart(p) {
|
|
|
77
83
|
}
|
|
78
84
|
return { type: "input_file", file_id: p.fileId };
|
|
79
85
|
}
|
|
86
|
+
case "tool-call":
|
|
87
|
+
case "tool-result":
|
|
88
|
+
throw badXaiRequest(
|
|
89
|
+
`xAI mapPart does not emit ${p.kind} as a content part; the request mapper handles replay items.`
|
|
90
|
+
);
|
|
80
91
|
default:
|
|
81
92
|
return core.assertNever(p);
|
|
82
93
|
}
|
|
83
94
|
}
|
|
95
|
+
var XAI_PROVIDER_OPTION_KEYS = /* @__PURE__ */ new Set(["promptCacheKey", "tools", "parallelToolCalls"]);
|
|
84
96
|
function mapXaiProviderOptions(xaiOpts, model) {
|
|
85
97
|
if (xaiOpts === void 0) {
|
|
86
98
|
return {};
|
|
@@ -88,12 +100,14 @@ function mapXaiProviderOptions(xaiOpts, model) {
|
|
|
88
100
|
if (!isPlainRecord(xaiOpts)) {
|
|
89
101
|
throw badXaiRequest(`providerOptions.xai must be an object for model "${model}".`);
|
|
90
102
|
}
|
|
91
|
-
const unknownKeys = Object.keys(xaiOpts).filter(
|
|
103
|
+
const unknownKeys = Object.keys(xaiOpts).filter(
|
|
104
|
+
(key) => !XAI_PROVIDER_OPTION_KEYS.has(key)
|
|
105
|
+
);
|
|
92
106
|
if (unknownKeys.length > 0) {
|
|
93
107
|
throw badXaiRequest(
|
|
94
108
|
`providerOptions.xai contains unsupported keys [${unknownKeys.join(
|
|
95
109
|
", "
|
|
96
|
-
)}] for model "${model}". Allowed keys: promptCacheKey.`
|
|
110
|
+
)}] for model "${model}". Allowed keys: promptCacheKey, tools, parallelToolCalls.`
|
|
97
111
|
);
|
|
98
112
|
}
|
|
99
113
|
const mapped = {};
|
|
@@ -105,8 +119,69 @@ function mapXaiProviderOptions(xaiOpts, model) {
|
|
|
105
119
|
}
|
|
106
120
|
mapped.promptCacheKey = xaiOpts["promptCacheKey"];
|
|
107
121
|
}
|
|
122
|
+
if (xaiOpts["tools"] !== void 0) {
|
|
123
|
+
mapped.tools = mapXaiSearchTools(xaiOpts["tools"], model);
|
|
124
|
+
}
|
|
125
|
+
if (xaiOpts["parallelToolCalls"] !== void 0) {
|
|
126
|
+
if (typeof xaiOpts["parallelToolCalls"] !== "boolean") {
|
|
127
|
+
throw badXaiRequest(
|
|
128
|
+
`providerOptions.xai.parallelToolCalls must be a boolean for model "${model}".`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
mapped.parallelToolCalls = xaiOpts["parallelToolCalls"];
|
|
132
|
+
}
|
|
108
133
|
return mapped;
|
|
109
134
|
}
|
|
135
|
+
function mapXaiSearchTools(tools, model) {
|
|
136
|
+
if (!Array.isArray(tools)) {
|
|
137
|
+
throw badXaiRequest(
|
|
138
|
+
`providerOptions.xai.tools must be an array for model "${model}".`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
return tools.map((tool, index) => {
|
|
142
|
+
if (!isPlainRecord(tool) || typeof tool["type"] !== "string") {
|
|
143
|
+
throw badXaiRequest(
|
|
144
|
+
`providerOptions.xai.tools[${index}] must be an object with a type for model "${model}".`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
if (tool["type"] === "web_search") {
|
|
148
|
+
const wire = { type: "web_search" };
|
|
149
|
+
if (tool["allowedDomains"] !== void 0)
|
|
150
|
+
wire["allowed_domains"] = tool["allowedDomains"];
|
|
151
|
+
if (tool["excludedDomains"] !== void 0) {
|
|
152
|
+
wire["excluded_domains"] = tool["excludedDomains"];
|
|
153
|
+
}
|
|
154
|
+
if (tool["enableImageUnderstanding"] !== void 0) {
|
|
155
|
+
wire["enable_image_understanding"] = tool["enableImageUnderstanding"];
|
|
156
|
+
}
|
|
157
|
+
if (tool["enableImageSearch"] !== void 0) {
|
|
158
|
+
wire["enable_image_search"] = tool["enableImageSearch"];
|
|
159
|
+
}
|
|
160
|
+
return wire;
|
|
161
|
+
}
|
|
162
|
+
if (tool["type"] === "x_search") {
|
|
163
|
+
const wire = { type: "x_search" };
|
|
164
|
+
if (tool["allowedXHandles"] !== void 0) {
|
|
165
|
+
wire["allowed_x_handles"] = tool["allowedXHandles"];
|
|
166
|
+
}
|
|
167
|
+
if (tool["excludedXHandles"] !== void 0) {
|
|
168
|
+
wire["excluded_x_handles"] = tool["excludedXHandles"];
|
|
169
|
+
}
|
|
170
|
+
if (tool["fromDate"] !== void 0) wire["from_date"] = tool["fromDate"];
|
|
171
|
+
if (tool["toDate"] !== void 0) wire["to_date"] = tool["toDate"];
|
|
172
|
+
if (tool["enableImageUnderstanding"] !== void 0) {
|
|
173
|
+
wire["enable_image_understanding"] = tool["enableImageUnderstanding"];
|
|
174
|
+
}
|
|
175
|
+
if (tool["enableVideoUnderstanding"] !== void 0) {
|
|
176
|
+
wire["enable_video_understanding"] = tool["enableVideoUnderstanding"];
|
|
177
|
+
}
|
|
178
|
+
return wire;
|
|
179
|
+
}
|
|
180
|
+
throw badXaiRequest(
|
|
181
|
+
`providerOptions.xai.tools[${index}].type "${String(tool["type"])}" is not supported for model "${model}".`
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
110
185
|
function mapFinishReason(response) {
|
|
111
186
|
if (response.status === "completed") {
|
|
112
187
|
return "stop";
|
|
@@ -138,6 +213,14 @@ function mapUsage(usage) {
|
|
|
138
213
|
details[key] = value;
|
|
139
214
|
}
|
|
140
215
|
}
|
|
216
|
+
const toolUsage = usage["server_side_tool_usage_details"];
|
|
217
|
+
if (isPlainRecord(toolUsage)) {
|
|
218
|
+
for (const [key, value] of Object.entries(toolUsage)) {
|
|
219
|
+
if (typeof value === "number") {
|
|
220
|
+
details[key] = value;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
141
224
|
const raw = usage;
|
|
142
225
|
const result = {
|
|
143
226
|
inputTokens,
|
|
@@ -173,6 +256,11 @@ function isXaiAuthFailureBody(rawErr) {
|
|
|
173
256
|
const text = extractXaiErrorBodyText(rawErr);
|
|
174
257
|
return text !== void 0 && text.startsWith(XAI_AUTH_ERROR_MESSAGE_PREFIX);
|
|
175
258
|
}
|
|
259
|
+
var XAI_SAFETY_CHECK_MESSAGE_PREFIX = "Content violates usage guidelines";
|
|
260
|
+
function isXaiSafetyCheckBody(rawErr) {
|
|
261
|
+
const text = extractXaiErrorBodyText(rawErr);
|
|
262
|
+
return text !== void 0 && text.startsWith(XAI_SAFETY_CHECK_MESSAGE_PREFIX);
|
|
263
|
+
}
|
|
176
264
|
var XAI_TRANSPORT_ERROR_PATTERN = /connection error|econnreset|econnrefused|etimedout|eai_again|epipe|socket hang up|fetch failed/i;
|
|
177
265
|
function matchesXaiTransportSignature(err) {
|
|
178
266
|
if (!(err instanceof Error)) return false;
|
|
@@ -211,6 +299,16 @@ function classifyXaiError(rawErr) {
|
|
|
211
299
|
cause: base.cause ?? rawErr
|
|
212
300
|
});
|
|
213
301
|
}
|
|
302
|
+
if (base.httpStatus === 403 && isXaiSafetyCheckBody(rawErr)) {
|
|
303
|
+
const bodyText = extractXaiErrorBodyText(rawErr);
|
|
304
|
+
return new core.LlmError(bodyText ?? base.message, {
|
|
305
|
+
kind: "content_filter",
|
|
306
|
+
retryable: false,
|
|
307
|
+
httpStatus: base.httpStatus,
|
|
308
|
+
provider: "xai",
|
|
309
|
+
cause: base.cause ?? rawErr
|
|
310
|
+
});
|
|
311
|
+
}
|
|
214
312
|
if (base.kind === "unknown" && isXaiTransportError(rawErr)) {
|
|
215
313
|
return new core.LlmError(base.message, {
|
|
216
314
|
kind: "server",
|
|
@@ -241,10 +339,48 @@ function xaiAdapter(opts) {
|
|
|
241
339
|
const warnings = [];
|
|
242
340
|
const model = req.model;
|
|
243
341
|
const genConfig = req.config;
|
|
244
|
-
const input =
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
342
|
+
const input = [];
|
|
343
|
+
for (const msg of req.messages) {
|
|
344
|
+
const contentParts = [];
|
|
345
|
+
for (const part of msg.parts) {
|
|
346
|
+
if (part.kind === "tool-call") {
|
|
347
|
+
if (contentParts.length > 0) {
|
|
348
|
+
input.push({
|
|
349
|
+
role: msg.role === "assistant" ? "assistant" : "user",
|
|
350
|
+
content: contentParts.splice(0)
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
input.push({
|
|
354
|
+
type: "function_call",
|
|
355
|
+
call_id: part.toolCallId,
|
|
356
|
+
name: part.toolName,
|
|
357
|
+
arguments: JSON.stringify(part.args)
|
|
358
|
+
});
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
if (part.kind === "tool-result") {
|
|
362
|
+
if (contentParts.length > 0) {
|
|
363
|
+
input.push({
|
|
364
|
+
role: msg.role === "assistant" ? "assistant" : "user",
|
|
365
|
+
content: contentParts.splice(0)
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
input.push({
|
|
369
|
+
type: "function_call_output",
|
|
370
|
+
call_id: part.toolCallId,
|
|
371
|
+
output: JSON.stringify(part.result)
|
|
372
|
+
});
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
contentParts.push(mapPart(part));
|
|
376
|
+
}
|
|
377
|
+
if (contentParts.length > 0) {
|
|
378
|
+
input.push({
|
|
379
|
+
role: msg.role === "assistant" ? "assistant" : "user",
|
|
380
|
+
content: contentParts
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
248
384
|
const params = {
|
|
249
385
|
model,
|
|
250
386
|
input,
|
|
@@ -314,6 +450,38 @@ function xaiAdapter(opts) {
|
|
|
314
450
|
if (xaiProviderConfig.promptCacheKey !== void 0) {
|
|
315
451
|
params.prompt_cache_key = xaiProviderConfig.promptCacheKey;
|
|
316
452
|
}
|
|
453
|
+
const hasFileRef = req.messages.some(
|
|
454
|
+
(msg) => msg.parts.some((part) => part.kind === "file-ref")
|
|
455
|
+
);
|
|
456
|
+
const searchTools = xaiProviderConfig.tools;
|
|
457
|
+
if (searchTools !== void 0) {
|
|
458
|
+
if (req.modelDescriptor?.capabilities?.grounding !== true) {
|
|
459
|
+
throw badXaiRequest(
|
|
460
|
+
`providerOptions.xai.tools requires capabilities.grounding on the model descriptor for "${model}".`
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
params.tools = searchTools;
|
|
464
|
+
}
|
|
465
|
+
if (req.tools !== void 0 && req.tools.length > 0) {
|
|
466
|
+
if (req.modelDescriptor?.capabilities?.functionCalling !== true) {
|
|
467
|
+
throw badXaiRequest(
|
|
468
|
+
`tools is not supported for xai model "${model}" (capabilities.functionCalling is not true).`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
const functionTools = req.tools.map((tool) => ({
|
|
472
|
+
type: "function",
|
|
473
|
+
name: tool.name,
|
|
474
|
+
description: tool.description,
|
|
475
|
+
parameters: tool.inputJsonSchema
|
|
476
|
+
}));
|
|
477
|
+
params.tools = [...params.tools ?? [], ...functionTools];
|
|
478
|
+
if (req.toolChoice !== void 0) {
|
|
479
|
+
params.tool_choice = typeof req.toolChoice === "string" ? req.toolChoice : { type: "function", name: req.toolChoice.name };
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
if (xaiProviderConfig.parallelToolCalls !== void 0) {
|
|
483
|
+
params.parallel_tool_calls = xaiProviderConfig.parallelToolCalls;
|
|
484
|
+
}
|
|
317
485
|
let response;
|
|
318
486
|
try {
|
|
319
487
|
const buildClient = opts?._clientFactory ?? buildXaiClient;
|
|
@@ -332,14 +500,29 @@ function xaiAdapter(opts) {
|
|
|
332
500
|
let text = "";
|
|
333
501
|
let reasoningText;
|
|
334
502
|
const messageItems = [];
|
|
503
|
+
const toolCalls = [];
|
|
335
504
|
for (const item of response.output) {
|
|
336
|
-
if (item
|
|
505
|
+
if (isXaiMessageItem(item)) {
|
|
337
506
|
messageItems.push(item);
|
|
338
|
-
} else {
|
|
507
|
+
} else if (isXaiReasoningItem(item)) {
|
|
339
508
|
const joined = item.summary.map((s) => s.text).join("");
|
|
340
509
|
if (joined.length > 0) {
|
|
341
510
|
reasoningText = (reasoningText ?? "") + joined;
|
|
342
511
|
}
|
|
512
|
+
} else if (item.type === "function_call") {
|
|
513
|
+
const callId = typeof item["call_id"] === "string" ? item["call_id"] : "";
|
|
514
|
+
const name = typeof item["name"] === "string" ? item["name"] : "";
|
|
515
|
+
let args = {};
|
|
516
|
+
if (typeof item["arguments"] === "string") {
|
|
517
|
+
try {
|
|
518
|
+
args = JSON.parse(item["arguments"]);
|
|
519
|
+
} catch {
|
|
520
|
+
args = item["arguments"];
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
if (callId.length > 0 && name.length > 0) {
|
|
524
|
+
toolCalls.push({ toolCallId: callId, toolName: name, args });
|
|
525
|
+
}
|
|
343
526
|
}
|
|
344
527
|
}
|
|
345
528
|
if (messageItems.length > 0) {
|
|
@@ -361,6 +544,22 @@ function xaiAdapter(opts) {
|
|
|
361
544
|
}
|
|
362
545
|
const usage = mapUsage(response.usage);
|
|
363
546
|
const finishReason = mapFinishReason(response);
|
|
547
|
+
const expectedToolCounters = expectedServerToolCounters(
|
|
548
|
+
xaiProviderConfig.tools);
|
|
549
|
+
if (expectedToolCounters.length > 0 || hasFileRef) {
|
|
550
|
+
usage.details["server_tools_requested"] = 1;
|
|
551
|
+
const missing = expectedToolCounters.filter((key) => !(key in usage.details));
|
|
552
|
+
if (missing.length > 0) {
|
|
553
|
+
usage.details["server_tools_missing"] = 1;
|
|
554
|
+
warnings.push({
|
|
555
|
+
type: "other",
|
|
556
|
+
message: `xai: server tools were requested but usage is missing counters [${missing.join(
|
|
557
|
+
", "
|
|
558
|
+
)}]; tool cost will be estimated.`
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
const citations = collectXaiCitations(response, messageItems);
|
|
364
563
|
const providerMeta = {};
|
|
365
564
|
const contextDetails = response.usage["context_details"];
|
|
366
565
|
if (isPlainRecord(contextDetails)) {
|
|
@@ -380,12 +579,159 @@ function xaiAdapter(opts) {
|
|
|
380
579
|
...reasoningText !== void 0 ? { reasoningText } : {},
|
|
381
580
|
...rawStructured !== void 0 ? { rawStructured } : {},
|
|
382
581
|
...servedServiceTier !== void 0 ? { servedServiceTier } : {},
|
|
383
|
-
...Object.keys(providerMeta).length > 0 ? { providerMetadata: providerMeta } : {}
|
|
582
|
+
...Object.keys(providerMeta).length > 0 ? { providerMetadata: providerMeta } : {},
|
|
583
|
+
...citations.length > 0 ? { citations } : {},
|
|
584
|
+
...toolCalls.length > 0 ? { toolCalls, finishReason: "tool_calls" } : {}
|
|
384
585
|
};
|
|
385
586
|
return result;
|
|
587
|
+
},
|
|
588
|
+
async countTokens(req, ctx) {
|
|
589
|
+
if (req.provider !== "xai") {
|
|
590
|
+
throw new core.LlmError(
|
|
591
|
+
`xaiAdapter received a request for provider "${req.provider}", expected "xai".`,
|
|
592
|
+
{ kind: "bad_request", retryable: false }
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
if (req.tools !== void 0 && req.tools.length > 0) {
|
|
596
|
+
throw badXaiRequest(
|
|
597
|
+
"xAI countTokens rejects tools; tokenize-text cannot represent tool declarations."
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
const text = concatenateTokenizeText(req);
|
|
601
|
+
const apiKey = requireApiKey(ctx.auth);
|
|
602
|
+
const fetchImpl = opts?._fetch ?? fetch;
|
|
603
|
+
try {
|
|
604
|
+
const res = await fetchImpl("https://api.x.ai/v1/tokenize-text", {
|
|
605
|
+
method: "POST",
|
|
606
|
+
headers: {
|
|
607
|
+
Authorization: `Bearer ${apiKey}`,
|
|
608
|
+
"Content-Type": "application/json"
|
|
609
|
+
},
|
|
610
|
+
body: JSON.stringify({ model: req.model, text }),
|
|
611
|
+
...ctx.signal !== void 0 ? { signal: ctx.signal } : {}
|
|
612
|
+
});
|
|
613
|
+
if (!res.ok) {
|
|
614
|
+
let parsed;
|
|
615
|
+
try {
|
|
616
|
+
parsed = await res.json();
|
|
617
|
+
} catch {
|
|
618
|
+
parsed = await res.text().catch(() => "");
|
|
619
|
+
}
|
|
620
|
+
throw Object.assign(new Error(`xAI tokenize-text HTTP ${res.status}`), {
|
|
621
|
+
status: res.status,
|
|
622
|
+
error: parsed
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
const raw = await res.json();
|
|
626
|
+
if (!isPlainRecord(raw) || !Array.isArray(raw["token_ids"])) {
|
|
627
|
+
throw new core.LlmError(
|
|
628
|
+
"xAI tokenize-text response is malformed: missing required field: token_ids",
|
|
629
|
+
{ kind: "server", retryable: true, provider: "xai" }
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
const n = raw["token_ids"].length;
|
|
633
|
+
return {
|
|
634
|
+
totalTokens: n,
|
|
635
|
+
accuracy: "lower-bound",
|
|
636
|
+
details: { textTokens: n },
|
|
637
|
+
raw
|
|
638
|
+
};
|
|
639
|
+
} catch (rawErr) {
|
|
640
|
+
if (rawErr instanceof Error && rawErr.name === "AbortError") {
|
|
641
|
+
throw new core.LlmError("xAI tokenize-text aborted", {
|
|
642
|
+
kind: "aborted",
|
|
643
|
+
retryable: false,
|
|
644
|
+
provider: "xai",
|
|
645
|
+
cause: rawErr
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
throw classifyXaiError(rawErr);
|
|
649
|
+
}
|
|
386
650
|
}
|
|
387
651
|
};
|
|
388
652
|
}
|
|
653
|
+
var WEB_SEARCH_COUNTER = "web_search_calls";
|
|
654
|
+
var X_SEARCH_COUNTER = "x_search_calls";
|
|
655
|
+
function expectedServerToolCounters(tools, _hasFileRef) {
|
|
656
|
+
const keys = [];
|
|
657
|
+
if (tools !== void 0) {
|
|
658
|
+
for (const tool of tools) {
|
|
659
|
+
if (tool["type"] === "web_search") keys.push(WEB_SEARCH_COUNTER);
|
|
660
|
+
if (tool["type"] === "x_search") keys.push(X_SEARCH_COUNTER);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return keys;
|
|
664
|
+
}
|
|
665
|
+
function collectXaiCitations(response, messageItems) {
|
|
666
|
+
const seen = /* @__PURE__ */ new Set();
|
|
667
|
+
const citations = [];
|
|
668
|
+
const push = (url, title) => {
|
|
669
|
+
if (typeof url !== "string" || url.length === 0) return;
|
|
670
|
+
if (seen.has(url)) return;
|
|
671
|
+
seen.add(url);
|
|
672
|
+
const citation = { url };
|
|
673
|
+
if (typeof title === "string" && title.length > 0 && title !== url) {
|
|
674
|
+
citation.title = title;
|
|
675
|
+
}
|
|
676
|
+
try {
|
|
677
|
+
const parsed = new URL(url);
|
|
678
|
+
if (parsed.hostname.length > 0) {
|
|
679
|
+
citation.sourceName = parsed.hostname.startsWith("www.") ? parsed.hostname.slice(4) : parsed.hostname;
|
|
680
|
+
}
|
|
681
|
+
} catch {
|
|
682
|
+
}
|
|
683
|
+
citations.push(citation);
|
|
684
|
+
};
|
|
685
|
+
if (Array.isArray(response.citations)) {
|
|
686
|
+
for (const item of response.citations) {
|
|
687
|
+
if (typeof item === "string") {
|
|
688
|
+
push(item, void 0);
|
|
689
|
+
} else if (isPlainRecord(item)) {
|
|
690
|
+
push(item["url"] ?? item["uri"], item["title"]);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
const lastMessage = messageItems.at(-1);
|
|
695
|
+
const citationMessages = lastMessage === void 0 ? [] : [lastMessage];
|
|
696
|
+
for (const item of citationMessages) {
|
|
697
|
+
for (const part of item.content) {
|
|
698
|
+
const annotations = part.annotations;
|
|
699
|
+
if (!Array.isArray(annotations)) continue;
|
|
700
|
+
for (const ann of annotations) {
|
|
701
|
+
if (!isPlainRecord(ann)) continue;
|
|
702
|
+
if (ann["type"] !== void 0 && ann["type"] !== "url_citation") continue;
|
|
703
|
+
push(ann["url"], ann["title"]);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return citations;
|
|
708
|
+
}
|
|
709
|
+
function concatenateTokenizeText(req) {
|
|
710
|
+
const chunks = [];
|
|
711
|
+
if (req.system !== void 0 && req.system.length > 0) {
|
|
712
|
+
chunks.push(req.system);
|
|
713
|
+
}
|
|
714
|
+
for (const message of req.messages) {
|
|
715
|
+
for (const part of message.parts) {
|
|
716
|
+
switch (part.kind) {
|
|
717
|
+
case "text":
|
|
718
|
+
chunks.push(part.text);
|
|
719
|
+
break;
|
|
720
|
+
case "inline-media":
|
|
721
|
+
case "file-uri":
|
|
722
|
+
case "file-ref":
|
|
723
|
+
case "tool-call":
|
|
724
|
+
case "tool-result":
|
|
725
|
+
throw badXaiRequest(
|
|
726
|
+
`xAI countTokens rejects ${part.kind} parts; tokenize-text is text-only.`
|
|
727
|
+
);
|
|
728
|
+
default:
|
|
729
|
+
return core.assertNever(part);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return chunks.join("\n");
|
|
734
|
+
}
|
|
389
735
|
var XAI_FILE_TTL_MIN_SECONDS = 3600;
|
|
390
736
|
var XAI_FILE_TTL_MAX_SECONDS = 2592e3;
|
|
391
737
|
var XAI_FILE_MAX_BYTES = 48 * 1024 * 1024;
|
|
@@ -841,6 +1187,100 @@ var XaiFileStore = class {
|
|
|
841
1187
|
return new Uint8Array(buf);
|
|
842
1188
|
}
|
|
843
1189
|
};
|
|
1190
|
+
var webSearchFlags = {
|
|
1191
|
+
enableImageUnderstanding: zod.z.boolean().optional().meta({
|
|
1192
|
+
title: "Enable Image Understanding",
|
|
1193
|
+
description: "Ask xAI to analyze images found during web search."
|
|
1194
|
+
}),
|
|
1195
|
+
enableImageSearch: zod.z.boolean().optional().meta({
|
|
1196
|
+
title: "Enable Image Search",
|
|
1197
|
+
description: "Ask xAI to include image search results."
|
|
1198
|
+
})
|
|
1199
|
+
};
|
|
1200
|
+
var XaiWebSearchToolSchema = zod.z.union([
|
|
1201
|
+
zod.z.strictObject({
|
|
1202
|
+
type: zod.z.literal("web_search"),
|
|
1203
|
+
allowedDomains: zod.z.array(zod.z.string()).max(5),
|
|
1204
|
+
...webSearchFlags
|
|
1205
|
+
}),
|
|
1206
|
+
zod.z.strictObject({
|
|
1207
|
+
type: zod.z.literal("web_search"),
|
|
1208
|
+
excludedDomains: zod.z.array(zod.z.string()).max(5),
|
|
1209
|
+
...webSearchFlags
|
|
1210
|
+
}),
|
|
1211
|
+
zod.z.strictObject({
|
|
1212
|
+
type: zod.z.literal("web_search"),
|
|
1213
|
+
...webSearchFlags
|
|
1214
|
+
})
|
|
1215
|
+
]).meta({
|
|
1216
|
+
title: "XaiWebSearchTool",
|
|
1217
|
+
description: "xAI web_search server tool. allowedDomains and excludedDomains are mutually exclusive (max 5)."
|
|
1218
|
+
});
|
|
1219
|
+
var xSearchFlags = {
|
|
1220
|
+
fromDate: zod.z.iso.date().optional().meta({
|
|
1221
|
+
title: "From Date",
|
|
1222
|
+
description: "Inclusive ISO-8601 date lower bound for X search."
|
|
1223
|
+
}),
|
|
1224
|
+
toDate: zod.z.iso.date().optional().meta({
|
|
1225
|
+
title: "To Date",
|
|
1226
|
+
description: "Inclusive ISO-8601 date upper bound for X search."
|
|
1227
|
+
}),
|
|
1228
|
+
enableImageUnderstanding: zod.z.boolean().optional().meta({
|
|
1229
|
+
title: "Enable Image Understanding",
|
|
1230
|
+
description: "Ask xAI to analyze images in matched posts."
|
|
1231
|
+
}),
|
|
1232
|
+
enableVideoUnderstanding: zod.z.boolean().optional().meta({
|
|
1233
|
+
title: "Enable Video Understanding",
|
|
1234
|
+
description: "Ask xAI to analyze videos in matched posts."
|
|
1235
|
+
})
|
|
1236
|
+
};
|
|
1237
|
+
var XaiXSearchToolSchema = zod.z.union([
|
|
1238
|
+
zod.z.strictObject({
|
|
1239
|
+
type: zod.z.literal("x_search"),
|
|
1240
|
+
allowedXHandles: zod.z.array(zod.z.string()).max(20),
|
|
1241
|
+
...xSearchFlags
|
|
1242
|
+
}),
|
|
1243
|
+
zod.z.strictObject({
|
|
1244
|
+
type: zod.z.literal("x_search"),
|
|
1245
|
+
excludedXHandles: zod.z.array(zod.z.string()).max(20),
|
|
1246
|
+
...xSearchFlags
|
|
1247
|
+
}),
|
|
1248
|
+
zod.z.strictObject({
|
|
1249
|
+
type: zod.z.literal("x_search"),
|
|
1250
|
+
...xSearchFlags
|
|
1251
|
+
})
|
|
1252
|
+
]).meta({
|
|
1253
|
+
title: "XaiXSearchTool",
|
|
1254
|
+
description: "xAI x_search server tool. allowedXHandles and excludedXHandles are mutually exclusive (max 20)."
|
|
1255
|
+
});
|
|
1256
|
+
var XaiToolsSchema = zod.z.union([
|
|
1257
|
+
zod.z.tuple([XaiWebSearchToolSchema]),
|
|
1258
|
+
zod.z.tuple([XaiXSearchToolSchema]),
|
|
1259
|
+
zod.z.tuple([XaiWebSearchToolSchema, XaiXSearchToolSchema]),
|
|
1260
|
+
zod.z.tuple([XaiXSearchToolSchema, XaiWebSearchToolSchema])
|
|
1261
|
+
]).meta({
|
|
1262
|
+
title: "XaiTools",
|
|
1263
|
+
description: "xAI Live Search tools. At most one web_search and at most one x_search."
|
|
1264
|
+
});
|
|
1265
|
+
var XaiProviderOptionsSchema = zod.z.strictObject({
|
|
1266
|
+
promptCacheKey: zod.z.string().min(1).optional().meta({
|
|
1267
|
+
title: "Prompt Cache Key",
|
|
1268
|
+
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
1269
|
+
}),
|
|
1270
|
+
tools: XaiToolsSchema.optional().meta({
|
|
1271
|
+
title: "Live Search Tools",
|
|
1272
|
+
description: "xAI server-side web_search / x_search tools."
|
|
1273
|
+
}),
|
|
1274
|
+
parallelToolCalls: zod.z.boolean().optional().meta({
|
|
1275
|
+
title: "Parallel Tool Calls",
|
|
1276
|
+
description: "xAI Responses parallel_tool_calls. Not a generic contract field."
|
|
1277
|
+
})
|
|
1278
|
+
}).meta({
|
|
1279
|
+
title: "xAI Provider Options",
|
|
1280
|
+
description: "Allowlisted xAI provider options."
|
|
1281
|
+
});
|
|
1282
|
+
|
|
1283
|
+
// src/model-config/grok-4-5.ts
|
|
844
1284
|
var Grok45ConfigSchema = zod.z.strictObject({
|
|
845
1285
|
temperature: zod.z.number().optional().meta({
|
|
846
1286
|
title: "Temperature",
|
|
@@ -855,9 +1295,9 @@ var Grok45ConfigSchema = zod.z.strictObject({
|
|
|
855
1295
|
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."
|
|
856
1296
|
}),
|
|
857
1297
|
reasoning: zod.z.strictObject({
|
|
858
|
-
effort: zod.z.enum(["low", "high"]).meta({
|
|
1298
|
+
effort: zod.z.enum(["low", "medium", "high"]).meta({
|
|
859
1299
|
title: "Reasoning Effort",
|
|
860
|
-
description: 'Reasoning effort for grok-4.5.
|
|
1300
|
+
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".'
|
|
861
1301
|
})
|
|
862
1302
|
}).optional().meta({
|
|
863
1303
|
title: "Reasoning",
|
|
@@ -868,22 +1308,14 @@ var Grok45ConfigSchema = zod.z.strictObject({
|
|
|
868
1308
|
description: "Logical request timeout in milliseconds."
|
|
869
1309
|
}),
|
|
870
1310
|
providerOptions: zod.z.strictObject({
|
|
871
|
-
xai:
|
|
872
|
-
promptCacheKey: zod.z.string().min(1).optional().meta({
|
|
873
|
-
title: "Prompt Cache Key",
|
|
874
|
-
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
875
|
-
})
|
|
876
|
-
}).optional().meta({
|
|
877
|
-
title: "xAI Provider Options",
|
|
878
|
-
description: "Allowlisted xAI provider options for grok-4.5."
|
|
879
|
-
})
|
|
1311
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
880
1312
|
}).optional().meta({
|
|
881
1313
|
title: "Provider Options",
|
|
882
1314
|
description: "Provider-specific options accepted for grok-4.5."
|
|
883
1315
|
})
|
|
884
1316
|
}).meta({
|
|
885
1317
|
title: "Grok45Config",
|
|
886
|
-
description: "Strict Responses API config for model grok-4.5. Level reasoning (low/high
|
|
1318
|
+
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.",
|
|
887
1319
|
examples: [{ reasoning: { effort: "high" } }]
|
|
888
1320
|
});
|
|
889
1321
|
var Grok46ConfigSchema = zod.z.strictObject({
|
|
@@ -917,15 +1349,7 @@ var Grok46ConfigSchema = zod.z.strictObject({
|
|
|
917
1349
|
description: "Logical request timeout in milliseconds."
|
|
918
1350
|
}),
|
|
919
1351
|
providerOptions: zod.z.strictObject({
|
|
920
|
-
xai:
|
|
921
|
-
promptCacheKey: zod.z.string().min(1).optional().meta({
|
|
922
|
-
title: "Prompt Cache Key",
|
|
923
|
-
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
924
|
-
})
|
|
925
|
-
}).optional().meta({
|
|
926
|
-
title: "xAI Provider Options",
|
|
927
|
-
description: "Allowlisted xAI provider options for grok-4.6."
|
|
928
|
-
})
|
|
1352
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
929
1353
|
}).optional().meta({
|
|
930
1354
|
title: "Provider Options",
|
|
931
1355
|
description: "Provider-specific options accepted for grok-4.6."
|
|
@@ -944,14 +1368,15 @@ var grok45ModelDescriptor = {
|
|
|
944
1368
|
capabilities: {
|
|
945
1369
|
reasoning: true,
|
|
946
1370
|
reasoningApi: "level",
|
|
947
|
-
admittedReasoningEfforts: ["low", "high"],
|
|
1371
|
+
admittedReasoningEfforts: ["low", "medium", "high"],
|
|
948
1372
|
structuredOutput: true,
|
|
949
1373
|
nativeStructuredOutput: true,
|
|
950
1374
|
vision: true,
|
|
951
1375
|
audioInput: false,
|
|
952
1376
|
sampling: "tunable",
|
|
953
1377
|
caching: { explicit: false, minTokens: 0 },
|
|
954
|
-
grounding:
|
|
1378
|
+
grounding: true,
|
|
1379
|
+
functionCalling: true
|
|
955
1380
|
// No serviceTiers key — grok-4.5 has no admitted service-tier vocabulary.
|
|
956
1381
|
},
|
|
957
1382
|
configSchema: Grok45ConfigSchema,
|
|
@@ -972,7 +1397,8 @@ var grok46ModelDescriptor = {
|
|
|
972
1397
|
audioInput: false,
|
|
973
1398
|
sampling: "tunable",
|
|
974
1399
|
caching: { explicit: false, minTokens: 0 },
|
|
975
|
-
grounding:
|
|
1400
|
+
grounding: true,
|
|
1401
|
+
functionCalling: true,
|
|
976
1402
|
serviceTiers: ["priority"]
|
|
977
1403
|
},
|
|
978
1404
|
configSchema: Grok46ConfigSchema,
|
|
@@ -986,7 +1412,12 @@ var xaiModelDescriptors = [
|
|
|
986
1412
|
var xaiRegistry = core.createModelRegistry(xaiModelDescriptors);
|
|
987
1413
|
|
|
988
1414
|
// src/pricing.ts
|
|
989
|
-
var xaiPricingVersion = "xai-2026-08-
|
|
1415
|
+
var xaiPricingVersion = "xai-2026-08-24";
|
|
1416
|
+
var XAI_TOOL_RATE_MICRO_USD = {
|
|
1417
|
+
web_search_calls: 5e3,
|
|
1418
|
+
x_search_calls: 5e3
|
|
1419
|
+
};
|
|
1420
|
+
var XAI_TOOL_COUNTER_KEYS = ["web_search_calls", "x_search_calls"];
|
|
990
1421
|
var XAI_PRICING = Object.freeze({
|
|
991
1422
|
// ── grok-4.5 ── $2.00/$6.00 (≤200k), $4.00/$12.00 (>200k); cached $0.30/$0.60
|
|
992
1423
|
"grok-4.5": {
|
|
@@ -1035,7 +1466,7 @@ function computeXaiCost(model, usage, tier) {
|
|
|
1035
1466
|
usd: null,
|
|
1036
1467
|
pricingVersion: xaiPricingVersion,
|
|
1037
1468
|
confidence: "estimated",
|
|
1038
|
-
details: { input: 0, cached: 0, output: 0 },
|
|
1469
|
+
details: { input: 0, cached: 0, output: 0, tools: 0 },
|
|
1039
1470
|
unpricedReason: `Unknown model "${model}"; no pricing entry found.`
|
|
1040
1471
|
};
|
|
1041
1472
|
}
|
|
@@ -1049,7 +1480,7 @@ function computeXaiCost(model, usage, tier) {
|
|
|
1049
1480
|
usd: null,
|
|
1050
1481
|
pricingVersion: xaiPricingVersion,
|
|
1051
1482
|
confidence: "estimated",
|
|
1052
|
-
details: { input: 0, cached: 0, output: 0 },
|
|
1483
|
+
details: { input: 0, cached: 0, output: 0, tools: 0 },
|
|
1053
1484
|
unpricedReason: `Unknown service tier "${tier}"; xai model "${model}" has no such tier, refusing to guess a pricing multiplier.`
|
|
1054
1485
|
};
|
|
1055
1486
|
}
|
|
@@ -1062,16 +1493,24 @@ function computeXaiCost(model, usage, tier) {
|
|
|
1062
1493
|
const outputCost = Math.round(
|
|
1063
1494
|
usage.outputTokens * base.outputPerM * factor / 1e6
|
|
1064
1495
|
);
|
|
1065
|
-
const
|
|
1496
|
+
const serverToolsRequested = usage.details["server_tools_requested"] === 1;
|
|
1497
|
+
const missingRequestedCounters = usage.details["server_tools_missing"] === 1 || serverToolsRequested && !XAI_TOOL_COUNTER_KEYS.some((key) => key in usage.details);
|
|
1498
|
+
const toolsCost = missingRequestedCounters ? 0 : XAI_TOOL_COUNTER_KEYS.reduce((sum, key) => {
|
|
1499
|
+
const count = usage.details[key];
|
|
1500
|
+
if (typeof count !== "number" || count <= 0) return sum;
|
|
1501
|
+
return sum + Math.round(count * XAI_TOOL_RATE_MICRO_USD[key]);
|
|
1502
|
+
}, 0);
|
|
1503
|
+
const microUsd = inputCost + cachedCost + outputCost + toolsCost;
|
|
1066
1504
|
return {
|
|
1067
1505
|
microUsd,
|
|
1068
1506
|
usd: microUsd / 1e6,
|
|
1069
1507
|
pricingVersion: xaiPricingVersion,
|
|
1070
|
-
confidence: "exact",
|
|
1508
|
+
confidence: missingRequestedCounters ? "estimated" : "exact",
|
|
1071
1509
|
details: {
|
|
1072
1510
|
input: inputCost,
|
|
1073
1511
|
cached: cachedCost,
|
|
1074
|
-
output: outputCost
|
|
1512
|
+
output: outputCost,
|
|
1513
|
+
tools: toolsCost
|
|
1075
1514
|
}
|
|
1076
1515
|
};
|
|
1077
1516
|
}
|
|
@@ -1106,6 +1545,7 @@ exports.XAI_FILE_MAX_BYTES = XAI_FILE_MAX_BYTES;
|
|
|
1106
1545
|
exports.XAI_FILE_TTL_MAX_SECONDS = XAI_FILE_TTL_MAX_SECONDS;
|
|
1107
1546
|
exports.XAI_FILE_TTL_MIN_SECONDS = XAI_FILE_TTL_MIN_SECONDS;
|
|
1108
1547
|
exports.XAI_PRICING = XAI_PRICING;
|
|
1548
|
+
exports.XAI_TOOL_RATE_MICRO_USD = XAI_TOOL_RATE_MICRO_USD;
|
|
1109
1549
|
exports.XaiFileStore = XaiFileStore;
|
|
1110
1550
|
exports.buildXaiClient = buildXaiClient;
|
|
1111
1551
|
exports.classifyXaiError = classifyXaiError;
|