@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.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { zodToStandardSchema, toConfigJsonSchema, createModelRegistry, LlmError, classifyError,
|
|
1
|
+
import { zodToStandardSchema, toConfigJsonSchema, createModelRegistry, LlmError, classifyError, assertNever, redactSecrets } from '@gullabs/core';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
// src/client.ts
|
|
@@ -31,6 +31,12 @@ async function buildXaiClient(auth) {
|
|
|
31
31
|
function isPlainRecord(value) {
|
|
32
32
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33
33
|
}
|
|
34
|
+
function isXaiMessageItem(item) {
|
|
35
|
+
return item.type === "message" && Array.isArray(item.content);
|
|
36
|
+
}
|
|
37
|
+
function isXaiReasoningItem(item) {
|
|
38
|
+
return item.type === "reasoning" && Array.isArray(item.summary);
|
|
39
|
+
}
|
|
34
40
|
function badXaiRequest(message) {
|
|
35
41
|
return new LlmError(message, { kind: "bad_request", retryable: false });
|
|
36
42
|
}
|
|
@@ -75,10 +81,16 @@ function mapPart(p) {
|
|
|
75
81
|
}
|
|
76
82
|
return { type: "input_file", file_id: p.fileId };
|
|
77
83
|
}
|
|
84
|
+
case "tool-call":
|
|
85
|
+
case "tool-result":
|
|
86
|
+
throw badXaiRequest(
|
|
87
|
+
`xAI mapPart does not emit ${p.kind} as a content part; the request mapper handles replay items.`
|
|
88
|
+
);
|
|
78
89
|
default:
|
|
79
90
|
return assertNever(p);
|
|
80
91
|
}
|
|
81
92
|
}
|
|
93
|
+
var XAI_PROVIDER_OPTION_KEYS = /* @__PURE__ */ new Set(["promptCacheKey", "tools", "parallelToolCalls"]);
|
|
82
94
|
function mapXaiProviderOptions(xaiOpts, model) {
|
|
83
95
|
if (xaiOpts === void 0) {
|
|
84
96
|
return {};
|
|
@@ -86,12 +98,14 @@ function mapXaiProviderOptions(xaiOpts, model) {
|
|
|
86
98
|
if (!isPlainRecord(xaiOpts)) {
|
|
87
99
|
throw badXaiRequest(`providerOptions.xai must be an object for model "${model}".`);
|
|
88
100
|
}
|
|
89
|
-
const unknownKeys = Object.keys(xaiOpts).filter(
|
|
101
|
+
const unknownKeys = Object.keys(xaiOpts).filter(
|
|
102
|
+
(key) => !XAI_PROVIDER_OPTION_KEYS.has(key)
|
|
103
|
+
);
|
|
90
104
|
if (unknownKeys.length > 0) {
|
|
91
105
|
throw badXaiRequest(
|
|
92
106
|
`providerOptions.xai contains unsupported keys [${unknownKeys.join(
|
|
93
107
|
", "
|
|
94
|
-
)}] for model "${model}". Allowed keys: promptCacheKey.`
|
|
108
|
+
)}] for model "${model}". Allowed keys: promptCacheKey, tools, parallelToolCalls.`
|
|
95
109
|
);
|
|
96
110
|
}
|
|
97
111
|
const mapped = {};
|
|
@@ -103,8 +117,69 @@ function mapXaiProviderOptions(xaiOpts, model) {
|
|
|
103
117
|
}
|
|
104
118
|
mapped.promptCacheKey = xaiOpts["promptCacheKey"];
|
|
105
119
|
}
|
|
120
|
+
if (xaiOpts["tools"] !== void 0) {
|
|
121
|
+
mapped.tools = mapXaiSearchTools(xaiOpts["tools"], model);
|
|
122
|
+
}
|
|
123
|
+
if (xaiOpts["parallelToolCalls"] !== void 0) {
|
|
124
|
+
if (typeof xaiOpts["parallelToolCalls"] !== "boolean") {
|
|
125
|
+
throw badXaiRequest(
|
|
126
|
+
`providerOptions.xai.parallelToolCalls must be a boolean for model "${model}".`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
mapped.parallelToolCalls = xaiOpts["parallelToolCalls"];
|
|
130
|
+
}
|
|
106
131
|
return mapped;
|
|
107
132
|
}
|
|
133
|
+
function mapXaiSearchTools(tools, model) {
|
|
134
|
+
if (!Array.isArray(tools)) {
|
|
135
|
+
throw badXaiRequest(
|
|
136
|
+
`providerOptions.xai.tools must be an array for model "${model}".`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
return tools.map((tool, index) => {
|
|
140
|
+
if (!isPlainRecord(tool) || typeof tool["type"] !== "string") {
|
|
141
|
+
throw badXaiRequest(
|
|
142
|
+
`providerOptions.xai.tools[${index}] must be an object with a type for model "${model}".`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
if (tool["type"] === "web_search") {
|
|
146
|
+
const wire = { type: "web_search" };
|
|
147
|
+
if (tool["allowedDomains"] !== void 0)
|
|
148
|
+
wire["allowed_domains"] = tool["allowedDomains"];
|
|
149
|
+
if (tool["excludedDomains"] !== void 0) {
|
|
150
|
+
wire["excluded_domains"] = tool["excludedDomains"];
|
|
151
|
+
}
|
|
152
|
+
if (tool["enableImageUnderstanding"] !== void 0) {
|
|
153
|
+
wire["enable_image_understanding"] = tool["enableImageUnderstanding"];
|
|
154
|
+
}
|
|
155
|
+
if (tool["enableImageSearch"] !== void 0) {
|
|
156
|
+
wire["enable_image_search"] = tool["enableImageSearch"];
|
|
157
|
+
}
|
|
158
|
+
return wire;
|
|
159
|
+
}
|
|
160
|
+
if (tool["type"] === "x_search") {
|
|
161
|
+
const wire = { type: "x_search" };
|
|
162
|
+
if (tool["allowedXHandles"] !== void 0) {
|
|
163
|
+
wire["allowed_x_handles"] = tool["allowedXHandles"];
|
|
164
|
+
}
|
|
165
|
+
if (tool["excludedXHandles"] !== void 0) {
|
|
166
|
+
wire["excluded_x_handles"] = tool["excludedXHandles"];
|
|
167
|
+
}
|
|
168
|
+
if (tool["fromDate"] !== void 0) wire["from_date"] = tool["fromDate"];
|
|
169
|
+
if (tool["toDate"] !== void 0) wire["to_date"] = tool["toDate"];
|
|
170
|
+
if (tool["enableImageUnderstanding"] !== void 0) {
|
|
171
|
+
wire["enable_image_understanding"] = tool["enableImageUnderstanding"];
|
|
172
|
+
}
|
|
173
|
+
if (tool["enableVideoUnderstanding"] !== void 0) {
|
|
174
|
+
wire["enable_video_understanding"] = tool["enableVideoUnderstanding"];
|
|
175
|
+
}
|
|
176
|
+
return wire;
|
|
177
|
+
}
|
|
178
|
+
throw badXaiRequest(
|
|
179
|
+
`providerOptions.xai.tools[${index}].type "${String(tool["type"])}" is not supported for model "${model}".`
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
108
183
|
function mapFinishReason(response) {
|
|
109
184
|
if (response.status === "completed") {
|
|
110
185
|
return "stop";
|
|
@@ -136,6 +211,14 @@ function mapUsage(usage) {
|
|
|
136
211
|
details[key] = value;
|
|
137
212
|
}
|
|
138
213
|
}
|
|
214
|
+
const toolUsage = usage["server_side_tool_usage_details"];
|
|
215
|
+
if (isPlainRecord(toolUsage)) {
|
|
216
|
+
for (const [key, value] of Object.entries(toolUsage)) {
|
|
217
|
+
if (typeof value === "number") {
|
|
218
|
+
details[key] = value;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
139
222
|
const raw = usage;
|
|
140
223
|
const result = {
|
|
141
224
|
inputTokens,
|
|
@@ -254,10 +337,48 @@ function xaiAdapter(opts) {
|
|
|
254
337
|
const warnings = [];
|
|
255
338
|
const model = req.model;
|
|
256
339
|
const genConfig = req.config;
|
|
257
|
-
const input =
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
340
|
+
const input = [];
|
|
341
|
+
for (const msg of req.messages) {
|
|
342
|
+
const contentParts = [];
|
|
343
|
+
for (const part of msg.parts) {
|
|
344
|
+
if (part.kind === "tool-call") {
|
|
345
|
+
if (contentParts.length > 0) {
|
|
346
|
+
input.push({
|
|
347
|
+
role: msg.role === "assistant" ? "assistant" : "user",
|
|
348
|
+
content: contentParts.splice(0)
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
input.push({
|
|
352
|
+
type: "function_call",
|
|
353
|
+
call_id: part.toolCallId,
|
|
354
|
+
name: part.toolName,
|
|
355
|
+
arguments: JSON.stringify(part.args)
|
|
356
|
+
});
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (part.kind === "tool-result") {
|
|
360
|
+
if (contentParts.length > 0) {
|
|
361
|
+
input.push({
|
|
362
|
+
role: msg.role === "assistant" ? "assistant" : "user",
|
|
363
|
+
content: contentParts.splice(0)
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
input.push({
|
|
367
|
+
type: "function_call_output",
|
|
368
|
+
call_id: part.toolCallId,
|
|
369
|
+
output: JSON.stringify(part.result)
|
|
370
|
+
});
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
contentParts.push(mapPart(part));
|
|
374
|
+
}
|
|
375
|
+
if (contentParts.length > 0) {
|
|
376
|
+
input.push({
|
|
377
|
+
role: msg.role === "assistant" ? "assistant" : "user",
|
|
378
|
+
content: contentParts
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
261
382
|
const params = {
|
|
262
383
|
model,
|
|
263
384
|
input,
|
|
@@ -327,6 +448,38 @@ function xaiAdapter(opts) {
|
|
|
327
448
|
if (xaiProviderConfig.promptCacheKey !== void 0) {
|
|
328
449
|
params.prompt_cache_key = xaiProviderConfig.promptCacheKey;
|
|
329
450
|
}
|
|
451
|
+
const hasFileRef = req.messages.some(
|
|
452
|
+
(msg) => msg.parts.some((part) => part.kind === "file-ref")
|
|
453
|
+
);
|
|
454
|
+
const searchTools = xaiProviderConfig.tools;
|
|
455
|
+
if (searchTools !== void 0) {
|
|
456
|
+
if (req.modelDescriptor?.capabilities?.grounding !== true) {
|
|
457
|
+
throw badXaiRequest(
|
|
458
|
+
`providerOptions.xai.tools requires capabilities.grounding on the model descriptor for "${model}".`
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
params.tools = searchTools;
|
|
462
|
+
}
|
|
463
|
+
if (req.tools !== void 0 && req.tools.length > 0) {
|
|
464
|
+
if (req.modelDescriptor?.capabilities?.functionCalling !== true) {
|
|
465
|
+
throw badXaiRequest(
|
|
466
|
+
`tools is not supported for xai model "${model}" (capabilities.functionCalling is not true).`
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
const functionTools = req.tools.map((tool) => ({
|
|
470
|
+
type: "function",
|
|
471
|
+
name: tool.name,
|
|
472
|
+
description: tool.description,
|
|
473
|
+
parameters: tool.inputJsonSchema
|
|
474
|
+
}));
|
|
475
|
+
params.tools = [...params.tools ?? [], ...functionTools];
|
|
476
|
+
if (req.toolChoice !== void 0) {
|
|
477
|
+
params.tool_choice = typeof req.toolChoice === "string" ? req.toolChoice : { type: "function", name: req.toolChoice.name };
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
if (xaiProviderConfig.parallelToolCalls !== void 0) {
|
|
481
|
+
params.parallel_tool_calls = xaiProviderConfig.parallelToolCalls;
|
|
482
|
+
}
|
|
330
483
|
let response;
|
|
331
484
|
try {
|
|
332
485
|
const buildClient = opts?._clientFactory ?? buildXaiClient;
|
|
@@ -345,14 +498,29 @@ function xaiAdapter(opts) {
|
|
|
345
498
|
let text = "";
|
|
346
499
|
let reasoningText;
|
|
347
500
|
const messageItems = [];
|
|
501
|
+
const toolCalls = [];
|
|
348
502
|
for (const item of response.output) {
|
|
349
|
-
if (item
|
|
503
|
+
if (isXaiMessageItem(item)) {
|
|
350
504
|
messageItems.push(item);
|
|
351
|
-
} else {
|
|
505
|
+
} else if (isXaiReasoningItem(item)) {
|
|
352
506
|
const joined = item.summary.map((s) => s.text).join("");
|
|
353
507
|
if (joined.length > 0) {
|
|
354
508
|
reasoningText = (reasoningText ?? "") + joined;
|
|
355
509
|
}
|
|
510
|
+
} else if (item.type === "function_call") {
|
|
511
|
+
const callId = typeof item["call_id"] === "string" ? item["call_id"] : "";
|
|
512
|
+
const name = typeof item["name"] === "string" ? item["name"] : "";
|
|
513
|
+
let args = {};
|
|
514
|
+
if (typeof item["arguments"] === "string") {
|
|
515
|
+
try {
|
|
516
|
+
args = JSON.parse(item["arguments"]);
|
|
517
|
+
} catch {
|
|
518
|
+
args = item["arguments"];
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
if (callId.length > 0 && name.length > 0) {
|
|
522
|
+
toolCalls.push({ toolCallId: callId, toolName: name, args });
|
|
523
|
+
}
|
|
356
524
|
}
|
|
357
525
|
}
|
|
358
526
|
if (messageItems.length > 0) {
|
|
@@ -374,6 +542,29 @@ function xaiAdapter(opts) {
|
|
|
374
542
|
}
|
|
375
543
|
const usage = mapUsage(response.usage);
|
|
376
544
|
const finishReason = mapFinishReason(response);
|
|
545
|
+
const expectedToolCounters = expectedServerToolCounters(
|
|
546
|
+
xaiProviderConfig.tools);
|
|
547
|
+
if (expectedToolCounters.length > 0 || hasFileRef) {
|
|
548
|
+
usage.details["server_tools_requested"] = 1;
|
|
549
|
+
const missing = expectedToolCounters.filter((key) => !(key in usage.details));
|
|
550
|
+
if (missing.length > 0) {
|
|
551
|
+
usage.details["server_tools_missing"] = 1;
|
|
552
|
+
warnings.push({
|
|
553
|
+
type: "other",
|
|
554
|
+
message: `xai: server tools were requested but usage is missing counters [${missing.join(
|
|
555
|
+
", "
|
|
556
|
+
)}]; tool cost will be estimated.`
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
if (hasFileRef) {
|
|
560
|
+
usage.details["attachment_search_unpinned"] = 1;
|
|
561
|
+
warnings.push({
|
|
562
|
+
type: "other",
|
|
563
|
+
message: "xai: file-ref enables attachment_search but that counter is not live-pinned; tool cost is estimated."
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
const citations = collectXaiCitations(response, messageItems);
|
|
377
568
|
const providerMeta = {};
|
|
378
569
|
const contextDetails = response.usage["context_details"];
|
|
379
570
|
if (isPlainRecord(contextDetails)) {
|
|
@@ -393,12 +584,159 @@ function xaiAdapter(opts) {
|
|
|
393
584
|
...reasoningText !== void 0 ? { reasoningText } : {},
|
|
394
585
|
...rawStructured !== void 0 ? { rawStructured } : {},
|
|
395
586
|
...servedServiceTier !== void 0 ? { servedServiceTier } : {},
|
|
396
|
-
...Object.keys(providerMeta).length > 0 ? { providerMetadata: providerMeta } : {}
|
|
587
|
+
...Object.keys(providerMeta).length > 0 ? { providerMetadata: providerMeta } : {},
|
|
588
|
+
...citations.length > 0 ? { citations } : {},
|
|
589
|
+
...toolCalls.length > 0 ? { toolCalls, finishReason: "tool_calls" } : {}
|
|
397
590
|
};
|
|
398
591
|
return result;
|
|
592
|
+
},
|
|
593
|
+
async countTokens(req, ctx) {
|
|
594
|
+
if (req.provider !== "xai") {
|
|
595
|
+
throw new LlmError(
|
|
596
|
+
`xaiAdapter received a request for provider "${req.provider}", expected "xai".`,
|
|
597
|
+
{ kind: "bad_request", retryable: false }
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
if (req.tools !== void 0 && req.tools.length > 0) {
|
|
601
|
+
throw badXaiRequest(
|
|
602
|
+
"xAI countTokens rejects tools; tokenize-text cannot represent tool declarations."
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
const text = concatenateTokenizeText(req);
|
|
606
|
+
const apiKey = requireApiKey(ctx.auth);
|
|
607
|
+
const fetchImpl = opts?._fetch ?? fetch;
|
|
608
|
+
try {
|
|
609
|
+
const res = await fetchImpl("https://api.x.ai/v1/tokenize-text", {
|
|
610
|
+
method: "POST",
|
|
611
|
+
headers: {
|
|
612
|
+
Authorization: `Bearer ${apiKey}`,
|
|
613
|
+
"Content-Type": "application/json"
|
|
614
|
+
},
|
|
615
|
+
body: JSON.stringify({ model: req.model, text }),
|
|
616
|
+
...ctx.signal !== void 0 ? { signal: ctx.signal } : {}
|
|
617
|
+
});
|
|
618
|
+
if (!res.ok) {
|
|
619
|
+
let parsed;
|
|
620
|
+
try {
|
|
621
|
+
parsed = await res.json();
|
|
622
|
+
} catch {
|
|
623
|
+
parsed = await res.text().catch(() => "");
|
|
624
|
+
}
|
|
625
|
+
throw Object.assign(new Error(`xAI tokenize-text HTTP ${res.status}`), {
|
|
626
|
+
status: res.status,
|
|
627
|
+
error: parsed
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
const raw = await res.json();
|
|
631
|
+
if (!isPlainRecord(raw) || !Array.isArray(raw["token_ids"])) {
|
|
632
|
+
throw new LlmError(
|
|
633
|
+
"xAI tokenize-text response is malformed: missing required field: token_ids",
|
|
634
|
+
{ kind: "server", retryable: true, provider: "xai" }
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
const n = raw["token_ids"].length;
|
|
638
|
+
return {
|
|
639
|
+
totalTokens: n,
|
|
640
|
+
accuracy: "lower-bound",
|
|
641
|
+
details: { textTokens: n },
|
|
642
|
+
raw
|
|
643
|
+
};
|
|
644
|
+
} catch (rawErr) {
|
|
645
|
+
if (rawErr instanceof Error && rawErr.name === "AbortError") {
|
|
646
|
+
throw new LlmError("xAI tokenize-text aborted", {
|
|
647
|
+
kind: "aborted",
|
|
648
|
+
retryable: false,
|
|
649
|
+
provider: "xai",
|
|
650
|
+
cause: rawErr
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
throw classifyXaiError(rawErr);
|
|
654
|
+
}
|
|
399
655
|
}
|
|
400
656
|
};
|
|
401
657
|
}
|
|
658
|
+
var WEB_SEARCH_COUNTER = "web_search_calls";
|
|
659
|
+
var X_SEARCH_COUNTER = "x_search_calls";
|
|
660
|
+
function expectedServerToolCounters(tools, _hasFileRef) {
|
|
661
|
+
const keys = [];
|
|
662
|
+
if (tools !== void 0) {
|
|
663
|
+
for (const tool of tools) {
|
|
664
|
+
if (tool["type"] === "web_search") keys.push(WEB_SEARCH_COUNTER);
|
|
665
|
+
if (tool["type"] === "x_search") keys.push(X_SEARCH_COUNTER);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return keys;
|
|
669
|
+
}
|
|
670
|
+
function collectXaiCitations(response, messageItems) {
|
|
671
|
+
const seen = /* @__PURE__ */ new Set();
|
|
672
|
+
const citations = [];
|
|
673
|
+
const push = (url, title) => {
|
|
674
|
+
if (typeof url !== "string" || url.length === 0) return;
|
|
675
|
+
if (seen.has(url)) return;
|
|
676
|
+
seen.add(url);
|
|
677
|
+
const citation = { url };
|
|
678
|
+
if (typeof title === "string" && title.length > 0 && title !== url) {
|
|
679
|
+
citation.title = title;
|
|
680
|
+
}
|
|
681
|
+
try {
|
|
682
|
+
const parsed = new URL(url);
|
|
683
|
+
if (parsed.hostname.length > 0) {
|
|
684
|
+
citation.sourceName = parsed.hostname.startsWith("www.") ? parsed.hostname.slice(4) : parsed.hostname;
|
|
685
|
+
}
|
|
686
|
+
} catch {
|
|
687
|
+
}
|
|
688
|
+
citations.push(citation);
|
|
689
|
+
};
|
|
690
|
+
if (Array.isArray(response.citations)) {
|
|
691
|
+
for (const item of response.citations) {
|
|
692
|
+
if (typeof item === "string") {
|
|
693
|
+
push(item, void 0);
|
|
694
|
+
} else if (isPlainRecord(item)) {
|
|
695
|
+
push(item["url"] ?? item["uri"], item["title"]);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
const lastMessage = messageItems.at(-1);
|
|
700
|
+
const citationMessages = lastMessage === void 0 ? [] : [lastMessage];
|
|
701
|
+
for (const item of citationMessages) {
|
|
702
|
+
for (const part of item.content) {
|
|
703
|
+
const annotations = part.annotations;
|
|
704
|
+
if (!Array.isArray(annotations)) continue;
|
|
705
|
+
for (const ann of annotations) {
|
|
706
|
+
if (!isPlainRecord(ann)) continue;
|
|
707
|
+
if (ann["type"] !== void 0 && ann["type"] !== "url_citation") continue;
|
|
708
|
+
push(ann["url"], ann["title"]);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return citations;
|
|
713
|
+
}
|
|
714
|
+
function concatenateTokenizeText(req) {
|
|
715
|
+
const chunks = [];
|
|
716
|
+
if (req.system !== void 0 && req.system.length > 0) {
|
|
717
|
+
chunks.push(req.system);
|
|
718
|
+
}
|
|
719
|
+
for (const message of req.messages) {
|
|
720
|
+
for (const part of message.parts) {
|
|
721
|
+
switch (part.kind) {
|
|
722
|
+
case "text":
|
|
723
|
+
chunks.push(part.text);
|
|
724
|
+
break;
|
|
725
|
+
case "inline-media":
|
|
726
|
+
case "file-uri":
|
|
727
|
+
case "file-ref":
|
|
728
|
+
case "tool-call":
|
|
729
|
+
case "tool-result":
|
|
730
|
+
throw badXaiRequest(
|
|
731
|
+
`xAI countTokens rejects ${part.kind} parts; tokenize-text is text-only.`
|
|
732
|
+
);
|
|
733
|
+
default:
|
|
734
|
+
return assertNever(part);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return chunks.join("\n");
|
|
739
|
+
}
|
|
402
740
|
var XAI_FILE_TTL_MIN_SECONDS = 3600;
|
|
403
741
|
var XAI_FILE_TTL_MAX_SECONDS = 2592e3;
|
|
404
742
|
var XAI_FILE_MAX_BYTES = 48 * 1024 * 1024;
|
|
@@ -854,6 +1192,100 @@ var XaiFileStore = class {
|
|
|
854
1192
|
return new Uint8Array(buf);
|
|
855
1193
|
}
|
|
856
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
|
|
857
1289
|
var Grok45ConfigSchema = z.strictObject({
|
|
858
1290
|
temperature: z.number().optional().meta({
|
|
859
1291
|
title: "Temperature",
|
|
@@ -868,9 +1300,9 @@ var Grok45ConfigSchema = z.strictObject({
|
|
|
868
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."
|
|
869
1301
|
}),
|
|
870
1302
|
reasoning: z.strictObject({
|
|
871
|
-
effort: z.enum(["low", "high"]).meta({
|
|
1303
|
+
effort: z.enum(["low", "medium", "high"]).meta({
|
|
872
1304
|
title: "Reasoning Effort",
|
|
873
|
-
description: 'Reasoning effort for grok-4.5.
|
|
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".'
|
|
874
1306
|
})
|
|
875
1307
|
}).optional().meta({
|
|
876
1308
|
title: "Reasoning",
|
|
@@ -881,22 +1313,14 @@ var Grok45ConfigSchema = z.strictObject({
|
|
|
881
1313
|
description: "Logical request timeout in milliseconds."
|
|
882
1314
|
}),
|
|
883
1315
|
providerOptions: z.strictObject({
|
|
884
|
-
xai:
|
|
885
|
-
promptCacheKey: z.string().min(1).optional().meta({
|
|
886
|
-
title: "Prompt Cache Key",
|
|
887
|
-
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
888
|
-
})
|
|
889
|
-
}).optional().meta({
|
|
890
|
-
title: "xAI Provider Options",
|
|
891
|
-
description: "Allowlisted xAI provider options for grok-4.5."
|
|
892
|
-
})
|
|
1316
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
893
1317
|
}).optional().meta({
|
|
894
1318
|
title: "Provider Options",
|
|
895
1319
|
description: "Provider-specific options accepted for grok-4.5."
|
|
896
1320
|
})
|
|
897
1321
|
}).meta({
|
|
898
1322
|
title: "Grok45Config",
|
|
899
|
-
description: "Strict Responses API config for model grok-4.5. Level reasoning (low/high
|
|
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.",
|
|
900
1324
|
examples: [{ reasoning: { effort: "high" } }]
|
|
901
1325
|
});
|
|
902
1326
|
var Grok46ConfigSchema = z.strictObject({
|
|
@@ -930,15 +1354,7 @@ var Grok46ConfigSchema = z.strictObject({
|
|
|
930
1354
|
description: "Logical request timeout in milliseconds."
|
|
931
1355
|
}),
|
|
932
1356
|
providerOptions: z.strictObject({
|
|
933
|
-
xai:
|
|
934
|
-
promptCacheKey: z.string().min(1).optional().meta({
|
|
935
|
-
title: "Prompt Cache Key",
|
|
936
|
-
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
937
|
-
})
|
|
938
|
-
}).optional().meta({
|
|
939
|
-
title: "xAI Provider Options",
|
|
940
|
-
description: "Allowlisted xAI provider options for grok-4.6."
|
|
941
|
-
})
|
|
1357
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
942
1358
|
}).optional().meta({
|
|
943
1359
|
title: "Provider Options",
|
|
944
1360
|
description: "Provider-specific options accepted for grok-4.6."
|
|
@@ -957,14 +1373,15 @@ var grok45ModelDescriptor = {
|
|
|
957
1373
|
capabilities: {
|
|
958
1374
|
reasoning: true,
|
|
959
1375
|
reasoningApi: "level",
|
|
960
|
-
admittedReasoningEfforts: ["low", "high"],
|
|
1376
|
+
admittedReasoningEfforts: ["low", "medium", "high"],
|
|
961
1377
|
structuredOutput: true,
|
|
962
1378
|
nativeStructuredOutput: true,
|
|
963
1379
|
vision: true,
|
|
964
1380
|
audioInput: false,
|
|
965
1381
|
sampling: "tunable",
|
|
966
1382
|
caching: { explicit: false, minTokens: 0 },
|
|
967
|
-
grounding:
|
|
1383
|
+
grounding: true,
|
|
1384
|
+
functionCalling: true
|
|
968
1385
|
// No serviceTiers key — grok-4.5 has no admitted service-tier vocabulary.
|
|
969
1386
|
},
|
|
970
1387
|
configSchema: Grok45ConfigSchema,
|
|
@@ -985,7 +1402,8 @@ var grok46ModelDescriptor = {
|
|
|
985
1402
|
audioInput: false,
|
|
986
1403
|
sampling: "tunable",
|
|
987
1404
|
caching: { explicit: false, minTokens: 0 },
|
|
988
|
-
grounding:
|
|
1405
|
+
grounding: true,
|
|
1406
|
+
functionCalling: true,
|
|
989
1407
|
serviceTiers: ["priority"]
|
|
990
1408
|
},
|
|
991
1409
|
configSchema: Grok46ConfigSchema,
|
|
@@ -999,7 +1417,12 @@ var xaiModelDescriptors = [
|
|
|
999
1417
|
var xaiRegistry = createModelRegistry(xaiModelDescriptors);
|
|
1000
1418
|
|
|
1001
1419
|
// src/pricing.ts
|
|
1002
|
-
var xaiPricingVersion = "xai-2026-08-
|
|
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"];
|
|
1003
1426
|
var XAI_PRICING = Object.freeze({
|
|
1004
1427
|
// ── grok-4.5 ── $2.00/$6.00 (≤200k), $4.00/$12.00 (>200k); cached $0.30/$0.60
|
|
1005
1428
|
"grok-4.5": {
|
|
@@ -1048,7 +1471,7 @@ function computeXaiCost(model, usage, tier) {
|
|
|
1048
1471
|
usd: null,
|
|
1049
1472
|
pricingVersion: xaiPricingVersion,
|
|
1050
1473
|
confidence: "estimated",
|
|
1051
|
-
details: { input: 0, cached: 0, output: 0 },
|
|
1474
|
+
details: { input: 0, cached: 0, output: 0, tools: 0 },
|
|
1052
1475
|
unpricedReason: `Unknown model "${model}"; no pricing entry found.`
|
|
1053
1476
|
};
|
|
1054
1477
|
}
|
|
@@ -1062,7 +1485,7 @@ function computeXaiCost(model, usage, tier) {
|
|
|
1062
1485
|
usd: null,
|
|
1063
1486
|
pricingVersion: xaiPricingVersion,
|
|
1064
1487
|
confidence: "estimated",
|
|
1065
|
-
details: { input: 0, cached: 0, output: 0 },
|
|
1488
|
+
details: { input: 0, cached: 0, output: 0, tools: 0 },
|
|
1066
1489
|
unpricedReason: `Unknown service tier "${tier}"; xai model "${model}" has no such tier, refusing to guess a pricing multiplier.`
|
|
1067
1490
|
};
|
|
1068
1491
|
}
|
|
@@ -1075,16 +1498,25 @@ function computeXaiCost(model, usage, tier) {
|
|
|
1075
1498
|
const outputCost = Math.round(
|
|
1076
1499
|
usage.outputTokens * base.outputPerM * factor / 1e6
|
|
1077
1500
|
);
|
|
1078
|
-
const
|
|
1501
|
+
const serverToolsRequested = usage.details["server_tools_requested"] === 1;
|
|
1502
|
+
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);
|
|
1503
|
+
const attachmentUnpinned = usage.details["attachment_search_unpinned"] === 1;
|
|
1504
|
+
const toolsCost = missingRequestedCounters ? 0 : XAI_TOOL_COUNTER_KEYS.reduce((sum, key) => {
|
|
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;
|
|
1079
1510
|
return {
|
|
1080
1511
|
microUsd,
|
|
1081
1512
|
usd: microUsd / 1e6,
|
|
1082
1513
|
pricingVersion: xaiPricingVersion,
|
|
1083
|
-
confidence: "exact",
|
|
1514
|
+
confidence: missingRequestedCounters || attachmentUnpinned ? "estimated" : "exact",
|
|
1084
1515
|
details: {
|
|
1085
1516
|
input: inputCost,
|
|
1086
1517
|
cached: cachedCost,
|
|
1087
|
-
output: outputCost
|
|
1518
|
+
output: outputCost,
|
|
1519
|
+
tools: toolsCost
|
|
1088
1520
|
}
|
|
1089
1521
|
};
|
|
1090
1522
|
}
|
|
@@ -1112,6 +1544,6 @@ function xaiProvider(opts) {
|
|
|
1112
1544
|
};
|
|
1113
1545
|
}
|
|
1114
1546
|
|
|
1115
|
-
export { Grok45ConfigSchema, Grok46ConfigSchema, XAI_FILES_DEFAULT_BASE_URL, XAI_FILE_MAX_BYTES, XAI_FILE_TTL_MAX_SECONDS, XAI_FILE_TTL_MIN_SECONDS, XAI_PRICING, XaiFileStore, buildXaiClient, classifyXaiError, computeXaiCost, grok45ModelDescriptor, grok46ModelDescriptor, requireApiKey, xaiAdapter, xaiModelDescriptors, xaiPricingSource, xaiPricingVersion, xaiProvider, xaiRegistry };
|
|
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 };
|
|
1116
1548
|
//# sourceMappingURL=index.js.map
|
|
1117
1549
|
//# sourceMappingURL=index.js.map
|