@gullabs/xai 0.5.1 → 0.6.1
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 +76 -4
- package/dist/index.cjs +472 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +293 -7
- package/dist/index.d.ts +293 -7
- package/dist/index.js +473 -41
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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,
|
|
@@ -256,10 +339,48 @@ function xaiAdapter(opts) {
|
|
|
256
339
|
const warnings = [];
|
|
257
340
|
const model = req.model;
|
|
258
341
|
const genConfig = req.config;
|
|
259
|
-
const input =
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
+
}
|
|
263
384
|
const params = {
|
|
264
385
|
model,
|
|
265
386
|
input,
|
|
@@ -329,6 +450,38 @@ function xaiAdapter(opts) {
|
|
|
329
450
|
if (xaiProviderConfig.promptCacheKey !== void 0) {
|
|
330
451
|
params.prompt_cache_key = xaiProviderConfig.promptCacheKey;
|
|
331
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
|
+
}
|
|
332
485
|
let response;
|
|
333
486
|
try {
|
|
334
487
|
const buildClient = opts?._clientFactory ?? buildXaiClient;
|
|
@@ -347,14 +500,29 @@ function xaiAdapter(opts) {
|
|
|
347
500
|
let text = "";
|
|
348
501
|
let reasoningText;
|
|
349
502
|
const messageItems = [];
|
|
503
|
+
const toolCalls = [];
|
|
350
504
|
for (const item of response.output) {
|
|
351
|
-
if (item
|
|
505
|
+
if (isXaiMessageItem(item)) {
|
|
352
506
|
messageItems.push(item);
|
|
353
|
-
} else {
|
|
507
|
+
} else if (isXaiReasoningItem(item)) {
|
|
354
508
|
const joined = item.summary.map((s) => s.text).join("");
|
|
355
509
|
if (joined.length > 0) {
|
|
356
510
|
reasoningText = (reasoningText ?? "") + joined;
|
|
357
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
|
+
}
|
|
358
526
|
}
|
|
359
527
|
}
|
|
360
528
|
if (messageItems.length > 0) {
|
|
@@ -376,6 +544,29 @@ function xaiAdapter(opts) {
|
|
|
376
544
|
}
|
|
377
545
|
const usage = mapUsage(response.usage);
|
|
378
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
|
+
if (hasFileRef) {
|
|
562
|
+
usage.details["attachment_search_unpinned"] = 1;
|
|
563
|
+
warnings.push({
|
|
564
|
+
type: "other",
|
|
565
|
+
message: "xai: file-ref enables attachment_search but that counter is not live-pinned; tool cost is estimated."
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
const citations = collectXaiCitations(response, messageItems);
|
|
379
570
|
const providerMeta = {};
|
|
380
571
|
const contextDetails = response.usage["context_details"];
|
|
381
572
|
if (isPlainRecord(contextDetails)) {
|
|
@@ -395,12 +586,159 @@ function xaiAdapter(opts) {
|
|
|
395
586
|
...reasoningText !== void 0 ? { reasoningText } : {},
|
|
396
587
|
...rawStructured !== void 0 ? { rawStructured } : {},
|
|
397
588
|
...servedServiceTier !== void 0 ? { servedServiceTier } : {},
|
|
398
|
-
...Object.keys(providerMeta).length > 0 ? { providerMetadata: providerMeta } : {}
|
|
589
|
+
...Object.keys(providerMeta).length > 0 ? { providerMetadata: providerMeta } : {},
|
|
590
|
+
...citations.length > 0 ? { citations } : {},
|
|
591
|
+
...toolCalls.length > 0 ? { toolCalls, finishReason: "tool_calls" } : {}
|
|
399
592
|
};
|
|
400
593
|
return result;
|
|
594
|
+
},
|
|
595
|
+
async countTokens(req, ctx) {
|
|
596
|
+
if (req.provider !== "xai") {
|
|
597
|
+
throw new core.LlmError(
|
|
598
|
+
`xaiAdapter received a request for provider "${req.provider}", expected "xai".`,
|
|
599
|
+
{ kind: "bad_request", retryable: false }
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
if (req.tools !== void 0 && req.tools.length > 0) {
|
|
603
|
+
throw badXaiRequest(
|
|
604
|
+
"xAI countTokens rejects tools; tokenize-text cannot represent tool declarations."
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
const text = concatenateTokenizeText(req);
|
|
608
|
+
const apiKey = requireApiKey(ctx.auth);
|
|
609
|
+
const fetchImpl = opts?._fetch ?? fetch;
|
|
610
|
+
try {
|
|
611
|
+
const res = await fetchImpl("https://api.x.ai/v1/tokenize-text", {
|
|
612
|
+
method: "POST",
|
|
613
|
+
headers: {
|
|
614
|
+
Authorization: `Bearer ${apiKey}`,
|
|
615
|
+
"Content-Type": "application/json"
|
|
616
|
+
},
|
|
617
|
+
body: JSON.stringify({ model: req.model, text }),
|
|
618
|
+
...ctx.signal !== void 0 ? { signal: ctx.signal } : {}
|
|
619
|
+
});
|
|
620
|
+
if (!res.ok) {
|
|
621
|
+
let parsed;
|
|
622
|
+
try {
|
|
623
|
+
parsed = await res.json();
|
|
624
|
+
} catch {
|
|
625
|
+
parsed = await res.text().catch(() => "");
|
|
626
|
+
}
|
|
627
|
+
throw Object.assign(new Error(`xAI tokenize-text HTTP ${res.status}`), {
|
|
628
|
+
status: res.status,
|
|
629
|
+
error: parsed
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
const raw = await res.json();
|
|
633
|
+
if (!isPlainRecord(raw) || !Array.isArray(raw["token_ids"])) {
|
|
634
|
+
throw new core.LlmError(
|
|
635
|
+
"xAI tokenize-text response is malformed: missing required field: token_ids",
|
|
636
|
+
{ kind: "server", retryable: true, provider: "xai" }
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
const n = raw["token_ids"].length;
|
|
640
|
+
return {
|
|
641
|
+
totalTokens: n,
|
|
642
|
+
accuracy: "lower-bound",
|
|
643
|
+
details: { textTokens: n },
|
|
644
|
+
raw
|
|
645
|
+
};
|
|
646
|
+
} catch (rawErr) {
|
|
647
|
+
if (rawErr instanceof Error && rawErr.name === "AbortError") {
|
|
648
|
+
throw new core.LlmError("xAI tokenize-text aborted", {
|
|
649
|
+
kind: "aborted",
|
|
650
|
+
retryable: false,
|
|
651
|
+
provider: "xai",
|
|
652
|
+
cause: rawErr
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
throw classifyXaiError(rawErr);
|
|
656
|
+
}
|
|
401
657
|
}
|
|
402
658
|
};
|
|
403
659
|
}
|
|
660
|
+
var WEB_SEARCH_COUNTER = "web_search_calls";
|
|
661
|
+
var X_SEARCH_COUNTER = "x_search_calls";
|
|
662
|
+
function expectedServerToolCounters(tools, _hasFileRef) {
|
|
663
|
+
const keys = [];
|
|
664
|
+
if (tools !== void 0) {
|
|
665
|
+
for (const tool of tools) {
|
|
666
|
+
if (tool["type"] === "web_search") keys.push(WEB_SEARCH_COUNTER);
|
|
667
|
+
if (tool["type"] === "x_search") keys.push(X_SEARCH_COUNTER);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return keys;
|
|
671
|
+
}
|
|
672
|
+
function collectXaiCitations(response, messageItems) {
|
|
673
|
+
const seen = /* @__PURE__ */ new Set();
|
|
674
|
+
const citations = [];
|
|
675
|
+
const push = (url, title) => {
|
|
676
|
+
if (typeof url !== "string" || url.length === 0) return;
|
|
677
|
+
if (seen.has(url)) return;
|
|
678
|
+
seen.add(url);
|
|
679
|
+
const citation = { url };
|
|
680
|
+
if (typeof title === "string" && title.length > 0 && title !== url) {
|
|
681
|
+
citation.title = title;
|
|
682
|
+
}
|
|
683
|
+
try {
|
|
684
|
+
const parsed = new URL(url);
|
|
685
|
+
if (parsed.hostname.length > 0) {
|
|
686
|
+
citation.sourceName = parsed.hostname.startsWith("www.") ? parsed.hostname.slice(4) : parsed.hostname;
|
|
687
|
+
}
|
|
688
|
+
} catch {
|
|
689
|
+
}
|
|
690
|
+
citations.push(citation);
|
|
691
|
+
};
|
|
692
|
+
if (Array.isArray(response.citations)) {
|
|
693
|
+
for (const item of response.citations) {
|
|
694
|
+
if (typeof item === "string") {
|
|
695
|
+
push(item, void 0);
|
|
696
|
+
} else if (isPlainRecord(item)) {
|
|
697
|
+
push(item["url"] ?? item["uri"], item["title"]);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const lastMessage = messageItems.at(-1);
|
|
702
|
+
const citationMessages = lastMessage === void 0 ? [] : [lastMessage];
|
|
703
|
+
for (const item of citationMessages) {
|
|
704
|
+
for (const part of item.content) {
|
|
705
|
+
const annotations = part.annotations;
|
|
706
|
+
if (!Array.isArray(annotations)) continue;
|
|
707
|
+
for (const ann of annotations) {
|
|
708
|
+
if (!isPlainRecord(ann)) continue;
|
|
709
|
+
if (ann["type"] !== void 0 && ann["type"] !== "url_citation") continue;
|
|
710
|
+
push(ann["url"], ann["title"]);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return citations;
|
|
715
|
+
}
|
|
716
|
+
function concatenateTokenizeText(req) {
|
|
717
|
+
const chunks = [];
|
|
718
|
+
if (req.system !== void 0 && req.system.length > 0) {
|
|
719
|
+
chunks.push(req.system);
|
|
720
|
+
}
|
|
721
|
+
for (const message of req.messages) {
|
|
722
|
+
for (const part of message.parts) {
|
|
723
|
+
switch (part.kind) {
|
|
724
|
+
case "text":
|
|
725
|
+
chunks.push(part.text);
|
|
726
|
+
break;
|
|
727
|
+
case "inline-media":
|
|
728
|
+
case "file-uri":
|
|
729
|
+
case "file-ref":
|
|
730
|
+
case "tool-call":
|
|
731
|
+
case "tool-result":
|
|
732
|
+
throw badXaiRequest(
|
|
733
|
+
`xAI countTokens rejects ${part.kind} parts; tokenize-text is text-only.`
|
|
734
|
+
);
|
|
735
|
+
default:
|
|
736
|
+
return core.assertNever(part);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return chunks.join("\n");
|
|
741
|
+
}
|
|
404
742
|
var XAI_FILE_TTL_MIN_SECONDS = 3600;
|
|
405
743
|
var XAI_FILE_TTL_MAX_SECONDS = 2592e3;
|
|
406
744
|
var XAI_FILE_MAX_BYTES = 48 * 1024 * 1024;
|
|
@@ -856,6 +1194,100 @@ var XaiFileStore = class {
|
|
|
856
1194
|
return new Uint8Array(buf);
|
|
857
1195
|
}
|
|
858
1196
|
};
|
|
1197
|
+
var webSearchFlags = {
|
|
1198
|
+
enableImageUnderstanding: zod.z.boolean().optional().meta({
|
|
1199
|
+
title: "Enable Image Understanding",
|
|
1200
|
+
description: "Ask xAI to analyze images found during web search."
|
|
1201
|
+
}),
|
|
1202
|
+
enableImageSearch: zod.z.boolean().optional().meta({
|
|
1203
|
+
title: "Enable Image Search",
|
|
1204
|
+
description: "Ask xAI to include image search results."
|
|
1205
|
+
})
|
|
1206
|
+
};
|
|
1207
|
+
var XaiWebSearchToolSchema = zod.z.union([
|
|
1208
|
+
zod.z.strictObject({
|
|
1209
|
+
type: zod.z.literal("web_search"),
|
|
1210
|
+
allowedDomains: zod.z.array(zod.z.string()).max(5),
|
|
1211
|
+
...webSearchFlags
|
|
1212
|
+
}),
|
|
1213
|
+
zod.z.strictObject({
|
|
1214
|
+
type: zod.z.literal("web_search"),
|
|
1215
|
+
excludedDomains: zod.z.array(zod.z.string()).max(5),
|
|
1216
|
+
...webSearchFlags
|
|
1217
|
+
}),
|
|
1218
|
+
zod.z.strictObject({
|
|
1219
|
+
type: zod.z.literal("web_search"),
|
|
1220
|
+
...webSearchFlags
|
|
1221
|
+
})
|
|
1222
|
+
]).meta({
|
|
1223
|
+
title: "XaiWebSearchTool",
|
|
1224
|
+
description: "xAI web_search server tool. allowedDomains and excludedDomains are mutually exclusive (max 5)."
|
|
1225
|
+
});
|
|
1226
|
+
var xSearchFlags = {
|
|
1227
|
+
fromDate: zod.z.iso.date().optional().meta({
|
|
1228
|
+
title: "From Date",
|
|
1229
|
+
description: "Inclusive ISO-8601 date lower bound for X search."
|
|
1230
|
+
}),
|
|
1231
|
+
toDate: zod.z.iso.date().optional().meta({
|
|
1232
|
+
title: "To Date",
|
|
1233
|
+
description: "Inclusive ISO-8601 date upper bound for X search."
|
|
1234
|
+
}),
|
|
1235
|
+
enableImageUnderstanding: zod.z.boolean().optional().meta({
|
|
1236
|
+
title: "Enable Image Understanding",
|
|
1237
|
+
description: "Ask xAI to analyze images in matched posts."
|
|
1238
|
+
}),
|
|
1239
|
+
enableVideoUnderstanding: zod.z.boolean().optional().meta({
|
|
1240
|
+
title: "Enable Video Understanding",
|
|
1241
|
+
description: "Ask xAI to analyze videos in matched posts."
|
|
1242
|
+
})
|
|
1243
|
+
};
|
|
1244
|
+
var XaiXSearchToolSchema = zod.z.union([
|
|
1245
|
+
zod.z.strictObject({
|
|
1246
|
+
type: zod.z.literal("x_search"),
|
|
1247
|
+
allowedXHandles: zod.z.array(zod.z.string()).max(20),
|
|
1248
|
+
...xSearchFlags
|
|
1249
|
+
}),
|
|
1250
|
+
zod.z.strictObject({
|
|
1251
|
+
type: zod.z.literal("x_search"),
|
|
1252
|
+
excludedXHandles: zod.z.array(zod.z.string()).max(20),
|
|
1253
|
+
...xSearchFlags
|
|
1254
|
+
}),
|
|
1255
|
+
zod.z.strictObject({
|
|
1256
|
+
type: zod.z.literal("x_search"),
|
|
1257
|
+
...xSearchFlags
|
|
1258
|
+
})
|
|
1259
|
+
]).meta({
|
|
1260
|
+
title: "XaiXSearchTool",
|
|
1261
|
+
description: "xAI x_search server tool. allowedXHandles and excludedXHandles are mutually exclusive (max 20)."
|
|
1262
|
+
});
|
|
1263
|
+
var XaiToolsSchema = zod.z.union([
|
|
1264
|
+
zod.z.tuple([XaiWebSearchToolSchema]),
|
|
1265
|
+
zod.z.tuple([XaiXSearchToolSchema]),
|
|
1266
|
+
zod.z.tuple([XaiWebSearchToolSchema, XaiXSearchToolSchema]),
|
|
1267
|
+
zod.z.tuple([XaiXSearchToolSchema, XaiWebSearchToolSchema])
|
|
1268
|
+
]).meta({
|
|
1269
|
+
title: "XaiTools",
|
|
1270
|
+
description: "xAI Live Search tools. At most one web_search and at most one x_search."
|
|
1271
|
+
});
|
|
1272
|
+
var XaiProviderOptionsSchema = zod.z.strictObject({
|
|
1273
|
+
promptCacheKey: zod.z.string().min(1).optional().meta({
|
|
1274
|
+
title: "Prompt Cache Key",
|
|
1275
|
+
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
1276
|
+
}),
|
|
1277
|
+
tools: XaiToolsSchema.optional().meta({
|
|
1278
|
+
title: "Live Search Tools",
|
|
1279
|
+
description: "xAI server-side web_search / x_search tools."
|
|
1280
|
+
}),
|
|
1281
|
+
parallelToolCalls: zod.z.boolean().optional().meta({
|
|
1282
|
+
title: "Parallel Tool Calls",
|
|
1283
|
+
description: "xAI Responses parallel_tool_calls. Not a generic contract field."
|
|
1284
|
+
})
|
|
1285
|
+
}).meta({
|
|
1286
|
+
title: "xAI Provider Options",
|
|
1287
|
+
description: "Allowlisted xAI provider options."
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
// src/model-config/grok-4-5.ts
|
|
859
1291
|
var Grok45ConfigSchema = zod.z.strictObject({
|
|
860
1292
|
temperature: zod.z.number().optional().meta({
|
|
861
1293
|
title: "Temperature",
|
|
@@ -870,9 +1302,9 @@ var Grok45ConfigSchema = zod.z.strictObject({
|
|
|
870
1302
|
description: "Maximum output token cap for grok-4.5. No artificial ceiling \u2014 xAI accepts arbitrarily large values (live-verified); truncation surfaces as finishReason:'length', not an error."
|
|
871
1303
|
}),
|
|
872
1304
|
reasoning: zod.z.strictObject({
|
|
873
|
-
effort: zod.z.enum(["low", "high"]).meta({
|
|
1305
|
+
effort: zod.z.enum(["low", "medium", "high"]).meta({
|
|
874
1306
|
title: "Reasoning Effort",
|
|
875
|
-
description: 'Reasoning effort for grok-4.5.
|
|
1307
|
+
description: 'Reasoning effort for grok-4.5. "low", "medium", and "high" are admitted (live-verified 2026-08-24); "none"/"xhigh" are rejected by the live API. Vendor default when omitted is "high".'
|
|
876
1308
|
})
|
|
877
1309
|
}).optional().meta({
|
|
878
1310
|
title: "Reasoning",
|
|
@@ -883,22 +1315,14 @@ var Grok45ConfigSchema = zod.z.strictObject({
|
|
|
883
1315
|
description: "Logical request timeout in milliseconds."
|
|
884
1316
|
}),
|
|
885
1317
|
providerOptions: zod.z.strictObject({
|
|
886
|
-
xai:
|
|
887
|
-
promptCacheKey: zod.z.string().min(1).optional().meta({
|
|
888
|
-
title: "Prompt Cache Key",
|
|
889
|
-
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
890
|
-
})
|
|
891
|
-
}).optional().meta({
|
|
892
|
-
title: "xAI Provider Options",
|
|
893
|
-
description: "Allowlisted xAI provider options for grok-4.5."
|
|
894
|
-
})
|
|
1318
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
895
1319
|
}).optional().meta({
|
|
896
1320
|
title: "Provider Options",
|
|
897
1321
|
description: "Provider-specific options accepted for grok-4.5."
|
|
898
1322
|
})
|
|
899
1323
|
}).meta({
|
|
900
1324
|
title: "Grok45Config",
|
|
901
|
-
description: "Strict Responses API config for model grok-4.5. Level reasoning (low/high
|
|
1325
|
+
description: "Strict Responses API config for model grok-4.5. Level reasoning (low/medium/high), tunable sampling, no service tiers, structured output, vision, Live Search tools, priced.",
|
|
902
1326
|
examples: [{ reasoning: { effort: "high" } }]
|
|
903
1327
|
});
|
|
904
1328
|
var Grok46ConfigSchema = zod.z.strictObject({
|
|
@@ -932,15 +1356,7 @@ var Grok46ConfigSchema = zod.z.strictObject({
|
|
|
932
1356
|
description: "Logical request timeout in milliseconds."
|
|
933
1357
|
}),
|
|
934
1358
|
providerOptions: zod.z.strictObject({
|
|
935
|
-
xai:
|
|
936
|
-
promptCacheKey: zod.z.string().min(1).optional().meta({
|
|
937
|
-
title: "Prompt Cache Key",
|
|
938
|
-
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
939
|
-
})
|
|
940
|
-
}).optional().meta({
|
|
941
|
-
title: "xAI Provider Options",
|
|
942
|
-
description: "Allowlisted xAI provider options for grok-4.6."
|
|
943
|
-
})
|
|
1359
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
944
1360
|
}).optional().meta({
|
|
945
1361
|
title: "Provider Options",
|
|
946
1362
|
description: "Provider-specific options accepted for grok-4.6."
|
|
@@ -959,14 +1375,15 @@ var grok45ModelDescriptor = {
|
|
|
959
1375
|
capabilities: {
|
|
960
1376
|
reasoning: true,
|
|
961
1377
|
reasoningApi: "level",
|
|
962
|
-
admittedReasoningEfforts: ["low", "high"],
|
|
1378
|
+
admittedReasoningEfforts: ["low", "medium", "high"],
|
|
963
1379
|
structuredOutput: true,
|
|
964
1380
|
nativeStructuredOutput: true,
|
|
965
1381
|
vision: true,
|
|
966
1382
|
audioInput: false,
|
|
967
1383
|
sampling: "tunable",
|
|
968
1384
|
caching: { explicit: false, minTokens: 0 },
|
|
969
|
-
grounding:
|
|
1385
|
+
grounding: true,
|
|
1386
|
+
functionCalling: true
|
|
970
1387
|
// No serviceTiers key — grok-4.5 has no admitted service-tier vocabulary.
|
|
971
1388
|
},
|
|
972
1389
|
configSchema: Grok45ConfigSchema,
|
|
@@ -987,7 +1404,8 @@ var grok46ModelDescriptor = {
|
|
|
987
1404
|
audioInput: false,
|
|
988
1405
|
sampling: "tunable",
|
|
989
1406
|
caching: { explicit: false, minTokens: 0 },
|
|
990
|
-
grounding:
|
|
1407
|
+
grounding: true,
|
|
1408
|
+
functionCalling: true,
|
|
991
1409
|
serviceTiers: ["priority"]
|
|
992
1410
|
},
|
|
993
1411
|
configSchema: Grok46ConfigSchema,
|
|
@@ -1001,7 +1419,12 @@ var xaiModelDescriptors = [
|
|
|
1001
1419
|
var xaiRegistry = core.createModelRegistry(xaiModelDescriptors);
|
|
1002
1420
|
|
|
1003
1421
|
// src/pricing.ts
|
|
1004
|
-
var xaiPricingVersion = "xai-2026-08-
|
|
1422
|
+
var xaiPricingVersion = "xai-2026-08-24";
|
|
1423
|
+
var XAI_TOOL_RATE_MICRO_USD = {
|
|
1424
|
+
web_search_calls: 5e3,
|
|
1425
|
+
x_search_calls: 5e3
|
|
1426
|
+
};
|
|
1427
|
+
var XAI_TOOL_COUNTER_KEYS = ["web_search_calls", "x_search_calls"];
|
|
1005
1428
|
var XAI_PRICING = Object.freeze({
|
|
1006
1429
|
// ── grok-4.5 ── $2.00/$6.00 (≤200k), $4.00/$12.00 (>200k); cached $0.30/$0.60
|
|
1007
1430
|
"grok-4.5": {
|
|
@@ -1050,7 +1473,7 @@ function computeXaiCost(model, usage, tier) {
|
|
|
1050
1473
|
usd: null,
|
|
1051
1474
|
pricingVersion: xaiPricingVersion,
|
|
1052
1475
|
confidence: "estimated",
|
|
1053
|
-
details: { input: 0, cached: 0, output: 0 },
|
|
1476
|
+
details: { input: 0, cached: 0, output: 0, tools: 0 },
|
|
1054
1477
|
unpricedReason: `Unknown model "${model}"; no pricing entry found.`
|
|
1055
1478
|
};
|
|
1056
1479
|
}
|
|
@@ -1064,7 +1487,7 @@ function computeXaiCost(model, usage, tier) {
|
|
|
1064
1487
|
usd: null,
|
|
1065
1488
|
pricingVersion: xaiPricingVersion,
|
|
1066
1489
|
confidence: "estimated",
|
|
1067
|
-
details: { input: 0, cached: 0, output: 0 },
|
|
1490
|
+
details: { input: 0, cached: 0, output: 0, tools: 0 },
|
|
1068
1491
|
unpricedReason: `Unknown service tier "${tier}"; xai model "${model}" has no such tier, refusing to guess a pricing multiplier.`
|
|
1069
1492
|
};
|
|
1070
1493
|
}
|
|
@@ -1077,16 +1500,25 @@ function computeXaiCost(model, usage, tier) {
|
|
|
1077
1500
|
const outputCost = Math.round(
|
|
1078
1501
|
usage.outputTokens * base.outputPerM * factor / 1e6
|
|
1079
1502
|
);
|
|
1080
|
-
const
|
|
1503
|
+
const serverToolsRequested = usage.details["server_tools_requested"] === 1;
|
|
1504
|
+
const missingRequestedCounters = usage.details["server_tools_missing"] === 1 || serverToolsRequested && usage.details["attachment_search_unpinned"] !== 1 && !XAI_TOOL_COUNTER_KEYS.some((key) => key in usage.details);
|
|
1505
|
+
const attachmentUnpinned = usage.details["attachment_search_unpinned"] === 1;
|
|
1506
|
+
const toolsCost = missingRequestedCounters ? 0 : XAI_TOOL_COUNTER_KEYS.reduce((sum, key) => {
|
|
1507
|
+
const count = usage.details[key];
|
|
1508
|
+
if (typeof count !== "number" || count <= 0) return sum;
|
|
1509
|
+
return sum + Math.round(count * XAI_TOOL_RATE_MICRO_USD[key]);
|
|
1510
|
+
}, 0);
|
|
1511
|
+
const microUsd = inputCost + cachedCost + outputCost + toolsCost;
|
|
1081
1512
|
return {
|
|
1082
1513
|
microUsd,
|
|
1083
1514
|
usd: microUsd / 1e6,
|
|
1084
1515
|
pricingVersion: xaiPricingVersion,
|
|
1085
|
-
confidence: "exact",
|
|
1516
|
+
confidence: missingRequestedCounters || attachmentUnpinned ? "estimated" : "exact",
|
|
1086
1517
|
details: {
|
|
1087
1518
|
input: inputCost,
|
|
1088
1519
|
cached: cachedCost,
|
|
1089
|
-
output: outputCost
|
|
1520
|
+
output: outputCost,
|
|
1521
|
+
tools: toolsCost
|
|
1090
1522
|
}
|
|
1091
1523
|
};
|
|
1092
1524
|
}
|
|
@@ -1121,6 +1553,7 @@ exports.XAI_FILE_MAX_BYTES = XAI_FILE_MAX_BYTES;
|
|
|
1121
1553
|
exports.XAI_FILE_TTL_MAX_SECONDS = XAI_FILE_TTL_MAX_SECONDS;
|
|
1122
1554
|
exports.XAI_FILE_TTL_MIN_SECONDS = XAI_FILE_TTL_MIN_SECONDS;
|
|
1123
1555
|
exports.XAI_PRICING = XAI_PRICING;
|
|
1556
|
+
exports.XAI_TOOL_RATE_MICRO_USD = XAI_TOOL_RATE_MICRO_USD;
|
|
1124
1557
|
exports.XaiFileStore = XaiFileStore;
|
|
1125
1558
|
exports.buildXaiClient = buildXaiClient;
|
|
1126
1559
|
exports.classifyXaiError = classifyXaiError;
|