@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.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,
|
|
@@ -171,6 +254,11 @@ function isXaiAuthFailureBody(rawErr) {
|
|
|
171
254
|
const text = extractXaiErrorBodyText(rawErr);
|
|
172
255
|
return text !== void 0 && text.startsWith(XAI_AUTH_ERROR_MESSAGE_PREFIX);
|
|
173
256
|
}
|
|
257
|
+
var XAI_SAFETY_CHECK_MESSAGE_PREFIX = "Content violates usage guidelines";
|
|
258
|
+
function isXaiSafetyCheckBody(rawErr) {
|
|
259
|
+
const text = extractXaiErrorBodyText(rawErr);
|
|
260
|
+
return text !== void 0 && text.startsWith(XAI_SAFETY_CHECK_MESSAGE_PREFIX);
|
|
261
|
+
}
|
|
174
262
|
var XAI_TRANSPORT_ERROR_PATTERN = /connection error|econnreset|econnrefused|etimedout|eai_again|epipe|socket hang up|fetch failed/i;
|
|
175
263
|
function matchesXaiTransportSignature(err) {
|
|
176
264
|
if (!(err instanceof Error)) return false;
|
|
@@ -209,6 +297,16 @@ function classifyXaiError(rawErr) {
|
|
|
209
297
|
cause: base.cause ?? rawErr
|
|
210
298
|
});
|
|
211
299
|
}
|
|
300
|
+
if (base.httpStatus === 403 && isXaiSafetyCheckBody(rawErr)) {
|
|
301
|
+
const bodyText = extractXaiErrorBodyText(rawErr);
|
|
302
|
+
return new LlmError(bodyText ?? base.message, {
|
|
303
|
+
kind: "content_filter",
|
|
304
|
+
retryable: false,
|
|
305
|
+
httpStatus: base.httpStatus,
|
|
306
|
+
provider: "xai",
|
|
307
|
+
cause: base.cause ?? rawErr
|
|
308
|
+
});
|
|
309
|
+
}
|
|
212
310
|
if (base.kind === "unknown" && isXaiTransportError(rawErr)) {
|
|
213
311
|
return new LlmError(base.message, {
|
|
214
312
|
kind: "server",
|
|
@@ -239,10 +337,48 @@ function xaiAdapter(opts) {
|
|
|
239
337
|
const warnings = [];
|
|
240
338
|
const model = req.model;
|
|
241
339
|
const genConfig = req.config;
|
|
242
|
-
const input =
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
+
}
|
|
246
382
|
const params = {
|
|
247
383
|
model,
|
|
248
384
|
input,
|
|
@@ -312,6 +448,38 @@ function xaiAdapter(opts) {
|
|
|
312
448
|
if (xaiProviderConfig.promptCacheKey !== void 0) {
|
|
313
449
|
params.prompt_cache_key = xaiProviderConfig.promptCacheKey;
|
|
314
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
|
+
}
|
|
315
483
|
let response;
|
|
316
484
|
try {
|
|
317
485
|
const buildClient = opts?._clientFactory ?? buildXaiClient;
|
|
@@ -330,14 +498,29 @@ function xaiAdapter(opts) {
|
|
|
330
498
|
let text = "";
|
|
331
499
|
let reasoningText;
|
|
332
500
|
const messageItems = [];
|
|
501
|
+
const toolCalls = [];
|
|
333
502
|
for (const item of response.output) {
|
|
334
|
-
if (item
|
|
503
|
+
if (isXaiMessageItem(item)) {
|
|
335
504
|
messageItems.push(item);
|
|
336
|
-
} else {
|
|
505
|
+
} else if (isXaiReasoningItem(item)) {
|
|
337
506
|
const joined = item.summary.map((s) => s.text).join("");
|
|
338
507
|
if (joined.length > 0) {
|
|
339
508
|
reasoningText = (reasoningText ?? "") + joined;
|
|
340
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
|
+
}
|
|
341
524
|
}
|
|
342
525
|
}
|
|
343
526
|
if (messageItems.length > 0) {
|
|
@@ -359,6 +542,22 @@ function xaiAdapter(opts) {
|
|
|
359
542
|
}
|
|
360
543
|
const usage = mapUsage(response.usage);
|
|
361
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
|
+
}
|
|
560
|
+
const citations = collectXaiCitations(response, messageItems);
|
|
362
561
|
const providerMeta = {};
|
|
363
562
|
const contextDetails = response.usage["context_details"];
|
|
364
563
|
if (isPlainRecord(contextDetails)) {
|
|
@@ -378,12 +577,159 @@ function xaiAdapter(opts) {
|
|
|
378
577
|
...reasoningText !== void 0 ? { reasoningText } : {},
|
|
379
578
|
...rawStructured !== void 0 ? { rawStructured } : {},
|
|
380
579
|
...servedServiceTier !== void 0 ? { servedServiceTier } : {},
|
|
381
|
-
...Object.keys(providerMeta).length > 0 ? { providerMetadata: providerMeta } : {}
|
|
580
|
+
...Object.keys(providerMeta).length > 0 ? { providerMetadata: providerMeta } : {},
|
|
581
|
+
...citations.length > 0 ? { citations } : {},
|
|
582
|
+
...toolCalls.length > 0 ? { toolCalls, finishReason: "tool_calls" } : {}
|
|
382
583
|
};
|
|
383
584
|
return result;
|
|
585
|
+
},
|
|
586
|
+
async countTokens(req, ctx) {
|
|
587
|
+
if (req.provider !== "xai") {
|
|
588
|
+
throw new LlmError(
|
|
589
|
+
`xaiAdapter received a request for provider "${req.provider}", expected "xai".`,
|
|
590
|
+
{ kind: "bad_request", retryable: false }
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
if (req.tools !== void 0 && req.tools.length > 0) {
|
|
594
|
+
throw badXaiRequest(
|
|
595
|
+
"xAI countTokens rejects tools; tokenize-text cannot represent tool declarations."
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
const text = concatenateTokenizeText(req);
|
|
599
|
+
const apiKey = requireApiKey(ctx.auth);
|
|
600
|
+
const fetchImpl = opts?._fetch ?? fetch;
|
|
601
|
+
try {
|
|
602
|
+
const res = await fetchImpl("https://api.x.ai/v1/tokenize-text", {
|
|
603
|
+
method: "POST",
|
|
604
|
+
headers: {
|
|
605
|
+
Authorization: `Bearer ${apiKey}`,
|
|
606
|
+
"Content-Type": "application/json"
|
|
607
|
+
},
|
|
608
|
+
body: JSON.stringify({ model: req.model, text }),
|
|
609
|
+
...ctx.signal !== void 0 ? { signal: ctx.signal } : {}
|
|
610
|
+
});
|
|
611
|
+
if (!res.ok) {
|
|
612
|
+
let parsed;
|
|
613
|
+
try {
|
|
614
|
+
parsed = await res.json();
|
|
615
|
+
} catch {
|
|
616
|
+
parsed = await res.text().catch(() => "");
|
|
617
|
+
}
|
|
618
|
+
throw Object.assign(new Error(`xAI tokenize-text HTTP ${res.status}`), {
|
|
619
|
+
status: res.status,
|
|
620
|
+
error: parsed
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
const raw = await res.json();
|
|
624
|
+
if (!isPlainRecord(raw) || !Array.isArray(raw["token_ids"])) {
|
|
625
|
+
throw new LlmError(
|
|
626
|
+
"xAI tokenize-text response is malformed: missing required field: token_ids",
|
|
627
|
+
{ kind: "server", retryable: true, provider: "xai" }
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
const n = raw["token_ids"].length;
|
|
631
|
+
return {
|
|
632
|
+
totalTokens: n,
|
|
633
|
+
accuracy: "lower-bound",
|
|
634
|
+
details: { textTokens: n },
|
|
635
|
+
raw
|
|
636
|
+
};
|
|
637
|
+
} catch (rawErr) {
|
|
638
|
+
if (rawErr instanceof Error && rawErr.name === "AbortError") {
|
|
639
|
+
throw new LlmError("xAI tokenize-text aborted", {
|
|
640
|
+
kind: "aborted",
|
|
641
|
+
retryable: false,
|
|
642
|
+
provider: "xai",
|
|
643
|
+
cause: rawErr
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
throw classifyXaiError(rawErr);
|
|
647
|
+
}
|
|
384
648
|
}
|
|
385
649
|
};
|
|
386
650
|
}
|
|
651
|
+
var WEB_SEARCH_COUNTER = "web_search_calls";
|
|
652
|
+
var X_SEARCH_COUNTER = "x_search_calls";
|
|
653
|
+
function expectedServerToolCounters(tools, _hasFileRef) {
|
|
654
|
+
const keys = [];
|
|
655
|
+
if (tools !== void 0) {
|
|
656
|
+
for (const tool of tools) {
|
|
657
|
+
if (tool["type"] === "web_search") keys.push(WEB_SEARCH_COUNTER);
|
|
658
|
+
if (tool["type"] === "x_search") keys.push(X_SEARCH_COUNTER);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return keys;
|
|
662
|
+
}
|
|
663
|
+
function collectXaiCitations(response, messageItems) {
|
|
664
|
+
const seen = /* @__PURE__ */ new Set();
|
|
665
|
+
const citations = [];
|
|
666
|
+
const push = (url, title) => {
|
|
667
|
+
if (typeof url !== "string" || url.length === 0) return;
|
|
668
|
+
if (seen.has(url)) return;
|
|
669
|
+
seen.add(url);
|
|
670
|
+
const citation = { url };
|
|
671
|
+
if (typeof title === "string" && title.length > 0 && title !== url) {
|
|
672
|
+
citation.title = title;
|
|
673
|
+
}
|
|
674
|
+
try {
|
|
675
|
+
const parsed = new URL(url);
|
|
676
|
+
if (parsed.hostname.length > 0) {
|
|
677
|
+
citation.sourceName = parsed.hostname.startsWith("www.") ? parsed.hostname.slice(4) : parsed.hostname;
|
|
678
|
+
}
|
|
679
|
+
} catch {
|
|
680
|
+
}
|
|
681
|
+
citations.push(citation);
|
|
682
|
+
};
|
|
683
|
+
if (Array.isArray(response.citations)) {
|
|
684
|
+
for (const item of response.citations) {
|
|
685
|
+
if (typeof item === "string") {
|
|
686
|
+
push(item, void 0);
|
|
687
|
+
} else if (isPlainRecord(item)) {
|
|
688
|
+
push(item["url"] ?? item["uri"], item["title"]);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
const lastMessage = messageItems.at(-1);
|
|
693
|
+
const citationMessages = lastMessage === void 0 ? [] : [lastMessage];
|
|
694
|
+
for (const item of citationMessages) {
|
|
695
|
+
for (const part of item.content) {
|
|
696
|
+
const annotations = part.annotations;
|
|
697
|
+
if (!Array.isArray(annotations)) continue;
|
|
698
|
+
for (const ann of annotations) {
|
|
699
|
+
if (!isPlainRecord(ann)) continue;
|
|
700
|
+
if (ann["type"] !== void 0 && ann["type"] !== "url_citation") continue;
|
|
701
|
+
push(ann["url"], ann["title"]);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
return citations;
|
|
706
|
+
}
|
|
707
|
+
function concatenateTokenizeText(req) {
|
|
708
|
+
const chunks = [];
|
|
709
|
+
if (req.system !== void 0 && req.system.length > 0) {
|
|
710
|
+
chunks.push(req.system);
|
|
711
|
+
}
|
|
712
|
+
for (const message of req.messages) {
|
|
713
|
+
for (const part of message.parts) {
|
|
714
|
+
switch (part.kind) {
|
|
715
|
+
case "text":
|
|
716
|
+
chunks.push(part.text);
|
|
717
|
+
break;
|
|
718
|
+
case "inline-media":
|
|
719
|
+
case "file-uri":
|
|
720
|
+
case "file-ref":
|
|
721
|
+
case "tool-call":
|
|
722
|
+
case "tool-result":
|
|
723
|
+
throw badXaiRequest(
|
|
724
|
+
`xAI countTokens rejects ${part.kind} parts; tokenize-text is text-only.`
|
|
725
|
+
);
|
|
726
|
+
default:
|
|
727
|
+
return assertNever(part);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return chunks.join("\n");
|
|
732
|
+
}
|
|
387
733
|
var XAI_FILE_TTL_MIN_SECONDS = 3600;
|
|
388
734
|
var XAI_FILE_TTL_MAX_SECONDS = 2592e3;
|
|
389
735
|
var XAI_FILE_MAX_BYTES = 48 * 1024 * 1024;
|
|
@@ -839,6 +1185,100 @@ var XaiFileStore = class {
|
|
|
839
1185
|
return new Uint8Array(buf);
|
|
840
1186
|
}
|
|
841
1187
|
};
|
|
1188
|
+
var webSearchFlags = {
|
|
1189
|
+
enableImageUnderstanding: z.boolean().optional().meta({
|
|
1190
|
+
title: "Enable Image Understanding",
|
|
1191
|
+
description: "Ask xAI to analyze images found during web search."
|
|
1192
|
+
}),
|
|
1193
|
+
enableImageSearch: z.boolean().optional().meta({
|
|
1194
|
+
title: "Enable Image Search",
|
|
1195
|
+
description: "Ask xAI to include image search results."
|
|
1196
|
+
})
|
|
1197
|
+
};
|
|
1198
|
+
var XaiWebSearchToolSchema = z.union([
|
|
1199
|
+
z.strictObject({
|
|
1200
|
+
type: z.literal("web_search"),
|
|
1201
|
+
allowedDomains: z.array(z.string()).max(5),
|
|
1202
|
+
...webSearchFlags
|
|
1203
|
+
}),
|
|
1204
|
+
z.strictObject({
|
|
1205
|
+
type: z.literal("web_search"),
|
|
1206
|
+
excludedDomains: z.array(z.string()).max(5),
|
|
1207
|
+
...webSearchFlags
|
|
1208
|
+
}),
|
|
1209
|
+
z.strictObject({
|
|
1210
|
+
type: z.literal("web_search"),
|
|
1211
|
+
...webSearchFlags
|
|
1212
|
+
})
|
|
1213
|
+
]).meta({
|
|
1214
|
+
title: "XaiWebSearchTool",
|
|
1215
|
+
description: "xAI web_search server tool. allowedDomains and excludedDomains are mutually exclusive (max 5)."
|
|
1216
|
+
});
|
|
1217
|
+
var xSearchFlags = {
|
|
1218
|
+
fromDate: z.iso.date().optional().meta({
|
|
1219
|
+
title: "From Date",
|
|
1220
|
+
description: "Inclusive ISO-8601 date lower bound for X search."
|
|
1221
|
+
}),
|
|
1222
|
+
toDate: z.iso.date().optional().meta({
|
|
1223
|
+
title: "To Date",
|
|
1224
|
+
description: "Inclusive ISO-8601 date upper bound for X search."
|
|
1225
|
+
}),
|
|
1226
|
+
enableImageUnderstanding: z.boolean().optional().meta({
|
|
1227
|
+
title: "Enable Image Understanding",
|
|
1228
|
+
description: "Ask xAI to analyze images in matched posts."
|
|
1229
|
+
}),
|
|
1230
|
+
enableVideoUnderstanding: z.boolean().optional().meta({
|
|
1231
|
+
title: "Enable Video Understanding",
|
|
1232
|
+
description: "Ask xAI to analyze videos in matched posts."
|
|
1233
|
+
})
|
|
1234
|
+
};
|
|
1235
|
+
var XaiXSearchToolSchema = z.union([
|
|
1236
|
+
z.strictObject({
|
|
1237
|
+
type: z.literal("x_search"),
|
|
1238
|
+
allowedXHandles: z.array(z.string()).max(20),
|
|
1239
|
+
...xSearchFlags
|
|
1240
|
+
}),
|
|
1241
|
+
z.strictObject({
|
|
1242
|
+
type: z.literal("x_search"),
|
|
1243
|
+
excludedXHandles: z.array(z.string()).max(20),
|
|
1244
|
+
...xSearchFlags
|
|
1245
|
+
}),
|
|
1246
|
+
z.strictObject({
|
|
1247
|
+
type: z.literal("x_search"),
|
|
1248
|
+
...xSearchFlags
|
|
1249
|
+
})
|
|
1250
|
+
]).meta({
|
|
1251
|
+
title: "XaiXSearchTool",
|
|
1252
|
+
description: "xAI x_search server tool. allowedXHandles and excludedXHandles are mutually exclusive (max 20)."
|
|
1253
|
+
});
|
|
1254
|
+
var XaiToolsSchema = z.union([
|
|
1255
|
+
z.tuple([XaiWebSearchToolSchema]),
|
|
1256
|
+
z.tuple([XaiXSearchToolSchema]),
|
|
1257
|
+
z.tuple([XaiWebSearchToolSchema, XaiXSearchToolSchema]),
|
|
1258
|
+
z.tuple([XaiXSearchToolSchema, XaiWebSearchToolSchema])
|
|
1259
|
+
]).meta({
|
|
1260
|
+
title: "XaiTools",
|
|
1261
|
+
description: "xAI Live Search tools. At most one web_search and at most one x_search."
|
|
1262
|
+
});
|
|
1263
|
+
var XaiProviderOptionsSchema = z.strictObject({
|
|
1264
|
+
promptCacheKey: z.string().min(1).optional().meta({
|
|
1265
|
+
title: "Prompt Cache Key",
|
|
1266
|
+
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
1267
|
+
}),
|
|
1268
|
+
tools: XaiToolsSchema.optional().meta({
|
|
1269
|
+
title: "Live Search Tools",
|
|
1270
|
+
description: "xAI server-side web_search / x_search tools."
|
|
1271
|
+
}),
|
|
1272
|
+
parallelToolCalls: z.boolean().optional().meta({
|
|
1273
|
+
title: "Parallel Tool Calls",
|
|
1274
|
+
description: "xAI Responses parallel_tool_calls. Not a generic contract field."
|
|
1275
|
+
})
|
|
1276
|
+
}).meta({
|
|
1277
|
+
title: "xAI Provider Options",
|
|
1278
|
+
description: "Allowlisted xAI provider options."
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
// src/model-config/grok-4-5.ts
|
|
842
1282
|
var Grok45ConfigSchema = z.strictObject({
|
|
843
1283
|
temperature: z.number().optional().meta({
|
|
844
1284
|
title: "Temperature",
|
|
@@ -853,9 +1293,9 @@ var Grok45ConfigSchema = z.strictObject({
|
|
|
853
1293
|
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."
|
|
854
1294
|
}),
|
|
855
1295
|
reasoning: z.strictObject({
|
|
856
|
-
effort: z.enum(["low", "high"]).meta({
|
|
1296
|
+
effort: z.enum(["low", "medium", "high"]).meta({
|
|
857
1297
|
title: "Reasoning Effort",
|
|
858
|
-
description: 'Reasoning effort for grok-4.5.
|
|
1298
|
+
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".'
|
|
859
1299
|
})
|
|
860
1300
|
}).optional().meta({
|
|
861
1301
|
title: "Reasoning",
|
|
@@ -866,22 +1306,14 @@ var Grok45ConfigSchema = z.strictObject({
|
|
|
866
1306
|
description: "Logical request timeout in milliseconds."
|
|
867
1307
|
}),
|
|
868
1308
|
providerOptions: z.strictObject({
|
|
869
|
-
xai:
|
|
870
|
-
promptCacheKey: z.string().min(1).optional().meta({
|
|
871
|
-
title: "Prompt Cache Key",
|
|
872
|
-
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
873
|
-
})
|
|
874
|
-
}).optional().meta({
|
|
875
|
-
title: "xAI Provider Options",
|
|
876
|
-
description: "Allowlisted xAI provider options for grok-4.5."
|
|
877
|
-
})
|
|
1309
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
878
1310
|
}).optional().meta({
|
|
879
1311
|
title: "Provider Options",
|
|
880
1312
|
description: "Provider-specific options accepted for grok-4.5."
|
|
881
1313
|
})
|
|
882
1314
|
}).meta({
|
|
883
1315
|
title: "Grok45Config",
|
|
884
|
-
description: "Strict Responses API config for model grok-4.5. Level reasoning (low/high
|
|
1316
|
+
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.",
|
|
885
1317
|
examples: [{ reasoning: { effort: "high" } }]
|
|
886
1318
|
});
|
|
887
1319
|
var Grok46ConfigSchema = z.strictObject({
|
|
@@ -915,15 +1347,7 @@ var Grok46ConfigSchema = z.strictObject({
|
|
|
915
1347
|
description: "Logical request timeout in milliseconds."
|
|
916
1348
|
}),
|
|
917
1349
|
providerOptions: z.strictObject({
|
|
918
|
-
xai:
|
|
919
|
-
promptCacheKey: z.string().min(1).optional().meta({
|
|
920
|
-
title: "Prompt Cache Key",
|
|
921
|
-
description: "xAI conversation-routing cache key \u2014 maps to Responses API `prompt_cache_key`."
|
|
922
|
-
})
|
|
923
|
-
}).optional().meta({
|
|
924
|
-
title: "xAI Provider Options",
|
|
925
|
-
description: "Allowlisted xAI provider options for grok-4.6."
|
|
926
|
-
})
|
|
1350
|
+
xai: XaiProviderOptionsSchema.optional()
|
|
927
1351
|
}).optional().meta({
|
|
928
1352
|
title: "Provider Options",
|
|
929
1353
|
description: "Provider-specific options accepted for grok-4.6."
|
|
@@ -942,14 +1366,15 @@ var grok45ModelDescriptor = {
|
|
|
942
1366
|
capabilities: {
|
|
943
1367
|
reasoning: true,
|
|
944
1368
|
reasoningApi: "level",
|
|
945
|
-
admittedReasoningEfforts: ["low", "high"],
|
|
1369
|
+
admittedReasoningEfforts: ["low", "medium", "high"],
|
|
946
1370
|
structuredOutput: true,
|
|
947
1371
|
nativeStructuredOutput: true,
|
|
948
1372
|
vision: true,
|
|
949
1373
|
audioInput: false,
|
|
950
1374
|
sampling: "tunable",
|
|
951
1375
|
caching: { explicit: false, minTokens: 0 },
|
|
952
|
-
grounding:
|
|
1376
|
+
grounding: true,
|
|
1377
|
+
functionCalling: true
|
|
953
1378
|
// No serviceTiers key — grok-4.5 has no admitted service-tier vocabulary.
|
|
954
1379
|
},
|
|
955
1380
|
configSchema: Grok45ConfigSchema,
|
|
@@ -970,7 +1395,8 @@ var grok46ModelDescriptor = {
|
|
|
970
1395
|
audioInput: false,
|
|
971
1396
|
sampling: "tunable",
|
|
972
1397
|
caching: { explicit: false, minTokens: 0 },
|
|
973
|
-
grounding:
|
|
1398
|
+
grounding: true,
|
|
1399
|
+
functionCalling: true,
|
|
974
1400
|
serviceTiers: ["priority"]
|
|
975
1401
|
},
|
|
976
1402
|
configSchema: Grok46ConfigSchema,
|
|
@@ -984,7 +1410,12 @@ var xaiModelDescriptors = [
|
|
|
984
1410
|
var xaiRegistry = createModelRegistry(xaiModelDescriptors);
|
|
985
1411
|
|
|
986
1412
|
// src/pricing.ts
|
|
987
|
-
var xaiPricingVersion = "xai-2026-08-
|
|
1413
|
+
var xaiPricingVersion = "xai-2026-08-24";
|
|
1414
|
+
var XAI_TOOL_RATE_MICRO_USD = {
|
|
1415
|
+
web_search_calls: 5e3,
|
|
1416
|
+
x_search_calls: 5e3
|
|
1417
|
+
};
|
|
1418
|
+
var XAI_TOOL_COUNTER_KEYS = ["web_search_calls", "x_search_calls"];
|
|
988
1419
|
var XAI_PRICING = Object.freeze({
|
|
989
1420
|
// ── grok-4.5 ── $2.00/$6.00 (≤200k), $4.00/$12.00 (>200k); cached $0.30/$0.60
|
|
990
1421
|
"grok-4.5": {
|
|
@@ -1033,7 +1464,7 @@ function computeXaiCost(model, usage, tier) {
|
|
|
1033
1464
|
usd: null,
|
|
1034
1465
|
pricingVersion: xaiPricingVersion,
|
|
1035
1466
|
confidence: "estimated",
|
|
1036
|
-
details: { input: 0, cached: 0, output: 0 },
|
|
1467
|
+
details: { input: 0, cached: 0, output: 0, tools: 0 },
|
|
1037
1468
|
unpricedReason: `Unknown model "${model}"; no pricing entry found.`
|
|
1038
1469
|
};
|
|
1039
1470
|
}
|
|
@@ -1047,7 +1478,7 @@ function computeXaiCost(model, usage, tier) {
|
|
|
1047
1478
|
usd: null,
|
|
1048
1479
|
pricingVersion: xaiPricingVersion,
|
|
1049
1480
|
confidence: "estimated",
|
|
1050
|
-
details: { input: 0, cached: 0, output: 0 },
|
|
1481
|
+
details: { input: 0, cached: 0, output: 0, tools: 0 },
|
|
1051
1482
|
unpricedReason: `Unknown service tier "${tier}"; xai model "${model}" has no such tier, refusing to guess a pricing multiplier.`
|
|
1052
1483
|
};
|
|
1053
1484
|
}
|
|
@@ -1060,16 +1491,24 @@ function computeXaiCost(model, usage, tier) {
|
|
|
1060
1491
|
const outputCost = Math.round(
|
|
1061
1492
|
usage.outputTokens * base.outputPerM * factor / 1e6
|
|
1062
1493
|
);
|
|
1063
|
-
const
|
|
1494
|
+
const serverToolsRequested = usage.details["server_tools_requested"] === 1;
|
|
1495
|
+
const missingRequestedCounters = usage.details["server_tools_missing"] === 1 || serverToolsRequested && !XAI_TOOL_COUNTER_KEYS.some((key) => key in usage.details);
|
|
1496
|
+
const toolsCost = missingRequestedCounters ? 0 : XAI_TOOL_COUNTER_KEYS.reduce((sum, key) => {
|
|
1497
|
+
const count = usage.details[key];
|
|
1498
|
+
if (typeof count !== "number" || count <= 0) return sum;
|
|
1499
|
+
return sum + Math.round(count * XAI_TOOL_RATE_MICRO_USD[key]);
|
|
1500
|
+
}, 0);
|
|
1501
|
+
const microUsd = inputCost + cachedCost + outputCost + toolsCost;
|
|
1064
1502
|
return {
|
|
1065
1503
|
microUsd,
|
|
1066
1504
|
usd: microUsd / 1e6,
|
|
1067
1505
|
pricingVersion: xaiPricingVersion,
|
|
1068
|
-
confidence: "exact",
|
|
1506
|
+
confidence: missingRequestedCounters ? "estimated" : "exact",
|
|
1069
1507
|
details: {
|
|
1070
1508
|
input: inputCost,
|
|
1071
1509
|
cached: cachedCost,
|
|
1072
|
-
output: outputCost
|
|
1510
|
+
output: outputCost,
|
|
1511
|
+
tools: toolsCost
|
|
1073
1512
|
}
|
|
1074
1513
|
};
|
|
1075
1514
|
}
|
|
@@ -1097,6 +1536,6 @@ function xaiProvider(opts) {
|
|
|
1097
1536
|
};
|
|
1098
1537
|
}
|
|
1099
1538
|
|
|
1100
|
-
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 };
|
|
1539
|
+
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 };
|
|
1101
1540
|
//# sourceMappingURL=index.js.map
|
|
1102
1541
|
//# sourceMappingURL=index.js.map
|