@combycode/llm-sdk 1.6.1 → 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 +88 -0
- package/dist/bus/hook-map.d.ts +11 -0
- package/dist/index.browser.js +177 -63
- package/dist/index.js +177 -63
- 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
|
@@ -23418,7 +23418,8 @@ var catalog_default5 = {
|
|
|
23418
23418
|
video: false,
|
|
23419
23419
|
imageGeneration: false,
|
|
23420
23420
|
audioGeneration: false,
|
|
23421
|
-
videoGeneration: true
|
|
23421
|
+
videoGeneration: true,
|
|
23422
|
+
videoExtension: true
|
|
23422
23423
|
},
|
|
23423
23424
|
reasoning: {
|
|
23424
23425
|
supported: false,
|
|
@@ -25101,7 +25102,9 @@ var AnthropicAdapter = class {
|
|
|
25101
25102
|
if (req.thinking.mode === "off") {
|
|
25102
25103
|
} else {
|
|
25103
25104
|
const budget = req.thinking.effort ? ANTHROPIC_THINKING_BUDGETS[req.thinking.effort] ?? DEFAULT_ANTHROPIC_THINKING_BUDGET : DEFAULT_ANTHROPIC_THINKING_BUDGET;
|
|
25104
|
-
|
|
25105
|
+
const thinking = { type: "enabled", budget_tokens: budget };
|
|
25106
|
+
if (req.thinking.visibility === "hidden") thinking.display = "omitted";
|
|
25107
|
+
body.thinking = thinking;
|
|
25105
25108
|
if (body.max_tokens <= budget) body.max_tokens = budget + 1024;
|
|
25106
25109
|
}
|
|
25107
25110
|
}
|
|
@@ -25618,6 +25621,21 @@ var GOOGLE_THINKING_LEVELS = {
|
|
|
25618
25621
|
high: "HIGH",
|
|
25619
25622
|
max: "HIGH"
|
|
25620
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
|
+
};
|
|
25621
25639
|
|
|
25622
25640
|
// src/llm/providers/google/generate.ts
|
|
25623
25641
|
var GoogleAdapter = class {
|
|
@@ -25698,9 +25716,16 @@ var GoogleAdapter = class {
|
|
|
25698
25716
|
body.toolConfig = { functionCallingConfig: { mode } };
|
|
25699
25717
|
}
|
|
25700
25718
|
if (req.thinking && req.thinking.mode !== "off") {
|
|
25701
|
-
|
|
25702
|
-
|
|
25719
|
+
const effort = req.thinking.effort ?? "high";
|
|
25720
|
+
const thinkingConfig = {
|
|
25721
|
+
includeThoughts: req.thinking.visibility !== "hidden"
|
|
25703
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;
|
|
25704
25729
|
}
|
|
25705
25730
|
if (req.structured) {
|
|
25706
25731
|
config.responseMimeType = "application/json";
|
|
@@ -25716,6 +25741,9 @@ var GoogleAdapter = class {
|
|
|
25716
25741
|
if (req.providerOptions.imageConfig) {
|
|
25717
25742
|
config.imageConfig = req.providerOptions.imageConfig;
|
|
25718
25743
|
}
|
|
25744
|
+
if (req.providerOptions.translationConfig) {
|
|
25745
|
+
config.translationConfig = req.providerOptions.translationConfig;
|
|
25746
|
+
}
|
|
25719
25747
|
}
|
|
25720
25748
|
return {
|
|
25721
25749
|
body,
|
|
@@ -26066,8 +26094,6 @@ var GoogleInteractionsAdapter = class {
|
|
|
26066
26094
|
if (req.maxTokens) genConfig.max_output_tokens = req.maxTokens;
|
|
26067
26095
|
if (req.temperature !== void 0) genConfig.temperature = req.temperature;
|
|
26068
26096
|
if (req.topP !== void 0) genConfig.top_p = req.topP;
|
|
26069
|
-
if (req.presencePenalty !== void 0) genConfig.presence_penalty = req.presencePenalty;
|
|
26070
|
-
if (req.frequencyPenalty !== void 0) genConfig.frequency_penalty = req.frequencyPenalty;
|
|
26071
26097
|
if (req.stop) genConfig.stop_sequences = req.stop;
|
|
26072
26098
|
if (req.tools?.length) {
|
|
26073
26099
|
body.tools = req.tools.filter(isFunctionTool).map((t) => ({
|
|
@@ -26078,9 +26104,7 @@ var GoogleInteractionsAdapter = class {
|
|
|
26078
26104
|
}));
|
|
26079
26105
|
}
|
|
26080
26106
|
if (req.thinking && req.thinking.mode !== "off") {
|
|
26081
|
-
genConfig.
|
|
26082
|
-
thinking_level: GOOGLE_THINKING_LEVELS[req.thinking.effort ?? "high"] ?? "HIGH"
|
|
26083
|
-
};
|
|
26107
|
+
genConfig.thinking_level = GOOGLE_INTERACTION_THINKING_LEVELS[req.thinking.effort ?? "high"] ?? "high";
|
|
26084
26108
|
}
|
|
26085
26109
|
if (Object.keys(genConfig).length > 0) body.generation_config = genConfig;
|
|
26086
26110
|
const cachedContent = req.providerOptions?.cachedContent;
|
|
@@ -26383,6 +26407,24 @@ function openaiImageRef(ref) {
|
|
|
26383
26407
|
function xaiImageRef(ref) {
|
|
26384
26408
|
return ref.fileId ? { file_id: ref.fileId } : { url: toDataUrl(ref) };
|
|
26385
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
|
+
}
|
|
26386
26428
|
function googleImagePart(ref) {
|
|
26387
26429
|
const mimeType = ref.mimeType ?? "image/png";
|
|
26388
26430
|
if (ref.base64) return { inline_data: { mime_type: mimeType, data: ref.base64 } };
|
|
@@ -26533,7 +26575,7 @@ var GoogleMediaAdapter = class {
|
|
|
26533
26575
|
const image = {};
|
|
26534
26576
|
if (req.params?.aspectRatio) image.aspectRatio = req.params.aspectRatio;
|
|
26535
26577
|
if (req.params?.imageSize) image.imageSize = req.params.imageSize;
|
|
26536
|
-
if (Object.keys(image).length) generationConfig.
|
|
26578
|
+
if (Object.keys(image).length) generationConfig.imageConfig = image;
|
|
26537
26579
|
const imagePart = googleImagePart(normalizeImageSource(req.sourceImage));
|
|
26538
26580
|
const { items, usage } = await this.generateContentMedia(
|
|
26539
26581
|
model,
|
|
@@ -26937,8 +26979,10 @@ var OpenAIBatchAdapter = class {
|
|
|
26937
26979
|
};
|
|
26938
26980
|
|
|
26939
26981
|
// src/llm/moderation/native.ts
|
|
26940
|
-
function buildNativeModeration(mod) {
|
|
26941
|
-
|
|
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;
|
|
26942
26986
|
}
|
|
26943
26987
|
function parseNativeModeration(raw) {
|
|
26944
26988
|
if (!raw || typeof raw !== "object") return void 0;
|
|
@@ -27048,8 +27092,12 @@ var OpenAIAdapter = class {
|
|
|
27048
27092
|
if (req.stop) body.stop = req.stop;
|
|
27049
27093
|
const tier = openaiRequestTier(req.serviceTier);
|
|
27050
27094
|
if (tier) body.service_tier = tier;
|
|
27051
|
-
|
|
27052
|
-
|
|
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;
|
|
27053
27101
|
}
|
|
27054
27102
|
const hasAudioInput = req.messages.some(
|
|
27055
27103
|
(m) => Array.isArray(m.content) && m.content.some((p) => p.type === "audio")
|
|
@@ -27225,7 +27273,7 @@ var OpenAIAdapter = class {
|
|
|
27225
27273
|
raw
|
|
27226
27274
|
};
|
|
27227
27275
|
}
|
|
27228
|
-
parseStreamEvent(event) {
|
|
27276
|
+
parseStreamEvent(event, state) {
|
|
27229
27277
|
const data = JSON.parse(event.data);
|
|
27230
27278
|
if (data.moderation) {
|
|
27231
27279
|
const report = parseNativeModeration(data.moderation);
|
|
@@ -27251,29 +27299,32 @@ var OpenAIAdapter = class {
|
|
|
27251
27299
|
if (delta.content) {
|
|
27252
27300
|
events.push({ type: "text", text: delta.content });
|
|
27253
27301
|
}
|
|
27302
|
+
const toolIdByIndex = state?.toolIdByIndex ?? /* @__PURE__ */ new Map();
|
|
27254
27303
|
const toolCalls = delta.tool_calls ?? [];
|
|
27255
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
|
+
}
|
|
27256
27311
|
const fn = tc.function;
|
|
27257
27312
|
if (fn?.name) {
|
|
27258
|
-
events.push({
|
|
27259
|
-
type: "tool_call_start",
|
|
27260
|
-
id: tc.id ?? "",
|
|
27261
|
-
name: fn.name
|
|
27262
|
-
});
|
|
27313
|
+
events.push({ type: "tool_call_start", id, name: fn.name });
|
|
27263
27314
|
}
|
|
27264
27315
|
if (fn?.arguments) {
|
|
27265
|
-
events.push({
|
|
27266
|
-
type: "tool_call_delta",
|
|
27267
|
-
id: tc.id ?? "",
|
|
27268
|
-
arguments: fn.arguments
|
|
27269
|
-
});
|
|
27316
|
+
events.push({ type: "tool_call_delta", id, arguments: fn.arguments });
|
|
27270
27317
|
}
|
|
27271
27318
|
}
|
|
27272
27319
|
const fr = choice.finish_reason;
|
|
27273
27320
|
if (fr) {
|
|
27274
27321
|
events.push({
|
|
27275
27322
|
type: "done",
|
|
27276
|
-
finishReason: extractFinishReason(false, fr, {
|
|
27323
|
+
finishReason: extractFinishReason(false, fr, {
|
|
27324
|
+
tool_calls: "tool_use",
|
|
27325
|
+
length: "length",
|
|
27326
|
+
content_filter: "content_filter"
|
|
27327
|
+
})
|
|
27277
27328
|
});
|
|
27278
27329
|
}
|
|
27279
27330
|
if (data.usage) {
|
|
@@ -27281,9 +27332,11 @@ var OpenAIAdapter = class {
|
|
|
27281
27332
|
}
|
|
27282
27333
|
return events;
|
|
27283
27334
|
}
|
|
27284
|
-
/**
|
|
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`). */
|
|
27285
27337
|
createStreamParser() {
|
|
27286
|
-
|
|
27338
|
+
const state = { toolIdByIndex: /* @__PURE__ */ new Map() };
|
|
27339
|
+
return (event) => this.parseStreamEvent(event, state);
|
|
27287
27340
|
}
|
|
27288
27341
|
parseUsage(u) {
|
|
27289
27342
|
if (!u) return emptyUsage();
|
|
@@ -27296,7 +27349,7 @@ var OpenAIAdapter = class {
|
|
|
27296
27349
|
outputTokens: output,
|
|
27297
27350
|
totalTokens: input + output,
|
|
27298
27351
|
cachedTokens: details.cached_tokens ?? 0,
|
|
27299
|
-
cacheWriteTokens: 0,
|
|
27352
|
+
cacheWriteTokens: details.cache_write_tokens ?? 0,
|
|
27300
27353
|
reasoningTokens: outDetails.reasoning_tokens ?? 0
|
|
27301
27354
|
};
|
|
27302
27355
|
}
|
|
@@ -27942,8 +27995,12 @@ var OpenAIResponsesAdapter = class {
|
|
|
27942
27995
|
if (req.topP !== void 0) body.top_p = req.topP;
|
|
27943
27996
|
const tier = openaiRequestTier(req.serviceTier);
|
|
27944
27997
|
if (tier) body.service_tier = tier;
|
|
27945
|
-
|
|
27946
|
-
|
|
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;
|
|
27947
28004
|
}
|
|
27948
28005
|
if (req.tools?.length) {
|
|
27949
28006
|
body.tools = req.tools.map((t) => {
|
|
@@ -27953,7 +28010,10 @@ var OpenAIResponsesAdapter = class {
|
|
|
27953
28010
|
name: t.name,
|
|
27954
28011
|
description: t.description,
|
|
27955
28012
|
parameters: ensureAdditionalProperties(t.parameters),
|
|
27956
|
-
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 } : {}
|
|
27957
28017
|
};
|
|
27958
28018
|
}
|
|
27959
28019
|
const builtin = { type: t.type, ...t.params };
|
|
@@ -27981,9 +28041,13 @@ var OpenAIResponsesAdapter = class {
|
|
|
27981
28041
|
};
|
|
27982
28042
|
}
|
|
27983
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;
|
|
27984
28047
|
body.reasoning = {
|
|
27985
28048
|
effort: req.thinking.effort ?? "medium",
|
|
27986
|
-
summary:
|
|
28049
|
+
...summary !== null ? { summary } : {},
|
|
28050
|
+
...mode ? { mode } : {},
|
|
27987
28051
|
// Cross-turn reasoning persistence (gpt-5/o-series, Responses only).
|
|
27988
28052
|
...req.thinking.context ? { context: req.thinking.context } : {}
|
|
27989
28053
|
};
|
|
@@ -28127,9 +28191,8 @@ var OpenAIResponsesAdapter = class {
|
|
|
28127
28191
|
}
|
|
28128
28192
|
}
|
|
28129
28193
|
const status = r.status;
|
|
28130
|
-
const
|
|
28131
|
-
|
|
28132
|
-
});
|
|
28194
|
+
const incompleteReason = r.incomplete_details?.reason;
|
|
28195
|
+
const finishReason = incompleteReason === "content_filter" ? "content_filter" : extractFinishReason(toolCalls.length > 0, status, { incomplete: "length" });
|
|
28133
28196
|
if (!text && typeof r.output_text === "string") {
|
|
28134
28197
|
text = r.output_text;
|
|
28135
28198
|
if (text && content.length === 0) content.push({ type: "text", text });
|
|
@@ -28249,7 +28312,7 @@ var OpenAIResponsesAdapter = class {
|
|
|
28249
28312
|
outputTokens: output,
|
|
28250
28313
|
totalTokens: u.total_tokens ?? input + output,
|
|
28251
28314
|
cachedTokens: inputDetails.cached_tokens ?? 0,
|
|
28252
|
-
cacheWriteTokens: 0,
|
|
28315
|
+
cacheWriteTokens: inputDetails.cache_write_tokens ?? 0,
|
|
28253
28316
|
reasoningTokens: outputDetails.reasoning_tokens ?? 0
|
|
28254
28317
|
};
|
|
28255
28318
|
}
|
|
@@ -28339,8 +28402,9 @@ var OpenRouterAdapter = class extends OpenAIAdapter {
|
|
|
28339
28402
|
* `url_citation` annotations appear in the stream (the `:online` search signal). */
|
|
28340
28403
|
createStreamParser() {
|
|
28341
28404
|
let webSearchEmitted = false;
|
|
28405
|
+
const state = { toolIdByIndex: /* @__PURE__ */ new Map() };
|
|
28342
28406
|
return (event) => {
|
|
28343
|
-
const events = this.parseStreamEvent(event);
|
|
28407
|
+
const events = this.parseStreamEvent(event, state);
|
|
28344
28408
|
if (!webSearchEmitted) {
|
|
28345
28409
|
const choice = JSON.parse(event.data).choices?.[0];
|
|
28346
28410
|
const annotations = choice?.delta?.annotations ?? choice?.message?.annotations;
|
|
@@ -28511,8 +28575,8 @@ var XAIAdapter = class extends OpenAIAdapter {
|
|
|
28511
28575
|
}
|
|
28512
28576
|
return result;
|
|
28513
28577
|
}
|
|
28514
|
-
parseStreamEvent(event) {
|
|
28515
|
-
const events = super.parseStreamEvent(event);
|
|
28578
|
+
parseStreamEvent(event, state) {
|
|
28579
|
+
const events = super.parseStreamEvent(event, state);
|
|
28516
28580
|
try {
|
|
28517
28581
|
const data = JSON.parse(event.data);
|
|
28518
28582
|
const choices = data.choices ?? [];
|
|
@@ -28635,7 +28699,8 @@ var XAIMediaAdapter = class {
|
|
|
28635
28699
|
imageEditing: true,
|
|
28636
28700
|
audioGeneration: true,
|
|
28637
28701
|
videoGeneration: true,
|
|
28638
|
-
audioStreaming: true
|
|
28702
|
+
audioStreaming: true,
|
|
28703
|
+
videoExtension: true
|
|
28639
28704
|
};
|
|
28640
28705
|
}
|
|
28641
28706
|
authHeaders() {
|
|
@@ -28749,13 +28814,9 @@ var XAIMediaAdapter = class {
|
|
|
28749
28814
|
}
|
|
28750
28815
|
async submitVideo(req, fetch2) {
|
|
28751
28816
|
const model = req.model ?? "grok-imagine-video";
|
|
28752
|
-
const
|
|
28753
|
-
if (req.params?.duration) body.duration = req.params.duration;
|
|
28754
|
-
if (req.params?.aspectRatio) body.aspect_ratio = req.params.aspectRatio;
|
|
28755
|
-
if (req.params?.resolution) body.resolution = req.params.resolution;
|
|
28756
|
-
if (req.sourceImage) body.image = xaiImageRef(normalizeImageSource(req.sourceImage));
|
|
28817
|
+
const { url, body } = this.buildVideoSubmit(req, model);
|
|
28757
28818
|
const res = await fetch2({
|
|
28758
|
-
url
|
|
28819
|
+
url,
|
|
28759
28820
|
method: "POST",
|
|
28760
28821
|
headers: this.authHeaders(),
|
|
28761
28822
|
body,
|
|
@@ -28766,6 +28827,30 @@ var XAIMediaAdapter = class {
|
|
|
28766
28827
|
const data = res.body;
|
|
28767
28828
|
return data.request_id ?? data.id ?? "";
|
|
28768
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
|
+
}
|
|
28769
28854
|
async getVideoStatus(operationId, fetch2) {
|
|
28770
28855
|
const res = await fetch2({
|
|
28771
28856
|
url: `${this.baseURL}/v1/videos/${operationId}`,
|
|
@@ -28779,13 +28864,15 @@ var XAIMediaAdapter = class {
|
|
|
28779
28864
|
if (res.status >= 400) return { status: "failed", error: `HTTP ${res.status}` };
|
|
28780
28865
|
const data = res.body;
|
|
28781
28866
|
const state = data.status ?? "";
|
|
28782
|
-
|
|
28783
|
-
|
|
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 };
|
|
28784
28871
|
}
|
|
28785
|
-
if (state === "failed" || state === "error") {
|
|
28872
|
+
if (state === "failed" || state === "error" || state === "expired") {
|
|
28786
28873
|
return { status: "failed", error: data.error ?? "Unknown error" };
|
|
28787
28874
|
}
|
|
28788
|
-
return { status: "processing", progress
|
|
28875
|
+
return { status: "processing", progress };
|
|
28789
28876
|
}
|
|
28790
28877
|
async downloadVideo(operationId, fetch2) {
|
|
28791
28878
|
const statusRes = await fetch2({
|
|
@@ -28801,8 +28888,19 @@ var XAIMediaAdapter = class {
|
|
|
28801
28888
|
throw new Error(`xAI video download failed: HTTP ${statusRes.status}`);
|
|
28802
28889
|
}
|
|
28803
28890
|
const data = statusRes.body;
|
|
28804
|
-
const
|
|
28891
|
+
const video = data.video;
|
|
28892
|
+
const downloadUrl = video?.url ?? data.download_url ?? data.url;
|
|
28805
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;
|
|
28806
28904
|
const videoRes = await fetch2({
|
|
28807
28905
|
url: downloadUrl,
|
|
28808
28906
|
method: "GET",
|
|
@@ -28812,13 +28910,7 @@ var XAIMediaAdapter = class {
|
|
|
28812
28910
|
model: "",
|
|
28813
28911
|
responseType: "arraybuffer"
|
|
28814
28912
|
});
|
|
28815
|
-
return {
|
|
28816
|
-
data: videoRes.body,
|
|
28817
|
-
mimeType: "video/mp4",
|
|
28818
|
-
durationMs: data.duration ? data.duration * 1e3 : void 0,
|
|
28819
|
-
// Provider-reported cost (usage.cost_in_usd_ticks), when present.
|
|
28820
|
-
providerMeta: data.usage ? { usage: data.usage } : void 0
|
|
28821
|
-
};
|
|
28913
|
+
return { ...base, data: videoRes.body };
|
|
28822
28914
|
}
|
|
28823
28915
|
async cancelVideo(operationId, fetch2) {
|
|
28824
28916
|
await fetch2({
|
|
@@ -30231,7 +30323,11 @@ var AgentLoop = class _AgentLoop {
|
|
|
30231
30323
|
id: lastResponse?.id ?? `agent-${runId}`,
|
|
30232
30324
|
model: this.client.model,
|
|
30233
30325
|
content: finalContent,
|
|
30234
|
-
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",
|
|
30235
30331
|
usage: totalUsage,
|
|
30236
30332
|
text: finalText,
|
|
30237
30333
|
toolCalls: lastResponse?.toolCalls ?? [],
|
|
@@ -30440,7 +30536,11 @@ var AgentLoop = class _AgentLoop {
|
|
|
30440
30536
|
id: lastResponse?.id ?? `agent-${runId}`,
|
|
30441
30537
|
model: this.client.model,
|
|
30442
30538
|
content: finalContent,
|
|
30443
|
-
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",
|
|
30444
30544
|
usage: totalUsage,
|
|
30445
30545
|
text: finalText,
|
|
30446
30546
|
toolCalls: lastResponse?.toolCalls ?? [],
|
|
@@ -34765,9 +34865,13 @@ var MediaOutput = class {
|
|
|
34765
34865
|
}
|
|
34766
34866
|
async generateVideo(req) {
|
|
34767
34867
|
const adapter = this.getAdapter(req.provider);
|
|
34768
|
-
|
|
34868
|
+
const caps = adapter.capabilities();
|
|
34869
|
+
if (!caps.videoGeneration || !adapter.submitVideo) {
|
|
34769
34870
|
throw new Error(`Provider ${req.provider} does not support video generation`);
|
|
34770
34871
|
}
|
|
34872
|
+
if (req.sourceVideo && !caps.videoExtension) {
|
|
34873
|
+
throw new Error(`Provider ${req.provider} does not support video extension/editing`);
|
|
34874
|
+
}
|
|
34771
34875
|
const { trace, fetch: fetch2 } = this.tracedOp();
|
|
34772
34876
|
const operationId = await adapter.submitVideo(req, fetch2);
|
|
34773
34877
|
return this.pollVideoCompletion(adapter, operationId, req, fetch2, trace);
|
|
@@ -34800,7 +34904,8 @@ var MediaOutput = class {
|
|
|
34800
34904
|
width: raw.width,
|
|
34801
34905
|
height: raw.height,
|
|
34802
34906
|
durationMs: raw.durationMs,
|
|
34803
|
-
sampleRate: raw.sampleRate
|
|
34907
|
+
sampleRate: raw.sampleRate,
|
|
34908
|
+
sourceUrl: raw.sourceUrl
|
|
34804
34909
|
};
|
|
34805
34910
|
await this.mediaStore.save(id, raw.data, meta);
|
|
34806
34911
|
results.push({ id, type, mimeType: raw.mimeType, meta });
|
|
@@ -34833,6 +34938,15 @@ var MediaOutput = class {
|
|
|
34833
34938
|
const start = Date.now();
|
|
34834
34939
|
while (Date.now() - start < this.maxPollWaitMs) {
|
|
34835
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
|
+
}
|
|
34836
34950
|
if (status.status === "completed") {
|
|
34837
34951
|
const raw = await adapter.downloadVideo(operationId, fetch2);
|
|
34838
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
|
|
@@ -26,6 +26,15 @@ export declare function toDataUrl(ref: NormalizedImageRef): string;
|
|
|
26
26
|
export declare function openaiImageRef(ref: NormalizedImageRef): Record<string, string>;
|
|
27
27
|
/** xAI image-ref object (`/v1/images/edits` image, video image). */
|
|
28
28
|
export declare function xaiImageRef(ref: NormalizedImageRef): Record<string, string>;
|
|
29
|
+
/** xAI video-ref object (`/v1/videos/extensions` + `/v1/videos/edits` `video`
|
|
30
|
+
* field) — `{ url }` (public URL or base64 data-URL) or `{ file_id }`. Kept
|
|
31
|
+
* separate from the image path so it doesn't run image mime-sniffing over
|
|
32
|
+
* video bytes; the video mime is taken from the DataSource as declared. */
|
|
33
|
+
export declare function xaiVideoRef(src: DataSource): {
|
|
34
|
+
url: string;
|
|
35
|
+
} | {
|
|
36
|
+
file_id: string;
|
|
37
|
+
};
|
|
29
38
|
/** Google generateContent image part (inline base64 or Files-API file_uri). */
|
|
30
39
|
export declare function googleImagePart(ref: NormalizedImageRef): Record<string, unknown>;
|
|
31
40
|
/** Google Veo instance image (`:predictLongRunning` instances[].image).
|