@combycode/llm-sdk 1.6.0 → 1.7.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/CHANGELOG.md +103 -0
- package/dist/bus/hook-map.d.ts +11 -0
- package/dist/index.browser.js +179 -70
- package/dist/index.js +179 -70
- package/dist/llm/moderation/native.d.ts +5 -4
- package/dist/llm/providers/google/constants.d.ts +17 -2
- package/dist/llm/providers/openai/completions.d.ts +9 -2
- package/dist/llm/providers/xai/completions.d.ts +2 -2
- package/dist/llm/providers/xai/media.d.ts +8 -0
- package/dist/llm/types/request.d.ts +8 -0
- package/dist/llm/types/tools.d.ts +10 -1
- package/dist/plugins/media/source-image.d.ts +9 -0
- package/dist/plugins/media/types.d.ts +21 -0
- package/dist/plugins/model-catalog/catalog.d.ts +3 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4233,7 +4233,6 @@ var catalog_default2 = {
|
|
|
4233
4233
|
},
|
|
4234
4234
|
imageSize: {
|
|
4235
4235
|
values: [
|
|
4236
|
-
"512",
|
|
4237
4236
|
"1K",
|
|
4238
4237
|
"2K",
|
|
4239
4238
|
"4K"
|
|
@@ -4398,7 +4397,6 @@ var catalog_default2 = {
|
|
|
4398
4397
|
},
|
|
4399
4398
|
imageSize: {
|
|
4400
4399
|
values: [
|
|
4401
|
-
"512",
|
|
4402
4400
|
"1K",
|
|
4403
4401
|
"2K",
|
|
4404
4402
|
"4K"
|
|
@@ -5953,10 +5951,7 @@ var catalog_default2 = {
|
|
|
5953
5951
|
},
|
|
5954
5952
|
imageSize: {
|
|
5955
5953
|
values: [
|
|
5956
|
-
"
|
|
5957
|
-
"1K",
|
|
5958
|
-
"2K",
|
|
5959
|
-
"4K"
|
|
5954
|
+
"1K"
|
|
5960
5955
|
],
|
|
5961
5956
|
default: "1K"
|
|
5962
5957
|
}
|
|
@@ -23423,7 +23418,8 @@ var catalog_default5 = {
|
|
|
23423
23418
|
video: false,
|
|
23424
23419
|
imageGeneration: false,
|
|
23425
23420
|
audioGeneration: false,
|
|
23426
|
-
videoGeneration: true
|
|
23421
|
+
videoGeneration: true,
|
|
23422
|
+
videoExtension: true
|
|
23427
23423
|
},
|
|
23428
23424
|
reasoning: {
|
|
23429
23425
|
supported: false,
|
|
@@ -25106,7 +25102,9 @@ var AnthropicAdapter = class {
|
|
|
25106
25102
|
if (req.thinking.mode === "off") {
|
|
25107
25103
|
} else {
|
|
25108
25104
|
const budget = req.thinking.effort ? ANTHROPIC_THINKING_BUDGETS[req.thinking.effort] ?? DEFAULT_ANTHROPIC_THINKING_BUDGET : DEFAULT_ANTHROPIC_THINKING_BUDGET;
|
|
25109
|
-
|
|
25105
|
+
const thinking = { type: "enabled", budget_tokens: budget };
|
|
25106
|
+
if (req.thinking.visibility === "hidden") thinking.display = "omitted";
|
|
25107
|
+
body.thinking = thinking;
|
|
25110
25108
|
if (body.max_tokens <= budget) body.max_tokens = budget + 1024;
|
|
25111
25109
|
}
|
|
25112
25110
|
}
|
|
@@ -25623,6 +25621,21 @@ var GOOGLE_THINKING_LEVELS = {
|
|
|
25623
25621
|
high: "HIGH",
|
|
25624
25622
|
max: "HIGH"
|
|
25625
25623
|
};
|
|
25624
|
+
var GOOGLE_THINKING_BUDGETS = {
|
|
25625
|
+
low: 2048,
|
|
25626
|
+
medium: 8192,
|
|
25627
|
+
high: 16384,
|
|
25628
|
+
max: 24576
|
|
25629
|
+
};
|
|
25630
|
+
function googleUsesThinkingBudget(model) {
|
|
25631
|
+
return /gemini-2\.5/.test(model);
|
|
25632
|
+
}
|
|
25633
|
+
var GOOGLE_INTERACTION_THINKING_LEVELS = {
|
|
25634
|
+
low: "low",
|
|
25635
|
+
medium: "medium",
|
|
25636
|
+
high: "high",
|
|
25637
|
+
max: "high"
|
|
25638
|
+
};
|
|
25626
25639
|
|
|
25627
25640
|
// src/llm/providers/google/generate.ts
|
|
25628
25641
|
var GoogleAdapter = class {
|
|
@@ -25703,9 +25716,16 @@ var GoogleAdapter = class {
|
|
|
25703
25716
|
body.toolConfig = { functionCallingConfig: { mode } };
|
|
25704
25717
|
}
|
|
25705
25718
|
if (req.thinking && req.thinking.mode !== "off") {
|
|
25706
|
-
|
|
25707
|
-
|
|
25719
|
+
const effort = req.thinking.effort ?? "high";
|
|
25720
|
+
const thinkingConfig = {
|
|
25721
|
+
includeThoughts: req.thinking.visibility !== "hidden"
|
|
25708
25722
|
};
|
|
25723
|
+
if (googleUsesThinkingBudget(req.model)) {
|
|
25724
|
+
thinkingConfig.thinkingBudget = GOOGLE_THINKING_BUDGETS[effort] ?? GOOGLE_THINKING_BUDGETS.high;
|
|
25725
|
+
} else {
|
|
25726
|
+
thinkingConfig.thinkingLevel = GOOGLE_THINKING_LEVELS[effort] ?? "HIGH";
|
|
25727
|
+
}
|
|
25728
|
+
config.thinkingConfig = thinkingConfig;
|
|
25709
25729
|
}
|
|
25710
25730
|
if (req.structured) {
|
|
25711
25731
|
config.responseMimeType = "application/json";
|
|
@@ -25721,6 +25741,9 @@ var GoogleAdapter = class {
|
|
|
25721
25741
|
if (req.providerOptions.imageConfig) {
|
|
25722
25742
|
config.imageConfig = req.providerOptions.imageConfig;
|
|
25723
25743
|
}
|
|
25744
|
+
if (req.providerOptions.translationConfig) {
|
|
25745
|
+
config.translationConfig = req.providerOptions.translationConfig;
|
|
25746
|
+
}
|
|
25724
25747
|
}
|
|
25725
25748
|
return {
|
|
25726
25749
|
body,
|
|
@@ -26071,8 +26094,6 @@ var GoogleInteractionsAdapter = class {
|
|
|
26071
26094
|
if (req.maxTokens) genConfig.max_output_tokens = req.maxTokens;
|
|
26072
26095
|
if (req.temperature !== void 0) genConfig.temperature = req.temperature;
|
|
26073
26096
|
if (req.topP !== void 0) genConfig.top_p = req.topP;
|
|
26074
|
-
if (req.presencePenalty !== void 0) genConfig.presence_penalty = req.presencePenalty;
|
|
26075
|
-
if (req.frequencyPenalty !== void 0) genConfig.frequency_penalty = req.frequencyPenalty;
|
|
26076
26097
|
if (req.stop) genConfig.stop_sequences = req.stop;
|
|
26077
26098
|
if (req.tools?.length) {
|
|
26078
26099
|
body.tools = req.tools.filter(isFunctionTool).map((t) => ({
|
|
@@ -26083,9 +26104,7 @@ var GoogleInteractionsAdapter = class {
|
|
|
26083
26104
|
}));
|
|
26084
26105
|
}
|
|
26085
26106
|
if (req.thinking && req.thinking.mode !== "off") {
|
|
26086
|
-
genConfig.
|
|
26087
|
-
thinking_level: GOOGLE_THINKING_LEVELS[req.thinking.effort ?? "high"] ?? "HIGH"
|
|
26088
|
-
};
|
|
26107
|
+
genConfig.thinking_level = GOOGLE_INTERACTION_THINKING_LEVELS[req.thinking.effort ?? "high"] ?? "high";
|
|
26089
26108
|
}
|
|
26090
26109
|
if (Object.keys(genConfig).length > 0) body.generation_config = genConfig;
|
|
26091
26110
|
const cachedContent = req.providerOptions?.cachedContent;
|
|
@@ -26388,6 +26407,24 @@ function openaiImageRef(ref) {
|
|
|
26388
26407
|
function xaiImageRef(ref) {
|
|
26389
26408
|
return ref.fileId ? { file_id: ref.fileId } : { url: toDataUrl(ref) };
|
|
26390
26409
|
}
|
|
26410
|
+
function xaiVideoRef(src) {
|
|
26411
|
+
switch (src.type) {
|
|
26412
|
+
case "url":
|
|
26413
|
+
return { url: src.url };
|
|
26414
|
+
case "file":
|
|
26415
|
+
return { file_id: src.fileId };
|
|
26416
|
+
case "provider_ref":
|
|
26417
|
+
return { file_id: src.refId };
|
|
26418
|
+
case "base64":
|
|
26419
|
+
return { url: `data:${src.mimeType};base64,${src.data}` };
|
|
26420
|
+
case "buffer":
|
|
26421
|
+
return { url: `data:${src.mimeType};base64,${bytesToBase64(src.data)}` };
|
|
26422
|
+
case "path":
|
|
26423
|
+
throw new Error(
|
|
26424
|
+
"media source video: `path` DataSource is not supported here \u2014 read the file and pass base64/buffer."
|
|
26425
|
+
);
|
|
26426
|
+
}
|
|
26427
|
+
}
|
|
26391
26428
|
function googleImagePart(ref) {
|
|
26392
26429
|
const mimeType = ref.mimeType ?? "image/png";
|
|
26393
26430
|
if (ref.base64) return { inline_data: { mime_type: mimeType, data: ref.base64 } };
|
|
@@ -26474,7 +26511,7 @@ var GoogleMediaAdapter = class {
|
|
|
26474
26511
|
const image = {};
|
|
26475
26512
|
if (req.params?.aspectRatio) image.aspectRatio = req.params.aspectRatio;
|
|
26476
26513
|
if (req.params?.imageSize) image.imageSize = req.params.imageSize;
|
|
26477
|
-
if (Object.keys(image).length) generationConfig.
|
|
26514
|
+
if (Object.keys(image).length) generationConfig.imageConfig = image;
|
|
26478
26515
|
const { items, usage } = await this.generateContentMedia(
|
|
26479
26516
|
model,
|
|
26480
26517
|
req.prompt,
|
|
@@ -26538,7 +26575,7 @@ var GoogleMediaAdapter = class {
|
|
|
26538
26575
|
const image = {};
|
|
26539
26576
|
if (req.params?.aspectRatio) image.aspectRatio = req.params.aspectRatio;
|
|
26540
26577
|
if (req.params?.imageSize) image.imageSize = req.params.imageSize;
|
|
26541
|
-
if (Object.keys(image).length) generationConfig.
|
|
26578
|
+
if (Object.keys(image).length) generationConfig.imageConfig = image;
|
|
26542
26579
|
const imagePart = googleImagePart(normalizeImageSource(req.sourceImage));
|
|
26543
26580
|
const { items, usage } = await this.generateContentMedia(
|
|
26544
26581
|
model,
|
|
@@ -26942,8 +26979,10 @@ var OpenAIBatchAdapter = class {
|
|
|
26942
26979
|
};
|
|
26943
26980
|
|
|
26944
26981
|
// src/llm/moderation/native.ts
|
|
26945
|
-
function buildNativeModeration(mod) {
|
|
26946
|
-
|
|
26982
|
+
function buildNativeModeration(mod, policy) {
|
|
26983
|
+
const out = { model: mod?.model ?? MODERATION_DEFAULT_MODEL };
|
|
26984
|
+
if (policy && typeof policy === "object") out.policy = policy;
|
|
26985
|
+
return out;
|
|
26947
26986
|
}
|
|
26948
26987
|
function parseNativeModeration(raw) {
|
|
26949
26988
|
if (!raw || typeof raw !== "object") return void 0;
|
|
@@ -27053,8 +27092,12 @@ var OpenAIAdapter = class {
|
|
|
27053
27092
|
if (req.stop) body.stop = req.stop;
|
|
27054
27093
|
const tier = openaiRequestTier(req.serviceTier);
|
|
27055
27094
|
if (tier) body.service_tier = tier;
|
|
27056
|
-
|
|
27057
|
-
|
|
27095
|
+
const modPolicy = req.providerOptions?.moderationPolicy;
|
|
27096
|
+
if (req.moderation && req.moderation.mode !== "emulate" || modPolicy) {
|
|
27097
|
+
body.moderation = buildNativeModeration(req.moderation, modPolicy);
|
|
27098
|
+
}
|
|
27099
|
+
if (this.name === "openai" && req.providerOptions?.promptCacheOptions) {
|
|
27100
|
+
body.prompt_cache_options = req.providerOptions.promptCacheOptions;
|
|
27058
27101
|
}
|
|
27059
27102
|
const hasAudioInput = req.messages.some(
|
|
27060
27103
|
(m) => Array.isArray(m.content) && m.content.some((p) => p.type === "audio")
|
|
@@ -27230,7 +27273,7 @@ var OpenAIAdapter = class {
|
|
|
27230
27273
|
raw
|
|
27231
27274
|
};
|
|
27232
27275
|
}
|
|
27233
|
-
parseStreamEvent(event) {
|
|
27276
|
+
parseStreamEvent(event, state) {
|
|
27234
27277
|
const data = JSON.parse(event.data);
|
|
27235
27278
|
if (data.moderation) {
|
|
27236
27279
|
const report = parseNativeModeration(data.moderation);
|
|
@@ -27256,29 +27299,32 @@ var OpenAIAdapter = class {
|
|
|
27256
27299
|
if (delta.content) {
|
|
27257
27300
|
events.push({ type: "text", text: delta.content });
|
|
27258
27301
|
}
|
|
27302
|
+
const toolIdByIndex = state?.toolIdByIndex ?? /* @__PURE__ */ new Map();
|
|
27259
27303
|
const toolCalls = delta.tool_calls ?? [];
|
|
27260
27304
|
for (const tc of toolCalls) {
|
|
27305
|
+
const index = tc.index ?? 0;
|
|
27306
|
+
let id = toolIdByIndex.get(index);
|
|
27307
|
+
if (id === void 0) {
|
|
27308
|
+
id = tc.id || `call_${crypto.randomUUID()}`;
|
|
27309
|
+
toolIdByIndex.set(index, id);
|
|
27310
|
+
}
|
|
27261
27311
|
const fn = tc.function;
|
|
27262
27312
|
if (fn?.name) {
|
|
27263
|
-
events.push({
|
|
27264
|
-
type: "tool_call_start",
|
|
27265
|
-
id: tc.id ?? "",
|
|
27266
|
-
name: fn.name
|
|
27267
|
-
});
|
|
27313
|
+
events.push({ type: "tool_call_start", id, name: fn.name });
|
|
27268
27314
|
}
|
|
27269
27315
|
if (fn?.arguments) {
|
|
27270
|
-
events.push({
|
|
27271
|
-
type: "tool_call_delta",
|
|
27272
|
-
id: tc.id ?? "",
|
|
27273
|
-
arguments: fn.arguments
|
|
27274
|
-
});
|
|
27316
|
+
events.push({ type: "tool_call_delta", id, arguments: fn.arguments });
|
|
27275
27317
|
}
|
|
27276
27318
|
}
|
|
27277
27319
|
const fr = choice.finish_reason;
|
|
27278
27320
|
if (fr) {
|
|
27279
27321
|
events.push({
|
|
27280
27322
|
type: "done",
|
|
27281
|
-
finishReason: extractFinishReason(false, fr, {
|
|
27323
|
+
finishReason: extractFinishReason(false, fr, {
|
|
27324
|
+
tool_calls: "tool_use",
|
|
27325
|
+
length: "length",
|
|
27326
|
+
content_filter: "content_filter"
|
|
27327
|
+
})
|
|
27282
27328
|
});
|
|
27283
27329
|
}
|
|
27284
27330
|
if (data.usage) {
|
|
@@ -27286,9 +27332,11 @@ var OpenAIAdapter = class {
|
|
|
27286
27332
|
}
|
|
27287
27333
|
return events;
|
|
27288
27334
|
}
|
|
27289
|
-
/**
|
|
27335
|
+
/** Per-stream: correlates streamed tool-call fragments by index and synthesizes
|
|
27336
|
+
* a stable id for backends that omit tool-call ids (see `parseStreamEvent`). */
|
|
27290
27337
|
createStreamParser() {
|
|
27291
|
-
|
|
27338
|
+
const state = { toolIdByIndex: /* @__PURE__ */ new Map() };
|
|
27339
|
+
return (event) => this.parseStreamEvent(event, state);
|
|
27292
27340
|
}
|
|
27293
27341
|
parseUsage(u) {
|
|
27294
27342
|
if (!u) return emptyUsage();
|
|
@@ -27301,7 +27349,7 @@ var OpenAIAdapter = class {
|
|
|
27301
27349
|
outputTokens: output,
|
|
27302
27350
|
totalTokens: input + output,
|
|
27303
27351
|
cachedTokens: details.cached_tokens ?? 0,
|
|
27304
|
-
cacheWriteTokens: 0,
|
|
27352
|
+
cacheWriteTokens: details.cache_write_tokens ?? 0,
|
|
27305
27353
|
reasoningTokens: outDetails.reasoning_tokens ?? 0
|
|
27306
27354
|
};
|
|
27307
27355
|
}
|
|
@@ -27947,8 +27995,12 @@ var OpenAIResponsesAdapter = class {
|
|
|
27947
27995
|
if (req.topP !== void 0) body.top_p = req.topP;
|
|
27948
27996
|
const tier = openaiRequestTier(req.serviceTier);
|
|
27949
27997
|
if (tier) body.service_tier = tier;
|
|
27950
|
-
|
|
27951
|
-
|
|
27998
|
+
const modPolicy = req.providerOptions?.moderationPolicy;
|
|
27999
|
+
if (req.moderation && req.moderation.mode !== "emulate" || modPolicy) {
|
|
28000
|
+
body.moderation = buildNativeModeration(req.moderation, modPolicy);
|
|
28001
|
+
}
|
|
28002
|
+
if (this.name === "openai" && req.providerOptions?.promptCacheOptions) {
|
|
28003
|
+
body.prompt_cache_options = req.providerOptions.promptCacheOptions;
|
|
27952
28004
|
}
|
|
27953
28005
|
if (req.tools?.length) {
|
|
27954
28006
|
body.tools = req.tools.map((t) => {
|
|
@@ -27958,7 +28010,10 @@ var OpenAIResponsesAdapter = class {
|
|
|
27958
28010
|
name: t.name,
|
|
27959
28011
|
description: t.description,
|
|
27960
28012
|
parameters: ensureAdditionalProperties(t.parameters),
|
|
27961
|
-
strict: t.strict ?? true
|
|
28013
|
+
strict: t.strict ?? true,
|
|
28014
|
+
// Programmatic tool calling (Responses): who may call it + return schema.
|
|
28015
|
+
...t.allowedCallers ? { allowed_callers: t.allowedCallers } : {},
|
|
28016
|
+
...t.outputSchema ? { output_schema: t.outputSchema } : {}
|
|
27962
28017
|
};
|
|
27963
28018
|
}
|
|
27964
28019
|
const builtin = { type: t.type, ...t.params };
|
|
@@ -27986,9 +28041,13 @@ var OpenAIResponsesAdapter = class {
|
|
|
27986
28041
|
};
|
|
27987
28042
|
}
|
|
27988
28043
|
if (req.thinking && req.thinking.mode !== "off") {
|
|
28044
|
+
const visibility = req.thinking.visibility ?? "full";
|
|
28045
|
+
const summary = visibility === "hidden" ? null : visibility === "summary" ? "concise" : "auto";
|
|
28046
|
+
const mode = req.providerOptions?.reasoningMode;
|
|
27989
28047
|
body.reasoning = {
|
|
27990
28048
|
effort: req.thinking.effort ?? "medium",
|
|
27991
|
-
summary:
|
|
28049
|
+
...summary !== null ? { summary } : {},
|
|
28050
|
+
...mode ? { mode } : {},
|
|
27992
28051
|
// Cross-turn reasoning persistence (gpt-5/o-series, Responses only).
|
|
27993
28052
|
...req.thinking.context ? { context: req.thinking.context } : {}
|
|
27994
28053
|
};
|
|
@@ -28132,9 +28191,8 @@ var OpenAIResponsesAdapter = class {
|
|
|
28132
28191
|
}
|
|
28133
28192
|
}
|
|
28134
28193
|
const status = r.status;
|
|
28135
|
-
const
|
|
28136
|
-
|
|
28137
|
-
});
|
|
28194
|
+
const incompleteReason = r.incomplete_details?.reason;
|
|
28195
|
+
const finishReason = incompleteReason === "content_filter" ? "content_filter" : extractFinishReason(toolCalls.length > 0, status, { incomplete: "length" });
|
|
28138
28196
|
if (!text && typeof r.output_text === "string") {
|
|
28139
28197
|
text = r.output_text;
|
|
28140
28198
|
if (text && content.length === 0) content.push({ type: "text", text });
|
|
@@ -28254,7 +28312,7 @@ var OpenAIResponsesAdapter = class {
|
|
|
28254
28312
|
outputTokens: output,
|
|
28255
28313
|
totalTokens: u.total_tokens ?? input + output,
|
|
28256
28314
|
cachedTokens: inputDetails.cached_tokens ?? 0,
|
|
28257
|
-
cacheWriteTokens: 0,
|
|
28315
|
+
cacheWriteTokens: inputDetails.cache_write_tokens ?? 0,
|
|
28258
28316
|
reasoningTokens: outputDetails.reasoning_tokens ?? 0
|
|
28259
28317
|
};
|
|
28260
28318
|
}
|
|
@@ -28344,8 +28402,9 @@ var OpenRouterAdapter = class extends OpenAIAdapter {
|
|
|
28344
28402
|
* `url_citation` annotations appear in the stream (the `:online` search signal). */
|
|
28345
28403
|
createStreamParser() {
|
|
28346
28404
|
let webSearchEmitted = false;
|
|
28405
|
+
const state = { toolIdByIndex: /* @__PURE__ */ new Map() };
|
|
28347
28406
|
return (event) => {
|
|
28348
|
-
const events = this.parseStreamEvent(event);
|
|
28407
|
+
const events = this.parseStreamEvent(event, state);
|
|
28349
28408
|
if (!webSearchEmitted) {
|
|
28350
28409
|
const choice = JSON.parse(event.data).choices?.[0];
|
|
28351
28410
|
const annotations = choice?.delta?.annotations ?? choice?.message?.annotations;
|
|
@@ -28516,8 +28575,8 @@ var XAIAdapter = class extends OpenAIAdapter {
|
|
|
28516
28575
|
}
|
|
28517
28576
|
return result;
|
|
28518
28577
|
}
|
|
28519
|
-
parseStreamEvent(event) {
|
|
28520
|
-
const events = super.parseStreamEvent(event);
|
|
28578
|
+
parseStreamEvent(event, state) {
|
|
28579
|
+
const events = super.parseStreamEvent(event, state);
|
|
28521
28580
|
try {
|
|
28522
28581
|
const data = JSON.parse(event.data);
|
|
28523
28582
|
const choices = data.choices ?? [];
|
|
@@ -28640,7 +28699,8 @@ var XAIMediaAdapter = class {
|
|
|
28640
28699
|
imageEditing: true,
|
|
28641
28700
|
audioGeneration: true,
|
|
28642
28701
|
videoGeneration: true,
|
|
28643
|
-
audioStreaming: true
|
|
28702
|
+
audioStreaming: true,
|
|
28703
|
+
videoExtension: true
|
|
28644
28704
|
};
|
|
28645
28705
|
}
|
|
28646
28706
|
authHeaders() {
|
|
@@ -28754,13 +28814,9 @@ var XAIMediaAdapter = class {
|
|
|
28754
28814
|
}
|
|
28755
28815
|
async submitVideo(req, fetch2) {
|
|
28756
28816
|
const model = req.model ?? "grok-imagine-video";
|
|
28757
|
-
const
|
|
28758
|
-
if (req.params?.duration) body.duration = req.params.duration;
|
|
28759
|
-
if (req.params?.aspectRatio) body.aspect_ratio = req.params.aspectRatio;
|
|
28760
|
-
if (req.params?.resolution) body.resolution = req.params.resolution;
|
|
28761
|
-
if (req.sourceImage) body.image = xaiImageRef(normalizeImageSource(req.sourceImage));
|
|
28817
|
+
const { url, body } = this.buildVideoSubmit(req, model);
|
|
28762
28818
|
const res = await fetch2({
|
|
28763
|
-
url
|
|
28819
|
+
url,
|
|
28764
28820
|
method: "POST",
|
|
28765
28821
|
headers: this.authHeaders(),
|
|
28766
28822
|
body,
|
|
@@ -28771,6 +28827,30 @@ var XAIMediaAdapter = class {
|
|
|
28771
28827
|
const data = res.body;
|
|
28772
28828
|
return data.request_id ?? data.id ?? "";
|
|
28773
28829
|
}
|
|
28830
|
+
/** Route a video request to the right xAI endpoint by input + mode:
|
|
28831
|
+
* - no `sourceVideo` → `/v1/videos/generations` (text/image-to-video)
|
|
28832
|
+
* - `sourceVideo` + `videoMode:'extend'` (default) → `/v1/videos/extensions`
|
|
28833
|
+
* — continues from the last frame; takes `duration`, NOT aspect/resolution.
|
|
28834
|
+
* - `sourceVideo` + `videoMode:'edit'` → `/v1/videos/edits` — prompt + video
|
|
28835
|
+
* only (no duration/aspect/resolution).
|
|
28836
|
+
* All three return a `request_id` polled via the same status endpoint. */
|
|
28837
|
+
buildVideoSubmit(req, model) {
|
|
28838
|
+
if (req.sourceVideo) {
|
|
28839
|
+
const video = xaiVideoRef(req.sourceVideo);
|
|
28840
|
+
if ((req.params?.videoMode ?? "extend") === "edit") {
|
|
28841
|
+
return { url: `${this.baseURL}/v1/videos/edits`, body: { model, prompt: req.prompt, video } };
|
|
28842
|
+
}
|
|
28843
|
+
const body2 = { model, prompt: req.prompt, video };
|
|
28844
|
+
if (req.params?.duration) body2.duration = req.params.duration;
|
|
28845
|
+
return { url: `${this.baseURL}/v1/videos/extensions`, body: body2 };
|
|
28846
|
+
}
|
|
28847
|
+
const body = { model, prompt: req.prompt };
|
|
28848
|
+
if (req.params?.duration) body.duration = req.params.duration;
|
|
28849
|
+
if (req.params?.aspectRatio) body.aspect_ratio = req.params.aspectRatio;
|
|
28850
|
+
if (req.params?.resolution) body.resolution = req.params.resolution;
|
|
28851
|
+
if (req.sourceImage) body.image = xaiImageRef(normalizeImageSource(req.sourceImage));
|
|
28852
|
+
return { url: `${this.baseURL}/v1/videos/generations`, body };
|
|
28853
|
+
}
|
|
28774
28854
|
async getVideoStatus(operationId, fetch2) {
|
|
28775
28855
|
const res = await fetch2({
|
|
28776
28856
|
url: `${this.baseURL}/v1/videos/${operationId}`,
|
|
@@ -28784,13 +28864,15 @@ var XAIMediaAdapter = class {
|
|
|
28784
28864
|
if (res.status >= 400) return { status: "failed", error: `HTTP ${res.status}` };
|
|
28785
28865
|
const data = res.body;
|
|
28786
28866
|
const state = data.status ?? "";
|
|
28787
|
-
|
|
28788
|
-
|
|
28867
|
+
const video = data.video;
|
|
28868
|
+
const progress = data.progress;
|
|
28869
|
+
if (state === "done" || state === "completed" || state === "ready" || video?.url || data.download_url) {
|
|
28870
|
+
return { status: "completed", progress };
|
|
28789
28871
|
}
|
|
28790
|
-
if (state === "failed" || state === "error") {
|
|
28872
|
+
if (state === "failed" || state === "error" || state === "expired") {
|
|
28791
28873
|
return { status: "failed", error: data.error ?? "Unknown error" };
|
|
28792
28874
|
}
|
|
28793
|
-
return { status: "processing", progress
|
|
28875
|
+
return { status: "processing", progress };
|
|
28794
28876
|
}
|
|
28795
28877
|
async downloadVideo(operationId, fetch2) {
|
|
28796
28878
|
const statusRes = await fetch2({
|
|
@@ -28806,8 +28888,19 @@ var XAIMediaAdapter = class {
|
|
|
28806
28888
|
throw new Error(`xAI video download failed: HTTP ${statusRes.status}`);
|
|
28807
28889
|
}
|
|
28808
28890
|
const data = statusRes.body;
|
|
28809
|
-
const
|
|
28891
|
+
const video = data.video;
|
|
28892
|
+
const downloadUrl = video?.url ?? data.download_url ?? data.url;
|
|
28810
28893
|
if (!downloadUrl) throw new Error("No download URL in video response");
|
|
28894
|
+
const durationSec = video?.duration ?? data.duration;
|
|
28895
|
+
const base = {
|
|
28896
|
+
data: new Uint8Array(0),
|
|
28897
|
+
mimeType: "video/mp4",
|
|
28898
|
+
sourceUrl: downloadUrl,
|
|
28899
|
+
durationMs: durationSec ? durationSec * 1e3 : void 0,
|
|
28900
|
+
// Provider-reported cost (usage.cost_in_usd_ticks), when present.
|
|
28901
|
+
providerMeta: data.usage ? { usage: data.usage } : void 0
|
|
28902
|
+
};
|
|
28903
|
+
if (isBrowser()) return base;
|
|
28811
28904
|
const videoRes = await fetch2({
|
|
28812
28905
|
url: downloadUrl,
|
|
28813
28906
|
method: "GET",
|
|
@@ -28817,13 +28910,7 @@ var XAIMediaAdapter = class {
|
|
|
28817
28910
|
model: "",
|
|
28818
28911
|
responseType: "arraybuffer"
|
|
28819
28912
|
});
|
|
28820
|
-
return {
|
|
28821
|
-
data: videoRes.body,
|
|
28822
|
-
mimeType: "video/mp4",
|
|
28823
|
-
durationMs: data.duration ? data.duration * 1e3 : void 0,
|
|
28824
|
-
// Provider-reported cost (usage.cost_in_usd_ticks), when present.
|
|
28825
|
-
providerMeta: data.usage ? { usage: data.usage } : void 0
|
|
28826
|
-
};
|
|
28913
|
+
return { ...base, data: videoRes.body };
|
|
28827
28914
|
}
|
|
28828
28915
|
async cancelVideo(operationId, fetch2) {
|
|
28829
28916
|
await fetch2({
|
|
@@ -30236,7 +30323,11 @@ var AgentLoop = class _AgentLoop {
|
|
|
30236
30323
|
id: lastResponse?.id ?? `agent-${runId}`,
|
|
30237
30324
|
model: this.client.model,
|
|
30238
30325
|
content: finalContent,
|
|
30239
|
-
finishReason: reason === "done" ?
|
|
30326
|
+
finishReason: reason === "done" ? (
|
|
30327
|
+
// Ended because the model requested no tools — surface the provider's
|
|
30328
|
+
// actual reason (stop / content_filter / length), not a flat 'stop'.
|
|
30329
|
+
lastResponse?.finishReason ?? "stop"
|
|
30330
|
+
) : reason === "stopped" ? "stop" : reason === "guardrail" ? "stop" : reason === "max_steps" ? "length" : "error",
|
|
30240
30331
|
usage: totalUsage,
|
|
30241
30332
|
text: finalText,
|
|
30242
30333
|
toolCalls: lastResponse?.toolCalls ?? [],
|
|
@@ -30445,7 +30536,11 @@ var AgentLoop = class _AgentLoop {
|
|
|
30445
30536
|
id: lastResponse?.id ?? `agent-${runId}`,
|
|
30446
30537
|
model: this.client.model,
|
|
30447
30538
|
content: finalContent,
|
|
30448
|
-
finishReason: reason === "done" ?
|
|
30539
|
+
finishReason: reason === "done" ? (
|
|
30540
|
+
// Ended because the model requested no tools — surface the provider's
|
|
30541
|
+
// actual reason (stop / content_filter / length), not a flat 'stop'.
|
|
30542
|
+
lastResponse?.finishReason ?? "stop"
|
|
30543
|
+
) : reason === "stopped" ? "stop" : reason === "guardrail" ? "stop" : reason === "max_steps" ? "length" : "error",
|
|
30449
30544
|
usage: totalUsage,
|
|
30450
30545
|
text: finalText,
|
|
30451
30546
|
toolCalls: lastResponse?.toolCalls ?? [],
|
|
@@ -34770,9 +34865,13 @@ var MediaOutput = class {
|
|
|
34770
34865
|
}
|
|
34771
34866
|
async generateVideo(req) {
|
|
34772
34867
|
const adapter = this.getAdapter(req.provider);
|
|
34773
|
-
|
|
34868
|
+
const caps = adapter.capabilities();
|
|
34869
|
+
if (!caps.videoGeneration || !adapter.submitVideo) {
|
|
34774
34870
|
throw new Error(`Provider ${req.provider} does not support video generation`);
|
|
34775
34871
|
}
|
|
34872
|
+
if (req.sourceVideo && !caps.videoExtension) {
|
|
34873
|
+
throw new Error(`Provider ${req.provider} does not support video extension/editing`);
|
|
34874
|
+
}
|
|
34776
34875
|
const { trace, fetch: fetch2 } = this.tracedOp();
|
|
34777
34876
|
const operationId = await adapter.submitVideo(req, fetch2);
|
|
34778
34877
|
return this.pollVideoCompletion(adapter, operationId, req, fetch2, trace);
|
|
@@ -34805,7 +34904,8 @@ var MediaOutput = class {
|
|
|
34805
34904
|
width: raw.width,
|
|
34806
34905
|
height: raw.height,
|
|
34807
34906
|
durationMs: raw.durationMs,
|
|
34808
|
-
sampleRate: raw.sampleRate
|
|
34907
|
+
sampleRate: raw.sampleRate,
|
|
34908
|
+
sourceUrl: raw.sourceUrl
|
|
34809
34909
|
};
|
|
34810
34910
|
await this.mediaStore.save(id, raw.data, meta);
|
|
34811
34911
|
results.push({ id, type, mimeType: raw.mimeType, meta });
|
|
@@ -34838,6 +34938,15 @@ var MediaOutput = class {
|
|
|
34838
34938
|
const start = Date.now();
|
|
34839
34939
|
while (Date.now() - start < this.maxPollWaitMs) {
|
|
34840
34940
|
const status = await adapter.getVideoStatus(operationId, fetch2);
|
|
34941
|
+
if (status.status === "processing" || status.status === "pending") {
|
|
34942
|
+
await this.hooks.emit("onMediaProgress", {
|
|
34943
|
+
type: "video",
|
|
34944
|
+
provider: req.provider,
|
|
34945
|
+
operationId,
|
|
34946
|
+
progress: status.progress,
|
|
34947
|
+
model: req.model
|
|
34948
|
+
});
|
|
34949
|
+
}
|
|
34841
34950
|
if (status.status === "completed") {
|
|
34842
34951
|
const raw = await adapter.downloadVideo(operationId, fetch2);
|
|
34843
34952
|
const results = await this.saveResults(
|
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
* - Chat Completions: moderation.{input,output} = moderation_results | error,
|
|
10
10
|
* where moderation_results wraps `results: [moderation_result]`. */
|
|
11
11
|
import type { ModerationReport, ModerationRequest } from './types';
|
|
12
|
-
/** The `moderation` request field for the OpenAI native path.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
/** The `moderation` request field for the OpenAI native path. `policy` is an
|
|
13
|
+
* OpenAI-only opt-in (via `providerOptions.moderationPolicy`) for server-side
|
|
14
|
+
* BLOCKING — `{ input?: { mode: 'score'|'block' }, output?: {...} }`; our unified
|
|
15
|
+
* moderation stays report-only, so it's a passthrough, not a first-class knob. */
|
|
16
|
+
export declare function buildNativeModeration(mod?: ModerationRequest, policy?: unknown): Record<string, unknown>;
|
|
16
17
|
/** Parse OpenAI's returned `moderation` object into a unified report, or undefined
|
|
17
18
|
* when the server returned nothing usable. */
|
|
18
19
|
export declare function parseNativeModeration(raw: unknown): ModerationReport | undefined;
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
/** Google provider constants. */
|
|
2
2
|
/**
|
|
3
|
-
* Map from unified thinking effort levels to Gemini thinkingLevel enum strings.
|
|
4
|
-
*
|
|
3
|
+
* Map from unified thinking effort levels to Gemini `thinkingLevel` enum strings.
|
|
4
|
+
* `thinkingLevel` is the Gemini 3.x thinking control (LOW/HIGH).
|
|
5
5
|
*/
|
|
6
6
|
export declare const GOOGLE_THINKING_LEVELS: Record<string, string>;
|
|
7
|
+
/**
|
|
8
|
+
* Map from unified thinking effort to a Gemini `thinkingBudget` (token count).
|
|
9
|
+
* Gemini **2.5** models only accept a token budget — they 400 on `thinkingLevel`
|
|
10
|
+
* ("Thinking level is not supported for this model", live-verified 2026-07-16).
|
|
11
|
+
* Values sit inside the 2.5 range (flash/flash-lite cap ~24576, pro ~32768).
|
|
12
|
+
*/
|
|
13
|
+
export declare const GOOGLE_THINKING_BUDGETS: Record<string, number>;
|
|
14
|
+
/** Gemini 2.5 series uses `thinkingBudget`; 3.x+ uses `thinkingLevel`. */
|
|
15
|
+
export declare function googleUsesThinkingBudget(model: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Effort → Interactions `thinking_level`. The Interactions API uses **lowercase**
|
|
18
|
+
* values (`minimal`/`low`/`medium`/`high`) — distinct from generateContent's
|
|
19
|
+
* uppercase `thinkingLevel`, and it 400s on the uppercase form (live 2026-07-16).
|
|
20
|
+
*/
|
|
21
|
+
export declare const GOOGLE_INTERACTION_THINKING_LEVELS: Record<string, string>;
|
|
@@ -8,6 +8,12 @@ export interface OpenAIAdapterConfig {
|
|
|
8
8
|
apiKey: string;
|
|
9
9
|
baseURL?: string;
|
|
10
10
|
}
|
|
11
|
+
/** Per-stream state threaded through `createStreamParser` — maps a streamed
|
|
12
|
+
* tool call's `index` to its resolved id (real, or a synthesized `call_<uuid>`
|
|
13
|
+
* for backends that omit ids), stable across the stream's chunks. */
|
|
14
|
+
export interface OpenAIStreamState {
|
|
15
|
+
toolIdByIndex: Map<number, string>;
|
|
16
|
+
}
|
|
11
17
|
export declare class OpenAIAdapter implements ProviderAdapter {
|
|
12
18
|
readonly name: ProviderAdapter['name'];
|
|
13
19
|
protected readonly apiKey: string;
|
|
@@ -20,8 +26,9 @@ export declare class OpenAIAdapter implements ProviderAdapter {
|
|
|
20
26
|
private buildMessage;
|
|
21
27
|
enableStreaming(providerReq: ProviderHttpRequest, _req: NormalizedRequest): void;
|
|
22
28
|
parseResponse(raw: unknown, latencyMs: number): CompletionResponse;
|
|
23
|
-
parseStreamEvent(event: SSEEvent): StreamEvent[];
|
|
24
|
-
/**
|
|
29
|
+
parseStreamEvent(event: SSEEvent, state?: OpenAIStreamState): StreamEvent[];
|
|
30
|
+
/** Per-stream: correlates streamed tool-call fragments by index and synthesizes
|
|
31
|
+
* a stable id for backends that omit tool-call ids (see `parseStreamEvent`). */
|
|
25
32
|
createStreamParser(): (event: SSEEvent) => StreamEvent[];
|
|
26
33
|
private parseUsage;
|
|
27
34
|
}
|
|
@@ -9,7 +9,7 @@ import type { ProviderAdapter, ProviderHttpRequest } from '../../types/provider'
|
|
|
9
9
|
import type { NormalizedRequest } from '../../types/request';
|
|
10
10
|
import type { CompletionResponse } from '../../types/response';
|
|
11
11
|
import type { StreamEvent } from '../../types/stream';
|
|
12
|
-
import { OpenAIAdapter } from '../openai/completions';
|
|
12
|
+
import { OpenAIAdapter, type OpenAIStreamState } from '../openai/completions';
|
|
13
13
|
export interface XAIAdapterConfig {
|
|
14
14
|
apiKey: string;
|
|
15
15
|
baseURL?: string;
|
|
@@ -20,5 +20,5 @@ export declare class XAIAdapter extends OpenAIAdapter {
|
|
|
20
20
|
baseURL(): string;
|
|
21
21
|
buildRequest(req: NormalizedRequest): ProviderHttpRequest;
|
|
22
22
|
parseResponse(raw: unknown, latencyMs: number): CompletionResponse;
|
|
23
|
-
parseStreamEvent(event: SSEEvent): StreamEvent[];
|
|
23
|
+
parseStreamEvent(event: SSEEvent, state?: OpenAIStreamState): StreamEvent[];
|
|
24
24
|
}
|
|
@@ -21,6 +21,14 @@ export declare class XAIMediaAdapter implements MediaProviderAdapter {
|
|
|
21
21
|
private parseImages;
|
|
22
22
|
generateAudio(req: AudioGenRequest, fetch: EngineFetch): Promise<RawMediaResult>;
|
|
23
23
|
submitVideo(req: VideoGenRequest, fetch: EngineFetch): Promise<string>;
|
|
24
|
+
/** Route a video request to the right xAI endpoint by input + mode:
|
|
25
|
+
* - no `sourceVideo` → `/v1/videos/generations` (text/image-to-video)
|
|
26
|
+
* - `sourceVideo` + `videoMode:'extend'` (default) → `/v1/videos/extensions`
|
|
27
|
+
* — continues from the last frame; takes `duration`, NOT aspect/resolution.
|
|
28
|
+
* - `sourceVideo` + `videoMode:'edit'` → `/v1/videos/edits` — prompt + video
|
|
29
|
+
* only (no duration/aspect/resolution).
|
|
30
|
+
* All three return a `request_id` polled via the same status endpoint. */
|
|
31
|
+
private buildVideoSubmit;
|
|
24
32
|
getVideoStatus(operationId: string, fetch: EngineFetch): Promise<VideoStatus>;
|
|
25
33
|
downloadVideo(operationId: string, fetch: EngineFetch): Promise<RawMediaResult>;
|
|
26
34
|
cancelVideo(operationId: string, fetch: EngineFetch): Promise<void>;
|
|
@@ -49,13 +49,21 @@ export interface NormalizedRequest {
|
|
|
49
49
|
* token cost; `current_turn` drops earlier reasoning; `auto` lets OpenAI decide.
|
|
50
50
|
* Ignored by every other provider. */
|
|
51
51
|
export type ReasoningContext = 'auto' | 'current_turn' | 'all_turns';
|
|
52
|
+
/** How much of the model's reasoning is returned. `full` (default) returns it as
|
|
53
|
+
* fully as the provider allows; `summary` a condensed form where the provider
|
|
54
|
+
* supports one (else full); `hidden` keeps reasoning internal. Best-effort per
|
|
55
|
+
* provider — Anthropic `enabled.display`, OpenAI Responses `summary`, Google
|
|
56
|
+
* `includeThoughts`; providers without a control ignore it. */
|
|
57
|
+
export type ThinkingVisibility = 'full' | 'summary' | 'hidden';
|
|
52
58
|
export type ThinkingConfig = {
|
|
53
59
|
mode: 'auto';
|
|
54
60
|
effort?: 'low' | 'medium' | 'high' | 'max';
|
|
61
|
+
visibility?: ThinkingVisibility;
|
|
55
62
|
context?: ReasoningContext;
|
|
56
63
|
} | {
|
|
57
64
|
mode: 'on';
|
|
58
65
|
effort?: 'low' | 'medium' | 'high' | 'max';
|
|
66
|
+
visibility?: ThinkingVisibility;
|
|
59
67
|
context?: ReasoningContext;
|
|
60
68
|
} | {
|
|
61
69
|
mode: 'off';
|
|
@@ -6,9 +6,18 @@ export interface FunctionTool {
|
|
|
6
6
|
parameters: JsonSchema;
|
|
7
7
|
strict?: boolean;
|
|
8
8
|
cache?: boolean;
|
|
9
|
+
/** OpenAI **Responses** programmatic tool calling: which callers may invoke this
|
|
10
|
+
* tool — `direct` (the model calls it) and/or `programmatic` (generated
|
|
11
|
+
* orchestration code calls it). OpenAI-Responses-only; ignored elsewhere. */
|
|
12
|
+
allowedCallers?: Array<'direct' | 'programmatic'>;
|
|
13
|
+
/** OpenAI **Responses** JSON schema for the tool's return value (lets the model
|
|
14
|
+
* reason over structured tool output). OpenAI-Responses-only. */
|
|
15
|
+
outputSchema?: JsonSchema;
|
|
9
16
|
}
|
|
10
17
|
export interface BuiltinTool {
|
|
11
|
-
type: 'image_generation' | 'web_search' | 'web_fetch' | 'code_interpreter' | 'file_search' | 'mcp'
|
|
18
|
+
type: 'image_generation' | 'web_search' | 'web_fetch' | 'code_interpreter' | 'file_search' | 'mcp'
|
|
19
|
+
/** OpenAI Responses: lets the model write JS to orchestrate tool calls. */
|
|
20
|
+
| 'programmatic_tool_calling';
|
|
12
21
|
params?: Record<string, unknown>;
|
|
13
22
|
}
|
|
14
23
|
/** Typed shape for an `mcp` builtin's `params` (OpenAI hosted MCP tool). The
|