@jeffusion/bungee-core 6.7.3 → 6.8.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/dist/config-storage-worker.js +1 -1
- package/dist/main.js +74 -66
- package/dist/plugins/codex-router/index.js +4 -4
- package/dist/plugins/codex-router/index.js.map +5 -5
- package/dist/plugins/llm-protocol-adapter/control.js +295 -47
- package/dist/plugins/llm-protocol-adapter/control.js.map +9 -8
- package/dist/plugins/llm-protocol-adapter/index.js +304 -48
- package/dist/plugins/llm-protocol-adapter/index.js.map +10 -9
- package/dist/plugins/llm-protocol-adapter/manifest.json +4 -0
- package/dist/plugins/token-metering/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -3565,7 +3565,7 @@ function definitionKey(value) {
|
|
|
3565
3565
|
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${definitionKey(value[key])}`).join(",")}}`;
|
|
3566
3566
|
return JSON.stringify(value);
|
|
3567
3567
|
}
|
|
3568
|
-
function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
3568
|
+
function decodeResponsesRequest(raw, protocol, capabilities = {}, options = {}) {
|
|
3569
3569
|
const body = record(raw, "Responses request");
|
|
3570
3570
|
if (capabilities.agentMessageDelivery !== undefined && capabilities.agentMessageDelivery !== "plaintext")
|
|
3571
3571
|
fail("invalid_payload", "Unknown agent message delivery policy", "agentMessageDelivery");
|
|
@@ -3832,12 +3832,16 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
3832
3832
|
if (item.content != null)
|
|
3833
3833
|
fail("unsupported_reasoning", "Reasoning content requires an explicit history mapping", `${messageParam}.content`);
|
|
3834
3834
|
const summary = list(item.summary, "reasoning summary");
|
|
3835
|
-
|
|
3835
|
+
const text = summary.map((part) => {
|
|
3836
3836
|
const p = record(part, "reasoning summary");
|
|
3837
3837
|
if (p.type !== "summary_text")
|
|
3838
3838
|
fail("unsupported_reasoning", "Unknown reasoning summary");
|
|
3839
3839
|
return string(p.text, "summary text");
|
|
3840
3840
|
}).join("");
|
|
3841
|
+
if (options.preserveContentOrder)
|
|
3842
|
+
addMessage({ role: "assistant", content: null, reasoning_content: text, ...item.id === undefined ? {} : { id: string(item.id, "item.id", true) } });
|
|
3843
|
+
else
|
|
3844
|
+
pendingReasoning += text;
|
|
3841
3845
|
return;
|
|
3842
3846
|
}
|
|
3843
3847
|
if (item.type === "function_call" || item.type === "custom_tool_call") {
|
|
@@ -3852,7 +3856,7 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
3852
3856
|
argumentObject(args);
|
|
3853
3857
|
const call = { id, type: "function", function: { name: resolve(item, custom), arguments: args } };
|
|
3854
3858
|
const previous = chat.at(-1);
|
|
3855
|
-
if (previous?.role === "assistant") {
|
|
3859
|
+
if (previous?.role === "assistant" && !options.preserveContentOrder) {
|
|
3856
3860
|
previous.tool_calls = [...previous.tool_calls ?? [], call];
|
|
3857
3861
|
if (pendingReasoning)
|
|
3858
3862
|
previous.reasoning_content = pendingReasoning;
|
|
@@ -3876,6 +3880,8 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
3876
3880
|
if (!["user", "assistant", "system", "developer"].includes(role))
|
|
3877
3881
|
fail("unsupported_content", "Unknown message role");
|
|
3878
3882
|
const message = { role, content: normalizeContent(item.content, role) };
|
|
3883
|
+
if (options.preserveContentOrder && item.id !== undefined)
|
|
3884
|
+
message.id = string(item.id, "item.id", true);
|
|
3879
3885
|
if (pendingReasoning) {
|
|
3880
3886
|
if (role !== "assistant")
|
|
3881
3887
|
fail("unsupported_reasoning", "Reasoning must precede an assistant item");
|
|
@@ -4416,6 +4422,9 @@ function encodeResponsesResult(raw, protocol, model, toolNames = new Map, config
|
|
|
4416
4422
|
fail("missing_terminal", "JSON response has no terminal result");
|
|
4417
4423
|
return record(end.response, "Responses result");
|
|
4418
4424
|
}
|
|
4425
|
+
// packages/llms/src/protocol-session/gemini.ts
|
|
4426
|
+
import { createHash } from "crypto";
|
|
4427
|
+
|
|
4419
4428
|
// packages/llms/src/protocol-session/validation.ts
|
|
4420
4429
|
function fields2(value, allowed, path, code = "unsupported_request") {
|
|
4421
4430
|
for (const key of Object.keys(value))
|
|
@@ -4460,6 +4469,145 @@ function textParts(raw, path, style, role) {
|
|
|
4460
4469
|
});
|
|
4461
4470
|
}
|
|
4462
4471
|
|
|
4472
|
+
// packages/llms/src/protocol-session/gemini.ts
|
|
4473
|
+
var signatureSeparator = "__thought__";
|
|
4474
|
+
function checkedSignature(value, maxBytes) {
|
|
4475
|
+
const signature = bounded(string(value, "thoughtSignature", true), maxBytes, "thoughtSignature");
|
|
4476
|
+
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(signature) || signature.length % 4 === 1 || signature.includes("=") && signature.length % 4 !== 0 || Buffer.from(signature, "base64").toString("base64").replace(/=+$/, "") !== signature.replace(/=+$/, ""))
|
|
4477
|
+
fail("invalid_tool_arguments", "Invalid Gemini thought signature encoding", "thoughtSignature");
|
|
4478
|
+
return signature;
|
|
4479
|
+
}
|
|
4480
|
+
var envelopePrefix = "bungee_gemini:";
|
|
4481
|
+
var geminiEnvelopeBytes = (payloadBytes) => envelopePrefix.length + Math.ceil(payloadBytes * 4 / 3);
|
|
4482
|
+
var geminiTextHash = (text) => createHash("sha256").update(text).digest("base64url");
|
|
4483
|
+
function geminiSignatureItem(items, budget) {
|
|
4484
|
+
const payload = serialized({ version: 1, items }, budget.maxArgumentBytes, "Gemini signatures");
|
|
4485
|
+
return {
|
|
4486
|
+
type: "reasoning",
|
|
4487
|
+
id: `rs_gemini_${crypto.randomUUID().replaceAll("-", "")}`,
|
|
4488
|
+
summary: [],
|
|
4489
|
+
encrypted_content: bounded(envelopePrefix + Buffer.from(payload).toString("base64url"), budget.maxArgumentBytes, "Gemini signatures")
|
|
4490
|
+
};
|
|
4491
|
+
}
|
|
4492
|
+
function readGeminiSignatures(item, budget) {
|
|
4493
|
+
fields2(item, ["type", "id", "summary", "encrypted_content", "status"], "Gemini signatures", "unsupported_reasoning");
|
|
4494
|
+
if (item.type !== "reasoning" || list(item.summary, "summary").length)
|
|
4495
|
+
fail("unsupported_reasoning", "Invalid Gemini signature carrier");
|
|
4496
|
+
string(item.id, "signature item id", true);
|
|
4497
|
+
const opaque = bounded(string(item.encrypted_content, "encrypted_content", true), budget.maxArgumentBytes, "Gemini signatures");
|
|
4498
|
+
if (!opaque.startsWith(envelopePrefix))
|
|
4499
|
+
fail("unsupported_reasoning", "Native encrypted reasoning cannot be restored to Gemini");
|
|
4500
|
+
const encoded = opaque.slice(envelopePrefix.length), bytes = Buffer.from(encoded, "base64url");
|
|
4501
|
+
if (!encoded || bytes.toString("base64url") !== encoded)
|
|
4502
|
+
fail("unsupported_reasoning", "Invalid Gemini signature carrier encoding");
|
|
4503
|
+
let raw;
|
|
4504
|
+
try {
|
|
4505
|
+
raw = JSON.parse(bytes.toString("utf8"));
|
|
4506
|
+
} catch {
|
|
4507
|
+
fail("unsupported_reasoning", "Invalid Gemini signature carrier JSON");
|
|
4508
|
+
}
|
|
4509
|
+
const body = record(raw, "Gemini signatures");
|
|
4510
|
+
fields2(body, ["version", "items"], "Gemini signatures", "unsupported_reasoning");
|
|
4511
|
+
if (body.version !== 1)
|
|
4512
|
+
fail("unsupported_reasoning", "Unknown Gemini signature format");
|
|
4513
|
+
const ids = new Set;
|
|
4514
|
+
let partCount = 0;
|
|
4515
|
+
const result = list(body.items, "signature items").map((raw) => {
|
|
4516
|
+
const item = record(raw, "signature item");
|
|
4517
|
+
fields2(item, ["id", "thought", "parts"], "signature item", "unsupported_reasoning");
|
|
4518
|
+
const id = string(item.id, "signed item id", true);
|
|
4519
|
+
if (ids.has(id) || typeof item.thought !== "boolean")
|
|
4520
|
+
fail("unsupported_reasoning", "Invalid signed item identity");
|
|
4521
|
+
ids.add(id);
|
|
4522
|
+
const parts = list(item.parts, "signed parts").map((raw) => {
|
|
4523
|
+
if (++partCount > budget.maxItems)
|
|
4524
|
+
fail("resource_limit", "Too many signed Gemini parts");
|
|
4525
|
+
const part = record(raw, "signed part");
|
|
4526
|
+
fields2(part, ["length", "hash", "signature", "textAbsent"], "signed part", "unsupported_reasoning");
|
|
4527
|
+
const length = integer(part.length, "signed part length"), hash = string(part.hash, "signed part hash", true);
|
|
4528
|
+
if (!/^[A-Za-z0-9_-]{43}$/.test(hash))
|
|
4529
|
+
fail("unsupported_reasoning", "Invalid signed part hash");
|
|
4530
|
+
const signature = part.signature === undefined ? undefined : checkedSignature(part.signature, budget.maxArgumentBytes);
|
|
4531
|
+
if (part.textAbsent !== undefined && (part.textAbsent !== true || length !== 0 || signature === undefined))
|
|
4532
|
+
fail("unsupported_reasoning", "Invalid empty signed part");
|
|
4533
|
+
return { length, hash, ...signature === undefined ? {} : { signature }, ...part.textAbsent ? { textAbsent: true } : {} };
|
|
4534
|
+
});
|
|
4535
|
+
if (!parts.some((p) => p.signature !== undefined))
|
|
4536
|
+
fail("unsupported_reasoning", "Signature carrier has no signature");
|
|
4537
|
+
return { id, thought: item.thought, parts };
|
|
4538
|
+
});
|
|
4539
|
+
if (!result.length || result.length > budget.maxItems)
|
|
4540
|
+
fail("unsupported_reasoning", "Invalid signature item count");
|
|
4541
|
+
return result;
|
|
4542
|
+
}
|
|
4543
|
+
function restoreGeminiHistory(input, budget) {
|
|
4544
|
+
const original = typeof input === "string" ? [] : list(input, "input").map((raw) => record(raw, "input item"));
|
|
4545
|
+
const carriers = new Set, parts = new Map, ids = new Map;
|
|
4546
|
+
original.forEach((item, i) => {
|
|
4547
|
+
if (typeof item.id === "string") {
|
|
4548
|
+
if (ids.has(item.id))
|
|
4549
|
+
fail("invalid_tool_history", "Duplicate history item id", "input.id");
|
|
4550
|
+
ids.set(item.id, i);
|
|
4551
|
+
}
|
|
4552
|
+
});
|
|
4553
|
+
original.forEach((carrier, index) => {
|
|
4554
|
+
if (carrier.encrypted_content == null)
|
|
4555
|
+
return;
|
|
4556
|
+
const signed = readGeminiSignatures(carrier, budget);
|
|
4557
|
+
carriers.add(carrier);
|
|
4558
|
+
let previous = -1;
|
|
4559
|
+
for (const item of signed) {
|
|
4560
|
+
const at = ids.get(item.id);
|
|
4561
|
+
if (at === undefined || at >= index || at <= previous || parts.has(item.id))
|
|
4562
|
+
fail("unsupported_reasoning", "Signed history reference is missing or ambiguous");
|
|
4563
|
+
previous = at;
|
|
4564
|
+
const source = original[at];
|
|
4565
|
+
if (item.thought ? source.type !== "reasoning" || source.encrypted_content != null : source.role !== "assistant" || source.type !== undefined && source.type !== "message")
|
|
4566
|
+
fail("unsupported_reasoning", "Signed history item kind changed");
|
|
4567
|
+
const content = item.thought ? list(source.summary, "summary") : typeof source.content === "string" ? [{ type: "output_text", text: source.content }] : list(source.content, "content");
|
|
4568
|
+
const text = content.map((raw) => {
|
|
4569
|
+
const p = record(raw, "signed text");
|
|
4570
|
+
if (p.type !== (item.thought ? "summary_text" : "output_text") && !(p.type === "input_text" && !item.thought))
|
|
4571
|
+
fail("unsupported_reasoning", "Signed history content kind changed");
|
|
4572
|
+
return string(p.text, "signed text");
|
|
4573
|
+
}).join("");
|
|
4574
|
+
let offset = 0;
|
|
4575
|
+
const restored = item.parts.map((part) => {
|
|
4576
|
+
const value = text.slice(offset, offset + part.length);
|
|
4577
|
+
offset += part.length;
|
|
4578
|
+
if (value.length !== part.length || geminiTextHash(value) !== part.hash)
|
|
4579
|
+
fail("unsupported_reasoning", "Signed history text changed");
|
|
4580
|
+
return { ...part.textAbsent ? {} : { text: value }, ...item.thought ? { thought: true } : {}, ...part.signature === undefined ? {} : { thoughtSignature: part.signature } };
|
|
4581
|
+
});
|
|
4582
|
+
if (offset !== text.length)
|
|
4583
|
+
fail("unsupported_reasoning", "Signed history text was not fully consumed");
|
|
4584
|
+
parts.set(item.id, restored);
|
|
4585
|
+
}
|
|
4586
|
+
});
|
|
4587
|
+
return {
|
|
4588
|
+
input: typeof input === "string" ? input : original.filter((item) => !carriers.has(item)),
|
|
4589
|
+
parts,
|
|
4590
|
+
canonicalHistory(decoded) {
|
|
4591
|
+
if (!carriers.size)
|
|
4592
|
+
return decoded;
|
|
4593
|
+
let next = 0;
|
|
4594
|
+
return original.flatMap((item) => carriers.has(item) ? [item] : item.type === "additional_tools" ? [] : [decoded[next++]]);
|
|
4595
|
+
}
|
|
4596
|
+
};
|
|
4597
|
+
}
|
|
4598
|
+
function encodeGeminiCallId(id, signature, maxBytes) {
|
|
4599
|
+
if (id.includes(signatureSeparator))
|
|
4600
|
+
fail("invalid_tool_arguments", "Gemini call id contains the signature separator", "functionCall.id");
|
|
4601
|
+
return signature === undefined ? id : `${id}${signatureSeparator}${checkedSignature(signature, maxBytes)}`;
|
|
4602
|
+
}
|
|
4603
|
+
function decodeGeminiCallId(value, maxBytes) {
|
|
4604
|
+
const separator = value.indexOf(signatureSeparator);
|
|
4605
|
+
if (separator < 0)
|
|
4606
|
+
return { id: value };
|
|
4607
|
+
const id = string(value.slice(0, separator), "functionCall.id", true);
|
|
4608
|
+
return { id, thoughtSignature: checkedSignature(value.slice(separator + signatureSeparator.length), maxBytes) };
|
|
4609
|
+
}
|
|
4610
|
+
|
|
4463
4611
|
// packages/llms/src/protocol-session/schema.ts
|
|
4464
4612
|
function geminiSchemaToJson(raw, path, code = "unsupported_request", depth = 0) {
|
|
4465
4613
|
if (depth > 64)
|
|
@@ -4893,6 +5041,9 @@ function convertProtocolRequest(raw, context) {
|
|
|
4893
5041
|
b.stream = context.streaming;
|
|
4894
5042
|
}
|
|
4895
5043
|
validateCanonical(b, context);
|
|
5044
|
+
const geminiHistory = context.sourceProtocol === "responses" && context.targetProtocol === "gemini_generate_content" ? restoreGeminiHistory(b.input, limits(context.capabilities?.limits)) : undefined;
|
|
5045
|
+
if (geminiHistory)
|
|
5046
|
+
b.input = geminiHistory.input;
|
|
4896
5047
|
const cap = { ...context.capabilities, agentMessageDelivery: context.agentMessageDelivery ?? context.capabilities?.agentMessageDelivery, omitOptionalWebSearch: true, preserveToolIdentityDescription: true, preserveRefusalContent: true }, policy = context.reasoningPolicy;
|
|
4897
5048
|
const reasoning = b.reasoning === undefined ? undefined : record(b.reasoning, "reasoning");
|
|
4898
5049
|
const sourceThinking = b.sourceThinking;
|
|
@@ -4918,10 +5069,10 @@ function convertProtocolRequest(raw, context) {
|
|
|
4918
5069
|
if (sourceThinking !== undefined && reasoning?.effort === undefined && policy?.targetEffort !== undefined)
|
|
4919
5070
|
mappedEffort = string(policy.targetEffort, "reasoningPolicy.targetEffort", true);
|
|
4920
5071
|
const target = context.targetProtocol;
|
|
4921
|
-
if (target === "gemini_generate_content" && reasoning?.effort !== undefined && policy?.geminiThinkingBudget === undefined && policy?.geminiThinkingLevel === undefined)
|
|
5072
|
+
if (target === "gemini_generate_content" && reasoning?.effort !== undefined && mappedEffort === undefined && policy?.geminiThinkingBudget === undefined && policy?.geminiThinkingLevel === undefined)
|
|
4922
5073
|
fail("unsupported_reasoning", "Gemini requires an explicit thinking budget or level", "reasoning.effort");
|
|
4923
5074
|
if (sourceThinking !== undefined) {
|
|
4924
|
-
const represented = target === "responses" || target === "chat_completions" ? mappedEffort !== undefined : target === "anthropic_messages" ? mappedEffort !== undefined || policy?.anthropicThinkingMode !== undefined || policy?.anthropicThinkingBudget !== undefined : policy?.geminiThinkingBudget !== undefined || policy?.geminiThinkingLevel !== undefined;
|
|
5075
|
+
const represented = target === "responses" || target === "chat_completions" ? mappedEffort !== undefined : target === "anthropic_messages" ? mappedEffort !== undefined || policy?.anthropicThinkingMode !== undefined || policy?.anthropicThinkingBudget !== undefined : mappedEffort !== undefined || policy?.geminiThinkingBudget !== undefined || policy?.geminiThinkingLevel !== undefined;
|
|
4925
5076
|
if (!represented)
|
|
4926
5077
|
fail("unsupported_reasoning", "Source thinking mode needs an explicit target wire policy", "thinking");
|
|
4927
5078
|
}
|
|
@@ -4980,9 +5131,13 @@ function convertProtocolRequest(raw, context) {
|
|
|
4980
5131
|
const decodingTarget = target === "gemini_generate_content" ? "chat_completions" : target;
|
|
4981
5132
|
if (target === "gemini_generate_content" && reasoning)
|
|
4982
5133
|
b.reasoning = { ...reasoning, effort: undefined };
|
|
4983
|
-
const decoded = decodeResponsesRequest(b, decodingTarget, { ...cap, reasoningHistory: target !== "anthropic_messages" && cap.reasoningHistory === true, reasoningEffort: target === "chat_completions" && cap.reasoningEffort === true, anthropicThinkingBudget: undefined });
|
|
5134
|
+
const decoded = decodeResponsesRequest(b, decodingTarget, { ...cap, reasoningHistory: target !== "anthropic_messages" && cap.reasoningHistory === true, reasoningEffort: target === "chat_completions" && cap.reasoningEffort === true, anthropicThinkingBudget: undefined }, { preserveContentOrder: target === "gemini_generate_content" });
|
|
4984
5135
|
const diagnostics = decoded.diagnostics;
|
|
4985
|
-
|
|
5136
|
+
if (target === "gemini_generate_content" && decoded.body.parallel_tool_calls === true) {
|
|
5137
|
+
delete decoded.body.parallel_tool_calls;
|
|
5138
|
+
diagnostics.push({ param: "parallel_tool_calls", action: "omitted", reason: "gemini_parallel_tool_calls_allowed" });
|
|
5139
|
+
}
|
|
5140
|
+
const body = target === "gemini_generate_content" ? renderGemini(decoded.body, context, geminiHistory?.parts, diagnostics) : decoded.body;
|
|
4986
5141
|
if (stop !== undefined)
|
|
4987
5142
|
body[target === "chat_completions" ? "stop" : target === "anthropic_messages" ? "stop_sequences" : "generationConfig"] = target === "gemini_generate_content" ? { ...record(body.generationConfig ?? {}, "generationConfig"), stopSequences: typeof stop === "string" ? [stop] : stop } : typeof stop === "string" ? [stop] : stop;
|
|
4988
5143
|
if (target === "anthropic_messages") {
|
|
@@ -5004,18 +5159,24 @@ function convertProtocolRequest(raw, context) {
|
|
|
5004
5159
|
body.thinking = { type: "enabled", budget_tokens: budget };
|
|
5005
5160
|
}
|
|
5006
5161
|
}
|
|
5007
|
-
if (target === "gemini_generate_content" && (policy?.geminiThinkingBudget !== undefined || policy?.geminiThinkingLevel !== undefined)) {
|
|
5008
|
-
|
|
5162
|
+
if (target === "gemini_generate_content" && (mappedEffort !== undefined || policy?.geminiThinkingBudget !== undefined || policy?.geminiThinkingLevel !== undefined)) {
|
|
5163
|
+
const level = policy?.geminiThinkingLevel ?? mappedEffort;
|
|
5164
|
+
if (policy?.geminiThinkingBudget !== undefined && level !== undefined)
|
|
5009
5165
|
fail("unsupported_reasoning", "Choose one Gemini thinking policy", "reasoningPolicy");
|
|
5010
|
-
|
|
5166
|
+
if (level !== undefined && !["minimal", "low", "medium", "high"].includes(level))
|
|
5167
|
+
fail("unsupported_reasoning", "Unknown Gemini thinking level", "reasoning.effort");
|
|
5168
|
+
if (mappedEffort !== undefined && policy?.geminiThinkingLevel !== undefined && mappedEffort !== policy.geminiThinkingLevel)
|
|
5169
|
+
fail("unsupported_reasoning", "Conflicting Gemini thinking levels", "reasoning.effort");
|
|
5170
|
+
const thinkingConfig = policy?.geminiThinkingBudget !== undefined ? { thinkingBudget: integer(policy.geminiThinkingBudget, "reasoningPolicy.geminiThinkingBudget", -1) } : { thinkingLevel: string(level, "reasoningPolicy.geminiThinkingLevel", true) };
|
|
5011
5171
|
body.generationConfig = { ...record(body.generationConfig ?? {}, "generationConfig"), thinkingConfig };
|
|
5012
5172
|
}
|
|
5013
5173
|
if (mappedEffort !== undefined || sourceThinking !== undefined || policy?.anthropicThinkingBudget !== undefined)
|
|
5014
5174
|
diagnostics.push({ param: "reasoning", action: "mapped", reason: "explicit_verified_wire_policy" });
|
|
5015
|
-
return { body, canonicalHistory: decoded.canonicalInput, diagnostics, toolNames: decoded.toolNames, streaming: b.stream === true };
|
|
5175
|
+
return { body, canonicalHistory: geminiHistory?.canonicalHistory(decoded.canonicalInput) ?? decoded.canonicalInput, diagnostics, toolNames: decoded.toolNames, streaming: b.stream === true };
|
|
5016
5176
|
}
|
|
5017
|
-
function renderGemini(chat,
|
|
5177
|
+
function renderGemini(chat, context, signedParts, diagnostics) {
|
|
5018
5178
|
const result = { contents: [] }, contents = result.contents, system = [], calls = new Map;
|
|
5179
|
+
const nativeIds = new Set;
|
|
5019
5180
|
const append = (role, parts) => {
|
|
5020
5181
|
const prev = contents.at(-1);
|
|
5021
5182
|
if (prev?.role === role)
|
|
@@ -5024,14 +5185,22 @@ function renderGemini(chat, jsonSchema) {
|
|
|
5024
5185
|
contents.push({ role, parts });
|
|
5025
5186
|
};
|
|
5026
5187
|
for (const m of chat.messages) {
|
|
5188
|
+
const signed = signedParts?.get(m.id);
|
|
5189
|
+
if (signed) {
|
|
5190
|
+
append("model", signed);
|
|
5191
|
+
continue;
|
|
5192
|
+
}
|
|
5027
5193
|
const parts = m.content == null ? [] : typeof m.content === "string" ? [{ text: m.content }] : m.content.map((p) => {
|
|
5028
5194
|
if (p.type !== "text")
|
|
5029
5195
|
fail("unsupported_content", "This content cannot cross to Gemini", "input");
|
|
5030
5196
|
return { text: p.text };
|
|
5031
5197
|
});
|
|
5032
5198
|
if (m.role === "system" || m.role === "developer") {
|
|
5033
|
-
if (contents.length)
|
|
5034
|
-
|
|
5199
|
+
if (contents.length) {
|
|
5200
|
+
if (context.sourceProtocol !== "responses")
|
|
5201
|
+
fail("unsupported_content", "Mid-conversation instructions cannot be relocated", "input");
|
|
5202
|
+
diagnostics.push({ param: "input", action: "mapped", reason: "gemini_mid_conversation_instructions_consolidated" });
|
|
5203
|
+
}
|
|
5035
5204
|
system.push(...parts);
|
|
5036
5205
|
continue;
|
|
5037
5206
|
}
|
|
@@ -5041,7 +5210,8 @@ function renderGemini(chat, jsonSchema) {
|
|
|
5041
5210
|
fail("invalid_tool_history", "Unknown tool result", "input");
|
|
5042
5211
|
if (m.is_error === true)
|
|
5043
5212
|
fail("unsupported_content", "Tool error authority cannot be represented by Gemini", "input");
|
|
5044
|
-
|
|
5213
|
+
const { id } = context.sourceProtocol === "responses" ? decodeGeminiCallId(m.tool_call_id, limits(context.capabilities?.limits).maxArgumentBytes) : { id: m.tool_call_id };
|
|
5214
|
+
append("user", [{ functionResponse: { id, name, response: { output: m.content } } }]);
|
|
5045
5215
|
continue;
|
|
5046
5216
|
}
|
|
5047
5217
|
if (m.reasoning_content !== undefined)
|
|
@@ -5049,10 +5219,20 @@ function renderGemini(chat, jsonSchema) {
|
|
|
5049
5219
|
for (const c of m.tool_calls ?? []) {
|
|
5050
5220
|
const fn = record(c.function, "function");
|
|
5051
5221
|
calls.set(c.id, fn.name);
|
|
5052
|
-
|
|
5222
|
+
const { id, thoughtSignature } = context.sourceProtocol === "responses" ? decodeGeminiCallId(c.id, limits(context.capabilities?.limits).maxArgumentBytes) : { id: c.id, thoughtSignature: undefined };
|
|
5223
|
+
if (nativeIds.has(id))
|
|
5224
|
+
fail("invalid_tool_history", "Duplicate decoded Gemini call id", "call_id");
|
|
5225
|
+
nativeIds.add(id);
|
|
5226
|
+
parts.push({ functionCall: { id, name: fn.name, args: argumentObject(fn.arguments) }, ...thoughtSignature === undefined ? {} : { thoughtSignature } });
|
|
5053
5227
|
}
|
|
5054
5228
|
append(m.role === "assistant" ? "model" : "user", parts);
|
|
5055
5229
|
}
|
|
5230
|
+
if (chat.parallel_tool_calls !== undefined) {
|
|
5231
|
+
if (context.sourceProtocol !== "responses" || chat.parallel_tool_calls !== false)
|
|
5232
|
+
fail("unsupported_request", "Gemini cannot enforce parallel tool preference", "parallel_tool_calls");
|
|
5233
|
+
system.push({ text: "Call at most one tool per response." });
|
|
5234
|
+
diagnostics.push({ param: "parallel_tool_calls", action: "mapped", reason: "gemini_serial_tool_calls_prompt_only" });
|
|
5235
|
+
}
|
|
5056
5236
|
if (system.length)
|
|
5057
5237
|
result.systemInstruction = { parts: system };
|
|
5058
5238
|
const generation = {};
|
|
@@ -5061,12 +5241,10 @@ function renderGemini(chat, jsonSchema) {
|
|
|
5061
5241
|
generation[to] = chat[from];
|
|
5062
5242
|
if (chat.metadata !== undefined)
|
|
5063
5243
|
fail("unsupported_request", "Gemini has no compatible request metadata", "metadata");
|
|
5064
|
-
if (chat.parallel_tool_calls !== undefined)
|
|
5065
|
-
fail("unsupported_request", "Gemini cannot enforce parallel tool preference", "parallel_tool_calls");
|
|
5066
5244
|
if (chat.response_format !== undefined) {
|
|
5067
5245
|
const f = record(chat.response_format, "format");
|
|
5068
5246
|
if (f.type === "json_schema") {
|
|
5069
|
-
if (!
|
|
5247
|
+
if (!context.capabilities?.geminiJsonSchema)
|
|
5070
5248
|
fail("unsupported_request", "Target JSON schema capability is required", "text.format");
|
|
5071
5249
|
const s = record(f.json_schema, "schema");
|
|
5072
5250
|
if (s.strict === false)
|
|
@@ -5211,7 +5389,11 @@ function validateCanonicalResponse(raw, context, itemOnly = false) {
|
|
|
5211
5389
|
fail("unsupported_content", "Output annotations cannot cross protocols", path);
|
|
5212
5390
|
}
|
|
5213
5391
|
} else if (t.type === "reasoning") {
|
|
5214
|
-
if (t.encrypted_content != null
|
|
5392
|
+
if (t.encrypted_content != null && context.sourceProtocol === "responses" && context.targetProtocol === "gemini_generate_content")
|
|
5393
|
+
readGeminiSignatures(t, limits(context.capabilities?.limits));
|
|
5394
|
+
else if (t.encrypted_content != null)
|
|
5395
|
+
fail("unsupported_reasoning", "Opaque reasoning cannot cross protocols", path);
|
|
5396
|
+
if (t.content != null)
|
|
5215
5397
|
fail("unsupported_reasoning", "Opaque or raw reasoning output cannot cross protocols", path);
|
|
5216
5398
|
for (const rawPart of list(t.summary, "summary")) {
|
|
5217
5399
|
const p = record(rawPart, "summary");
|
|
@@ -5334,20 +5516,28 @@ function responseFromCanonical(raw, protocol, context) {
|
|
|
5334
5516
|
|
|
5335
5517
|
class GeminiCanonicalDecoder {
|
|
5336
5518
|
context;
|
|
5519
|
+
streaming;
|
|
5337
5520
|
encoder;
|
|
5338
5521
|
serial = 0;
|
|
5339
5522
|
ended = false;
|
|
5340
5523
|
tools = false;
|
|
5341
5524
|
ids = new Set;
|
|
5342
|
-
|
|
5525
|
+
sequence = 0;
|
|
5526
|
+
contentId;
|
|
5527
|
+
partCount = 0;
|
|
5528
|
+
replayBytes = Buffer.byteLength(JSON.stringify({ version: 1, items: [] }));
|
|
5529
|
+
signedItems = 0;
|
|
5530
|
+
signed = new Map;
|
|
5531
|
+
constructor(context, names, streaming = true) {
|
|
5343
5532
|
this.context = context;
|
|
5344
|
-
this.
|
|
5533
|
+
this.streaming = streaming;
|
|
5534
|
+
this.encoder = new ResponsesEventEncoder("chat_completions", context.responseModel ?? context.model, names, context.capabilities?.limits, { preserveContentOrder: true, agentMessageDelivery: context.capabilities?.agentMessageDelivery });
|
|
5345
5535
|
}
|
|
5346
5536
|
push(raw) {
|
|
5347
5537
|
const b = record(raw, "Gemini response");
|
|
5348
5538
|
fields2(b, ["candidates", "usageMetadata", "modelVersion", "responseId", "promptFeedback", "error"], "", "unsupported_response");
|
|
5349
5539
|
if (b.error !== undefined)
|
|
5350
|
-
return this.encoder.push({ error: safeProtocolError() });
|
|
5540
|
+
return this.decorate(this.encoder.push({ error: safeProtocolError() }));
|
|
5351
5541
|
const events = [];
|
|
5352
5542
|
if (b.promptFeedback !== undefined) {
|
|
5353
5543
|
const p = record(b.promptFeedback, "promptFeedback");
|
|
@@ -5365,34 +5555,69 @@ class GeminiCanonicalDecoder {
|
|
|
5365
5555
|
fields2(c, ["index", "content", "finishReason", "safetyRatings", "finishMessage"], "candidates", "unsupported_response");
|
|
5366
5556
|
if (c.index !== undefined && c.index !== 0)
|
|
5367
5557
|
fail("unsupported_response", "Only candidate index zero is supported", "candidates.index");
|
|
5368
|
-
if (c.content !== undefined) {
|
|
5558
|
+
if (c.content !== undefined && c.content !== null) {
|
|
5369
5559
|
const content = record(c.content, "content");
|
|
5370
5560
|
fields2(content, ["role", "parts"], "candidates.content", "unsupported_content");
|
|
5371
5561
|
if (content.role !== undefined && content.role !== "model")
|
|
5372
5562
|
fail("unsupported_response", "Response role must be model", "candidates.content.role");
|
|
5373
|
-
const parts = list(content.parts, "parts");
|
|
5563
|
+
const parts = list(content.parts ?? [], "parts");
|
|
5374
5564
|
if (this.ended && parts.length)
|
|
5375
5565
|
fail("invalid_stream", "Content arrived after Gemini terminal");
|
|
5376
5566
|
for (const [i, rawPart] of parts.entries()) {
|
|
5377
5567
|
const p = record(rawPart, "part");
|
|
5378
5568
|
fields2(p, ["text", "thought", "functionCall", "thoughtSignature"], "candidates.content.parts", "unsupported_content");
|
|
5379
|
-
if (p.thoughtSignature !== undefined)
|
|
5569
|
+
if (p.thoughtSignature !== undefined && this.context.sourceProtocol !== "responses")
|
|
5380
5570
|
fail("unsupported_reasoning", "Thought signatures cannot cross protocols", "candidates.content.parts.thoughtSignature");
|
|
5381
5571
|
if (p.functionCall !== undefined) {
|
|
5382
5572
|
if (p.text !== undefined || p.thought !== undefined)
|
|
5383
5573
|
fail("unsupported_content", "Mixed Gemini part kinds", "candidates.content.parts");
|
|
5384
5574
|
const f = record(p.functionCall, "functionCall");
|
|
5385
5575
|
fields2(f, ["id", "name", "args"], "functionCall", "unsupported_tool");
|
|
5386
|
-
const index = this.serial++,
|
|
5387
|
-
if (this.ids.has(
|
|
5576
|
+
const index = this.serial++, originalId = f.id === undefined ? `call_gemini_${crypto.randomUUID().replaceAll("-", "")}` : string(f.id, "functionCall.id", true);
|
|
5577
|
+
if (this.ids.has(originalId))
|
|
5388
5578
|
fail("invalid_tool_arguments", "Duplicate Gemini call id", "functionCall.id");
|
|
5389
|
-
this.ids.add(
|
|
5579
|
+
this.ids.add(originalId);
|
|
5580
|
+
const id = this.context.sourceProtocol === "responses" ? encodeGeminiCallId(originalId, p.thoughtSignature, limits(this.context.capabilities?.limits).maxArgumentBytes) : originalId;
|
|
5390
5581
|
this.tools = true;
|
|
5391
5582
|
events.push(...this.encoder.push({ choices: [{ delta: { tool_calls: [{ index, id, type: "function", function: { name: string(f.name, "functionCall.name", true), arguments: JSON.stringify(record(f.args ?? {}, "functionCall.args")) } }] } }] }));
|
|
5392
5583
|
} else {
|
|
5393
5584
|
if (p.thought !== undefined && typeof p.thought !== "boolean")
|
|
5394
5585
|
fail("invalid_payload", "Thought flag must be boolean", "thought");
|
|
5395
|
-
|
|
5586
|
+
const text = string(p.text ?? (p.thoughtSignature === undefined ? undefined : ""), `parts[${i}].text`);
|
|
5587
|
+
const output = this.encoder.push({ choices: [{ delta: { [p.thought === true ? "reasoning_content" : "content"]: text } }] });
|
|
5588
|
+
const added = output.find((e) => e.type === "response.output_item.added");
|
|
5589
|
+
if (added)
|
|
5590
|
+
this.contentId = record(added.item, "item").id;
|
|
5591
|
+
const id = this.contentId;
|
|
5592
|
+
let item = this.signed.get(id);
|
|
5593
|
+
if (!item) {
|
|
5594
|
+
item = { id, thought: p.thought === true, parts: [], hasSignature: false, metadataBytes: Buffer.byteLength(JSON.stringify({ id, thought: p.thought === true, parts: [] })) };
|
|
5595
|
+
this.signed.set(id, item);
|
|
5596
|
+
}
|
|
5597
|
+
const signature = p.thoughtSignature === undefined ? undefined : checkedSignature(p.thoughtSignature, limits(this.context.capabilities?.limits).maxArgumentBytes);
|
|
5598
|
+
const previous = item.parts.at(-1);
|
|
5599
|
+
const merge = this.streaming && signature === undefined && previous && previous.signature === undefined && !previous.textAbsent;
|
|
5600
|
+
const partBytes = Buffer.byteLength(JSON.stringify({ length: text.length + (merge ? previous.text.length : 0), hash: "x".repeat(43), ...signature === undefined ? {} : { signature }, ...p.text === undefined ? { textAbsent: true } : {} }));
|
|
5601
|
+
const metadataBytes = item.metadataBytes + partBytes - (merge ? previous.metadataBytes : 0) + (!merge && item.parts.length ? 1 : 0);
|
|
5602
|
+
const hasSignature = item.hasSignature || signature !== undefined;
|
|
5603
|
+
const replayBytes = this.replayBytes + (item.hasSignature ? metadataBytes - item.metadataBytes : hasSignature ? metadataBytes + (this.signedItems ? 1 : 0) : 0);
|
|
5604
|
+
const budget = limits(this.context.capabilities?.limits);
|
|
5605
|
+
if (hasSignature && geminiEnvelopeBytes(replayBytes) > Math.min(budget.maxArgumentBytes, budget.maxOutputBytes))
|
|
5606
|
+
fail("resource_limit", "Accumulated Gemini signatures exceed replay budget");
|
|
5607
|
+
if (hasSignature && !item.hasSignature)
|
|
5608
|
+
this.signedItems++;
|
|
5609
|
+
this.replayBytes = replayBytes;
|
|
5610
|
+
item.metadataBytes = metadataBytes;
|
|
5611
|
+
item.hasSignature = hasSignature;
|
|
5612
|
+
if (merge) {
|
|
5613
|
+
previous.text += text;
|
|
5614
|
+
previous.metadataBytes = partBytes;
|
|
5615
|
+
} else {
|
|
5616
|
+
if (++this.partCount > limits(this.context.capabilities?.limits).maxItems)
|
|
5617
|
+
fail("resource_limit", "Too many Gemini parts");
|
|
5618
|
+
item.parts.push({ text, metadataBytes: partBytes, ...signature === undefined ? {} : { signature }, ...p.text === undefined ? { textAbsent: true } : {} });
|
|
5619
|
+
}
|
|
5620
|
+
events.push(...output);
|
|
5396
5621
|
}
|
|
5397
5622
|
}
|
|
5398
5623
|
}
|
|
@@ -5409,25 +5634,47 @@ class GeminiCanonicalDecoder {
|
|
|
5409
5634
|
}
|
|
5410
5635
|
if (b.usageMetadata !== undefined) {
|
|
5411
5636
|
const u = record(b.usageMetadata, "usageMetadata");
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
const input = u.promptTokenCount ?? 0, thoughts = u.thoughtsTokenCount ?? 0, output = (u.candidatesTokenCount ?? 0) + thoughts;
|
|
5637
|
+
for (const k of ["promptTokenCount", "candidatesTokenCount", "totalTokenCount", "cachedContentTokenCount", "thoughtsTokenCount", "toolUsePromptTokenCount"])
|
|
5638
|
+
if (u[k] !== undefined && u[k] !== null)
|
|
5639
|
+
integer(u[k], `usageMetadata.${k}`);
|
|
5640
|
+
const input = (u.promptTokenCount ?? 0) + (u.toolUsePromptTokenCount ?? 0), thoughts = u.thoughtsTokenCount ?? 0, output = (u.candidatesTokenCount ?? 0) + thoughts;
|
|
5416
5641
|
if (u.totalTokenCount !== undefined && u.totalTokenCount !== input + output)
|
|
5417
5642
|
fail("invalid_usage", "Gemini total does not match reported components", "usageMetadata.totalTokenCount");
|
|
5418
5643
|
events.push(...this.encoder.push({ choices: [], usage: { prompt_tokens: input, completion_tokens: output, prompt_tokens_details: { cached_tokens: u.cachedContentTokenCount ?? 0 }, completion_tokens_details: { reasoning_tokens: thoughts } } }));
|
|
5419
5644
|
}
|
|
5420
|
-
return events;
|
|
5645
|
+
return this.decorate(events);
|
|
5646
|
+
}
|
|
5647
|
+
decorate(events) {
|
|
5648
|
+
const output = [];
|
|
5649
|
+
for (const event of events) {
|
|
5650
|
+
if (["response.completed", "response.incomplete", "response.failed"].includes(event.type)) {
|
|
5651
|
+
const signed = [...this.signed.values()].filter((item) => item.parts.some((p) => p.signature !== undefined));
|
|
5652
|
+
if (signed.length) {
|
|
5653
|
+
const budget = limits(this.context.capabilities?.limits), response = record(event.response, "response");
|
|
5654
|
+
const items = list(response.output, "output");
|
|
5655
|
+
if (items.length >= budget.maxItems)
|
|
5656
|
+
fail("resource_limit", "Too many Gemini output items");
|
|
5657
|
+
const item = geminiSignatureItem(signed.map((item) => ({ id: item.id, thought: item.thought, parts: item.parts.map((p) => ({ length: p.text.length, hash: geminiTextHash(p.text), ...p.signature === undefined ? {} : { signature: p.signature }, ...p.textAbsent ? { textAbsent: true } : {} })) })), budget);
|
|
5658
|
+
const body = { ...response, output: [...items, item] };
|
|
5659
|
+
serialized(body, budget.maxOutputBytes, "Gemini response");
|
|
5660
|
+
output.push({ type: "response.output_item.added", response_id: response.id, output_index: items.length, item }, { type: "response.output_item.done", response_id: response.id, output_index: items.length, item });
|
|
5661
|
+
output.push({ ...event, response: body });
|
|
5662
|
+
continue;
|
|
5663
|
+
}
|
|
5664
|
+
}
|
|
5665
|
+
output.push(event);
|
|
5666
|
+
}
|
|
5667
|
+
return output.map((event) => ({ ...event, sequence_number: this.sequence++ }));
|
|
5421
5668
|
}
|
|
5422
5669
|
finish() {
|
|
5423
|
-
return this.encoder.finish();
|
|
5670
|
+
return this.decorate(this.encoder.finish());
|
|
5424
5671
|
}
|
|
5425
5672
|
}
|
|
5426
5673
|
function canonicalResponse(raw, context, names) {
|
|
5427
5674
|
if (context.targetProtocol === "responses")
|
|
5428
5675
|
return validateCanonicalResponse(raw, context);
|
|
5429
5676
|
if (context.targetProtocol === "gemini_generate_content") {
|
|
5430
|
-
const decoder = new GeminiCanonicalDecoder(context, names);
|
|
5677
|
+
const decoder = new GeminiCanonicalDecoder(context, names, false);
|
|
5431
5678
|
const events = [...decoder.push(raw), ...decoder.finish()];
|
|
5432
5679
|
return record(events.at(-1)?.response, "response");
|
|
5433
5680
|
}
|
|
@@ -5809,7 +6056,7 @@ class CanonicalStreamRenderer {
|
|
|
5809
6056
|
var LLM_PROTOCOLS = ["responses", "chat_completions", "anthropic_messages", "gemini_generate_content"];
|
|
5810
6057
|
|
|
5811
6058
|
// packages/llms/src/protocol-session/index.ts
|
|
5812
|
-
var PROTOCOL_CONVERSION_RULES_VERSION = "1.0.
|
|
6059
|
+
var PROTOCOL_CONVERSION_RULES_VERSION = "1.0.4";
|
|
5813
6060
|
function describeProtocolConversion() {
|
|
5814
6061
|
return {
|
|
5815
6062
|
rulesVersion: PROTOCOL_CONVERSION_RULES_VERSION,
|
|
@@ -5817,7 +6064,7 @@ function describeProtocolConversion() {
|
|
|
5817
6064
|
matrix: LLM_PROTOCOLS.flatMap((sourceProtocol) => LLM_PROTOCOLS.map((targetProtocol) => ({
|
|
5818
6065
|
sourceProtocol,
|
|
5819
6066
|
targetProtocol,
|
|
5820
|
-
mode: sourceProtocol === targetProtocol ? "passthrough" : sourceProtocol === "
|
|
6067
|
+
mode: sourceProtocol === targetProtocol ? "passthrough" : sourceProtocol === "gemini_generate_content" && targetProtocol === "responses" ? "unsupported" : "convert"
|
|
5821
6068
|
})))
|
|
5822
6069
|
};
|
|
5823
6070
|
}
|
|
@@ -5835,9 +6082,9 @@ function createProtocolSession(context) {
|
|
|
5835
6082
|
fail("unsupported_protocol", "Unknown protocol");
|
|
5836
6083
|
if (typeof context.model !== "string" || !context.model)
|
|
5837
6084
|
fail("invalid_payload", "Target model is required", "model");
|
|
5838
|
-
const unsupported = context.sourceProtocol === "
|
|
6085
|
+
const unsupported = context.sourceProtocol === "gemini_generate_content" && context.targetProtocol === "responses";
|
|
5839
6086
|
if (unsupported)
|
|
5840
|
-
fail("unsupported_protocol_pair", "
|
|
6087
|
+
fail("unsupported_protocol_pair", "Gemini to Responses conversion is not supported");
|
|
5841
6088
|
const same = context.sourceProtocol === context.targetProtocol;
|
|
5842
6089
|
let disposed = false, poisoned = false, requested = false, mode, finished = false, passTerminal = false, passAnthropicStopped = false;
|
|
5843
6090
|
let names = new Map;
|
|
@@ -6076,7 +6323,7 @@ function protocolRequestModel(protocol, body, url) {
|
|
|
6076
6323
|
return model;
|
|
6077
6324
|
}
|
|
6078
6325
|
// plugins/llm-protocol-adapter/server/relay.ts
|
|
6079
|
-
import { createHash } from "crypto";
|
|
6326
|
+
import { createHash as createHash2 } from "crypto";
|
|
6080
6327
|
|
|
6081
6328
|
// plugins/llm-protocol-adapter/server/history.ts
|
|
6082
6329
|
function serializeHistory2(value) {
|
|
@@ -6323,7 +6570,7 @@ async function relayPayload(result, param, signal) {
|
|
|
6323
6570
|
}
|
|
6324
6571
|
|
|
6325
6572
|
// plugins/llm-protocol-adapter/server/relay.ts
|
|
6326
|
-
var hash = (value) =>
|
|
6573
|
+
var hash = (value) => createHash2("sha256").update(JSON.stringify(value)).digest("hex");
|
|
6327
6574
|
function rejection(param, code, status = 502) {
|
|
6328
6575
|
return new DataAdmissionError(status, code, undefined, { param, message: "Agent payload relay is unavailable" });
|
|
6329
6576
|
}
|
|
@@ -6529,9 +6776,10 @@ class AgentRelay {
|
|
|
6529
6776
|
}
|
|
6530
6777
|
|
|
6531
6778
|
// plugins/llm-protocol-adapter/server/service.ts
|
|
6532
|
-
var REASONING_RULES_VERSION = "2026-10-
|
|
6779
|
+
var REASONING_RULES_VERSION = "2026-10-27.1";
|
|
6533
6780
|
var concrete = new Set(["none", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
6534
|
-
var effortProtocols = new Set(["responses", "chat_completions", "anthropic_messages"]);
|
|
6781
|
+
var effortProtocols = new Set(["responses", "chat_completions", "anthropic_messages", "gemini_generate_content"]);
|
|
6782
|
+
var geminiEfforts = new Set(["minimal", "low", "medium", "high"]);
|
|
6535
6783
|
var anthropicEfforts = new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
6536
6784
|
function identityError(code, message) {
|
|
6537
6785
|
throw new ResponsesCodecError(code, message, "modelIdentity");
|
|
@@ -6556,7 +6804,7 @@ function resolveCapabilities(catalog, context) {
|
|
|
6556
6804
|
if (!info)
|
|
6557
6805
|
return null;
|
|
6558
6806
|
const restrictions = context.restrictions, reasoningAllowed = info.reasoning && restrictions?.reasoning !== false;
|
|
6559
|
-
const allowed = context.targetProtocol === "anthropic_messages" ? anthropicEfforts : concrete;
|
|
6807
|
+
const allowed = context.targetProtocol === "anthropic_messages" ? anthropicEfforts : context.targetProtocol === "gemini_generate_content" ? geminiEfforts : concrete;
|
|
6560
6808
|
const candidates = reasoningAllowed && effortProtocols.has(context.targetProtocol) && info.reasoningOptionsStatus === "known" ? info.reasoningOptions?.flatMap((option) => option.type === "effort" ? option.values.filter((value) => typeof value === "string" && allowed.has(value)) : []) ?? [] : [];
|
|
6561
6809
|
const supportedEfforts = [...new Set(candidates)];
|
|
6562
6810
|
const wirePolicy = supportedEfforts.length ? { effortMap: Object.fromEntries(supportedEfforts.map((value) => [value, value])) } : null;
|
|
@@ -6974,6 +7222,10 @@ var manifest_default = {
|
|
|
6974
7222
|
"responses",
|
|
6975
7223
|
"anthropic_messages"
|
|
6976
7224
|
],
|
|
7225
|
+
[
|
|
7226
|
+
"responses",
|
|
7227
|
+
"gemini_generate_content"
|
|
7228
|
+
],
|
|
6977
7229
|
[
|
|
6978
7230
|
"chat_completions",
|
|
6979
7231
|
"responses"
|
|
@@ -7062,7 +7314,10 @@ function protocolForPath(path) {
|
|
|
7062
7314
|
}
|
|
7063
7315
|
function targetPath(path, protocol, model, streaming) {
|
|
7064
7316
|
const suffix = protocol === "responses" ? "/responses" : protocol === "chat_completions" ? "/chat/completions" : protocol === "anthropic_messages" ? "/messages" : `/models/${encodeURIComponent(model)}:${streaming ? "streamGenerateContent" : "generateContent"}`;
|
|
7065
|
-
|
|
7317
|
+
const prefix = path.replace(/\/(?:responses|chat\/completions|messages|models\/[^/]+:(?:streamGenerateContent|generateContent))$/, "");
|
|
7318
|
+
if (prefix === path)
|
|
7319
|
+
return path;
|
|
7320
|
+
return (protocol === "gemini_generate_content" ? prefix.replace(/\/v1(?:beta|alpha)?$/, "") + "/v1beta" : prefix) + suffix;
|
|
7066
7321
|
}
|
|
7067
7322
|
var server_default = definePlugin(class {
|
|
7068
7323
|
options;
|
|
@@ -7121,6 +7376,7 @@ var server_default = definePlugin(class {
|
|
|
7121
7376
|
targetProtocol: this.options.targetProtocol,
|
|
7122
7377
|
model,
|
|
7123
7378
|
modelIdentity: readModelIdentity(ctx.annotations),
|
|
7379
|
+
...source === "responses" && this.options.targetProtocol === "gemini_generate_content" ? { agentMessageDelivery: "plaintext" } : {},
|
|
7124
7380
|
...source === "gemini_generate_content" ? { streaming: ctx.url.pathname.endsWith(":streamGenerateContent") } : {}
|
|
7125
7381
|
});
|
|
7126
7382
|
const result = session.convertRequest(ctx.body);
|
|
@@ -7169,4 +7425,4 @@ export {
|
|
|
7169
7425
|
targetPath
|
|
7170
7426
|
};
|
|
7171
7427
|
|
|
7172
|
-
//# debugId=
|
|
7428
|
+
//# debugId=25B3D2AD452DB0F464756E2164756E21
|