@jeffusion/bungee-llms 5.20.0 → 5.21.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/index.js
CHANGED
|
@@ -8509,14 +8509,25 @@ var DEFAULT_RESPONSES_CODEC_LIMITS = Object.freeze({
|
|
|
8509
8509
|
|
|
8510
8510
|
class ResponsesCodecError extends Error {
|
|
8511
8511
|
code;
|
|
8512
|
-
|
|
8512
|
+
param;
|
|
8513
|
+
constructor(code, message, param) {
|
|
8513
8514
|
super(message);
|
|
8514
8515
|
this.code = code;
|
|
8516
|
+
this.param = param;
|
|
8515
8517
|
this.name = "ResponsesCodecError";
|
|
8516
8518
|
}
|
|
8517
8519
|
}
|
|
8518
|
-
function fail(code, message) {
|
|
8519
|
-
throw new ResponsesCodecError(code, message);
|
|
8520
|
+
function fail(code, message, param) {
|
|
8521
|
+
throw new ResponsesCodecError(code, message, param);
|
|
8522
|
+
}
|
|
8523
|
+
function atParam(param, run) {
|
|
8524
|
+
try {
|
|
8525
|
+
return run();
|
|
8526
|
+
} catch (error) {
|
|
8527
|
+
if (error instanceof ResponsesCodecError && error.param === undefined)
|
|
8528
|
+
throw new ResponsesCodecError(error.code, error.message, param);
|
|
8529
|
+
throw error;
|
|
8530
|
+
}
|
|
8520
8531
|
}
|
|
8521
8532
|
function record(value, where) {
|
|
8522
8533
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -8679,8 +8690,94 @@ function responsePayload(id, model, output, end, usage, error, createdAt = Math.
|
|
|
8679
8690
|
return result;
|
|
8680
8691
|
}
|
|
8681
8692
|
|
|
8693
|
+
// src/responses-codec/request-preferences.ts
|
|
8694
|
+
function fields(value, allowed, path) {
|
|
8695
|
+
for (const key of Object.keys(value))
|
|
8696
|
+
if (!allowed.includes(key)) {
|
|
8697
|
+
fail("unsupported_request", "Unrecognized request option", `${path}.${key}`);
|
|
8698
|
+
}
|
|
8699
|
+
}
|
|
8700
|
+
function applyResponsesPreferences(body, result, protocol, capabilities, maxTokens, diagnostics) {
|
|
8701
|
+
const note = (param, action, reason) => diagnostics.push({ param, action, reason });
|
|
8702
|
+
if (body.stream_options !== undefined)
|
|
8703
|
+
atParam("stream_options", () => {
|
|
8704
|
+
const options = record(body.stream_options, "stream_options");
|
|
8705
|
+
fields(options, ["include_usage", "reasoning_summary_delivery"], "stream_options");
|
|
8706
|
+
if (options.include_usage !== undefined) {
|
|
8707
|
+
if (typeof options.include_usage !== "boolean")
|
|
8708
|
+
fail("invalid_payload", "Usage preference must be boolean", "stream_options.include_usage");
|
|
8709
|
+
note("stream_options.include_usage", protocol === "chat_completions" && body.stream === true ? "mapped" : "omitted", protocol === "chat_completions" && body.stream === true ? "stream_usage_required" : "target_usage_events");
|
|
8710
|
+
}
|
|
8711
|
+
if (options.reasoning_summary_delivery !== undefined) {
|
|
8712
|
+
if (options.reasoning_summary_delivery !== "sequential_cutoff")
|
|
8713
|
+
fail("unsupported_request", "Unsupported summary delivery preference", "stream_options.reasoning_summary_delivery");
|
|
8714
|
+
note("stream_options.reasoning_summary_delivery", "omitted", "source_summary_delivery_not_applicable");
|
|
8715
|
+
}
|
|
8716
|
+
});
|
|
8717
|
+
if (body.access_programs !== undefined)
|
|
8718
|
+
atParam("access_programs", () => {
|
|
8719
|
+
const programs = record(body.access_programs, "access_programs");
|
|
8720
|
+
fields(programs, ["cyber"], "access_programs");
|
|
8721
|
+
if (programs.cyber !== undefined && programs.cyber !== "standard") {
|
|
8722
|
+
if (programs.cyber === "daybreak_blue" || programs.cyber === "daybreak_red") {
|
|
8723
|
+
fail("unsupported_access_program", "Selected access program cannot be represented by the target protocol", "access_programs.cyber");
|
|
8724
|
+
}
|
|
8725
|
+
fail("invalid_payload", "Invalid access program", "access_programs.cyber");
|
|
8726
|
+
}
|
|
8727
|
+
note("access_programs", "omitted", "target_uses_own_access_policy");
|
|
8728
|
+
});
|
|
8729
|
+
if (body.reasoning !== undefined)
|
|
8730
|
+
atParam("reasoning", () => {
|
|
8731
|
+
const reasoning = record(body.reasoning, "reasoning");
|
|
8732
|
+
fields(reasoning, ["effort", "summary", "context"], "reasoning");
|
|
8733
|
+
if (reasoning.summary !== undefined) {
|
|
8734
|
+
if (typeof reasoning.summary !== "string" || !["none", "auto", "concise", "detailed"].includes(reasoning.summary))
|
|
8735
|
+
fail("invalid_payload", "Invalid reasoning summary preference", "reasoning.summary");
|
|
8736
|
+
note("reasoning.summary", "omitted", "target_summary_not_guaranteed");
|
|
8737
|
+
}
|
|
8738
|
+
if (reasoning.context !== undefined) {
|
|
8739
|
+
if (reasoning.context !== "all_turns")
|
|
8740
|
+
fail("unsupported_reasoning", "Unsupported reasoning context preference", "reasoning.context");
|
|
8741
|
+
note("reasoning.context", "omitted", "source_reasoning_context_not_applicable");
|
|
8742
|
+
}
|
|
8743
|
+
if (reasoning.effort !== undefined)
|
|
8744
|
+
atParam("reasoning.effort", () => {
|
|
8745
|
+
const effort = string(reasoning.effort, "reasoning effort", true);
|
|
8746
|
+
if (protocol === "chat_completions" && capabilities.reasoningEffort) {
|
|
8747
|
+
result.reasoning_effort = effort;
|
|
8748
|
+
note("reasoning.effort", "mapped", "target_reasoning_effort");
|
|
8749
|
+
} else if (protocol === "anthropic_messages" && capabilities.anthropicThinkingBudget !== undefined && effort !== "none") {
|
|
8750
|
+
const budget = capabilities.anthropicThinkingBudget;
|
|
8751
|
+
if (!Number.isSafeInteger(budget) || budget < 1024 || typeof maxTokens !== "number" || budget >= maxTokens) {
|
|
8752
|
+
fail("unsupported_reasoning", "Anthropic thinking requires an explicit budget below max_output_tokens");
|
|
8753
|
+
}
|
|
8754
|
+
result.thinking = { type: "enabled", budget_tokens: budget };
|
|
8755
|
+
note("reasoning.effort", "mapped", "explicit_target_thinking_budget");
|
|
8756
|
+
} else
|
|
8757
|
+
note("reasoning.effort", "omitted", "target_reasoning_effort_unavailable");
|
|
8758
|
+
});
|
|
8759
|
+
});
|
|
8760
|
+
if (body.text !== undefined)
|
|
8761
|
+
atParam("text", () => {
|
|
8762
|
+
const text = record(body.text, "text");
|
|
8763
|
+
fields(text, ["verbosity", "format"], "text");
|
|
8764
|
+
if (text.verbosity !== undefined) {
|
|
8765
|
+
if (typeof text.verbosity !== "string" || !["low", "medium", "high"].includes(text.verbosity))
|
|
8766
|
+
fail("invalid_payload", "Invalid text verbosity preference", "text.verbosity");
|
|
8767
|
+
note("text.verbosity", "omitted", "target_verbosity_not_available");
|
|
8768
|
+
}
|
|
8769
|
+
});
|
|
8770
|
+
}
|
|
8771
|
+
|
|
8682
8772
|
// src/responses-codec/request.ts
|
|
8683
8773
|
var contentNormalizer = new OpenAIProtocolConversion({ trimWhitespace: false });
|
|
8774
|
+
function definitionKey(value) {
|
|
8775
|
+
if (Array.isArray(value))
|
|
8776
|
+
return `[${value.map(definitionKey).join(",")}]`;
|
|
8777
|
+
if (value && typeof value === "object")
|
|
8778
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${definitionKey(value[key])}`).join(",")}}`;
|
|
8779
|
+
return JSON.stringify(value);
|
|
8780
|
+
}
|
|
8684
8781
|
function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
8685
8782
|
const body = record(raw, "Responses request");
|
|
8686
8783
|
const budget = limits(capabilities.limits);
|
|
@@ -8706,11 +8803,15 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8706
8803
|
"n",
|
|
8707
8804
|
"truncation",
|
|
8708
8805
|
"include",
|
|
8709
|
-
"store"
|
|
8806
|
+
"store",
|
|
8807
|
+
"prompt_cache_key",
|
|
8808
|
+
"client_metadata",
|
|
8809
|
+
"stream_options",
|
|
8810
|
+
"access_programs"
|
|
8710
8811
|
]);
|
|
8711
8812
|
for (const key of Object.keys(body))
|
|
8712
8813
|
if (!supported.has(key))
|
|
8713
|
-
fail("unsupported_request",
|
|
8814
|
+
fail("unsupported_request", "Unrecognized Responses request field", key);
|
|
8714
8815
|
if (body.store === true)
|
|
8715
8816
|
fail("unsupported_request", "Stored Responses require caller-owned persistence");
|
|
8716
8817
|
for (const key of ["previous_response_id", "conversation", "response_id"]) {
|
|
@@ -8723,17 +8824,28 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8723
8824
|
fail("unsupported_request", "Only one response candidate is supported");
|
|
8724
8825
|
if (body.truncation !== undefined && body.truncation !== "disabled")
|
|
8725
8826
|
fail("unsupported_request", "Automatic input truncation cannot be represented");
|
|
8726
|
-
if (body.include !== undefined && list(body.include, "include").
|
|
8827
|
+
if (body.include !== undefined && list(body.include, "include").some((field) => field !== "reasoning.encrypted_content"))
|
|
8727
8828
|
fail("unsupported_request", "Requested extra Responses output fields cannot be represented");
|
|
8829
|
+
if (body.prompt_cache_key !== undefined)
|
|
8830
|
+
string(body.prompt_cache_key, "prompt_cache_key");
|
|
8831
|
+
if (body.client_metadata !== undefined)
|
|
8832
|
+
record(body.client_metadata, "client_metadata");
|
|
8728
8833
|
const model = string(body.model, "model", true);
|
|
8834
|
+
const diagnostics = [];
|
|
8835
|
+
const input = atParam("input", () => typeof body.input === "string" ? [{ role: "user", content: body.input }] : list(body.input, "input"));
|
|
8836
|
+
if (input.length > budget.maxItems)
|
|
8837
|
+
fail("resource_limit", "Too many input items", "input");
|
|
8838
|
+
const canonicalInput = [];
|
|
8729
8839
|
const toolNames = new Map;
|
|
8730
8840
|
const byOriginal = new Map;
|
|
8841
|
+
const definitions = new Map;
|
|
8842
|
+
let examinedTools = 0;
|
|
8731
8843
|
const tools = [];
|
|
8732
8844
|
const identity = (name, namespace) => JSON.stringify([namespace ?? null, name]);
|
|
8733
8845
|
const register = (name, namespace, custom) => {
|
|
8734
8846
|
const key = identity(name, namespace);
|
|
8735
8847
|
if (byOriginal.has(key))
|
|
8736
|
-
fail("tool_name_collision",
|
|
8848
|
+
fail("tool_name_collision", "Duplicate tool identity");
|
|
8737
8849
|
if (toolNames.size >= budget.maxItems)
|
|
8738
8850
|
fail("resource_limit", "Too many tools");
|
|
8739
8851
|
const wire = `bungee_tool_${toolNames.size}`;
|
|
@@ -8741,18 +8853,29 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8741
8853
|
byOriginal.set(key, wire);
|
|
8742
8854
|
return wire;
|
|
8743
8855
|
};
|
|
8744
|
-
const expandTool = (rawTool, namespace) => {
|
|
8856
|
+
const expandTool = (rawTool, namespace, path = "tools") => {
|
|
8857
|
+
if (++examinedTools > budget.maxItems)
|
|
8858
|
+
fail("resource_limit", "Too many tool declarations");
|
|
8745
8859
|
const tool = record(rawTool, "tool");
|
|
8860
|
+
if (namespace === undefined && capabilities.omitOptionalWebSearch && ["web_search", "web_search_preview"].includes(String(tool.type)) && (body.tool_choice === undefined || body.tool_choice === "auto" || body.tool_choice === "none"))
|
|
8861
|
+
return;
|
|
8862
|
+
const allowed = tool.type === "namespace" ? ["type", "name", "description", "tools"] : tool.type === "custom" ? ["type", "name", "description", "format"] : ["type", "name", "description", "parameters", "strict"];
|
|
8863
|
+
for (const key of Object.keys(tool))
|
|
8864
|
+
if (!allowed.includes(key))
|
|
8865
|
+
fail("unsupported_tool", "Unsupported tool definition field", `${path}.${key}`);
|
|
8746
8866
|
if (tool.type === "namespace") {
|
|
8747
8867
|
if (namespace !== undefined)
|
|
8748
8868
|
fail("unsupported_tool", "Nested tool namespaces are not supported");
|
|
8749
8869
|
const name = string(tool.name, "namespace name", true);
|
|
8750
|
-
for (const child of list(tool.tools, "namespace tools"))
|
|
8751
|
-
|
|
8870
|
+
for (const [index, child] of list(tool.tools, "namespace tools").entries()) {
|
|
8871
|
+
const childPath = `${path}.tools[${index}]`;
|
|
8872
|
+
atParam(childPath, () => expandTool(child, name, childPath));
|
|
8873
|
+
}
|
|
8752
8874
|
return;
|
|
8753
8875
|
}
|
|
8754
8876
|
if (tool.type !== "function" && tool.type !== "custom")
|
|
8755
|
-
fail("unsupported_tool",
|
|
8877
|
+
fail("unsupported_tool", "This tool type cannot be executed by Codex through the target protocol");
|
|
8878
|
+
serialized(tool, budget.maxArgumentBytes, "tool definition");
|
|
8756
8879
|
const name = string(tool.name, "tool name", true);
|
|
8757
8880
|
const custom = tool.type === "custom";
|
|
8758
8881
|
if (tool.strict !== undefined && typeof tool.strict !== "boolean")
|
|
@@ -8764,7 +8887,15 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8764
8887
|
if (format.type !== "text" && format.type !== "grammar")
|
|
8765
8888
|
fail("unsupported_tool", "Unknown custom tool format");
|
|
8766
8889
|
}
|
|
8890
|
+
const key = identity(name, namespace);
|
|
8891
|
+
const signature = definitionKey(tool);
|
|
8892
|
+
if (definitions.has(key)) {
|
|
8893
|
+
if (definitions.get(key) !== signature)
|
|
8894
|
+
fail("tool_name_collision", "Conflicting definitions for the same tool identity");
|
|
8895
|
+
return;
|
|
8896
|
+
}
|
|
8767
8897
|
const wire = register(name, namespace, custom);
|
|
8898
|
+
definitions.set(key, signature);
|
|
8768
8899
|
const parameters = custom ? { type: "object", properties: { input: { type: "string" } }, required: ["input"], additionalProperties: false } : record(tool.parameters ?? { type: "object", properties: {} }, "tool parameters");
|
|
8769
8900
|
const description = tool.description === undefined ? undefined : string(tool.description, "tool description");
|
|
8770
8901
|
const customHint = custom ? `Pass the complete original custom tool input verbatim in the input string.${tool.format ? ` Original input format: ${JSON.stringify(tool.format)}` : ""}` : undefined;
|
|
@@ -8777,8 +8908,24 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8777
8908
|
}
|
|
8778
8909
|
};
|
|
8779
8910
|
if (body.tools !== undefined)
|
|
8780
|
-
|
|
8781
|
-
|
|
8911
|
+
atParam("tools", () => {
|
|
8912
|
+
for (const [index, tool] of list(body.tools, "tools").entries())
|
|
8913
|
+
atParam(`tools[${index}]`, () => expandTool(tool, undefined, `tools[${index}]`));
|
|
8914
|
+
});
|
|
8915
|
+
for (const [index, rawItem] of input.entries())
|
|
8916
|
+
atParam(`input[${index}]`, () => {
|
|
8917
|
+
const item = record(rawItem, "input item");
|
|
8918
|
+
if (item.type !== "additional_tools") {
|
|
8919
|
+
canonicalInput.push(item);
|
|
8920
|
+
return;
|
|
8921
|
+
}
|
|
8922
|
+
if (item.content !== undefined)
|
|
8923
|
+
fail("invalid_payload", "A tool declaration carrier cannot also contain message content", `input[${index}].content`);
|
|
8924
|
+
for (const [toolIndex, tool] of list(item.tools, "additional tools").entries()) {
|
|
8925
|
+
const toolPath = `input[${index}].tools[${toolIndex}]`;
|
|
8926
|
+
atParam(toolPath, () => expandTool(tool, undefined, toolPath));
|
|
8927
|
+
}
|
|
8928
|
+
});
|
|
8782
8929
|
const resolve = (item, custom) => {
|
|
8783
8930
|
const name = string(item.name, "call name", true);
|
|
8784
8931
|
const namespace = item.namespace === undefined ? undefined : string(item.namespace, "call namespace", true);
|
|
@@ -8786,7 +8933,7 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8786
8933
|
if (wire === undefined)
|
|
8787
8934
|
wire = register(name, namespace, custom);
|
|
8788
8935
|
if (toolNames.get(wire).custom !== custom)
|
|
8789
|
-
fail("tool_name_collision",
|
|
8936
|
+
fail("tool_name_collision", "Historical tool kind conflicts with its current declaration");
|
|
8790
8937
|
return wire;
|
|
8791
8938
|
};
|
|
8792
8939
|
const normalizeContent = (rawContent, role) => {
|
|
@@ -8797,6 +8944,8 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8797
8944
|
const part = record(rawPart, "content part");
|
|
8798
8945
|
if (part.type === "input_text" || part.type === "output_text" || part.type === "text")
|
|
8799
8946
|
string(part.text, "content text");
|
|
8947
|
+
else if (part.type === "refusal" && role === "assistant")
|
|
8948
|
+
string(part.refusal, "refusal");
|
|
8800
8949
|
else if (part.type === "input_image" && role === "user") {
|
|
8801
8950
|
if (protocol !== "chat_completions")
|
|
8802
8951
|
fail("unsupported_content", "Anthropic image conversion requires provider-specific URL/data support");
|
|
@@ -8804,11 +8953,14 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8804
8953
|
if (part.detail !== undefined && !["auto", "low", "high"].includes(String(part.detail)))
|
|
8805
8954
|
fail("unsupported_content", "Unknown image detail");
|
|
8806
8955
|
} else
|
|
8807
|
-
fail("unsupported_content",
|
|
8956
|
+
fail("unsupported_content", "Unsupported message content type");
|
|
8808
8957
|
if (part.annotations !== undefined && list(part.annotations, "annotations").length)
|
|
8809
8958
|
fail("unsupported_content", "Output annotations cannot be represented in input history");
|
|
8810
8959
|
}
|
|
8811
|
-
const normalized = contentNormalizer.normalizeResponsesMessageContent(parts)
|
|
8960
|
+
const normalized = contentNormalizer.normalizeResponsesMessageContent(parts.map((raw) => {
|
|
8961
|
+
const part = raw;
|
|
8962
|
+
return part.type === "refusal" ? { type: "output_text", text: part.refusal } : part;
|
|
8963
|
+
}));
|
|
8812
8964
|
for (let i = 0;i < parts.length; i++) {
|
|
8813
8965
|
const part = parts[i];
|
|
8814
8966
|
if (part.type === "input_image" && part.detail !== undefined)
|
|
@@ -8817,6 +8969,8 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8817
8969
|
return normalized;
|
|
8818
8970
|
};
|
|
8819
8971
|
const chat = [];
|
|
8972
|
+
const messageParams = new WeakMap;
|
|
8973
|
+
let messageParam = "instructions";
|
|
8820
8974
|
const callIds = new Set;
|
|
8821
8975
|
const answered = new Set;
|
|
8822
8976
|
let pendingReasoning = "";
|
|
@@ -8824,73 +8978,77 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8824
8978
|
if (chat.length >= budget.maxItems)
|
|
8825
8979
|
fail("resource_limit", "Too many input messages");
|
|
8826
8980
|
chat.push(message);
|
|
8981
|
+
messageParams.set(message, messageParam);
|
|
8827
8982
|
};
|
|
8828
8983
|
if (body.instructions !== undefined)
|
|
8829
8984
|
addMessage({ role: "system", content: string(body.instructions, "instructions") });
|
|
8830
|
-
const
|
|
8831
|
-
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
|
|
8835
|
-
|
|
8836
|
-
|
|
8837
|
-
|
|
8838
|
-
|
|
8839
|
-
|
|
8840
|
-
if (
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
if (
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
|
|
8878
|
-
|
|
8879
|
-
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8985
|
+
for (const [inputIndex, rawItem] of input.entries())
|
|
8986
|
+
atParam(`input[${inputIndex}]`, () => {
|
|
8987
|
+
messageParam = `input[${inputIndex}]`;
|
|
8988
|
+
const item = record(rawItem, "input item");
|
|
8989
|
+
if (item.type === "additional_tools")
|
|
8990
|
+
return;
|
|
8991
|
+
if (item.encrypted_content != null || item.type === "compaction")
|
|
8992
|
+
fail("unsupported_content", "Encrypted reasoning and compaction cannot be restored");
|
|
8993
|
+
if (item.type === "item_reference")
|
|
8994
|
+
fail("unresolved_reference", "Input item references must be resolved before conversion");
|
|
8995
|
+
if (item.type === "reasoning") {
|
|
8996
|
+
if (protocol !== "chat_completions" || !(capabilities.reasoningHistory || capabilities.reasoningEffort))
|
|
8997
|
+
fail("unsupported_reasoning", "Reasoning history requires an explicit compatible reasoning capability");
|
|
8998
|
+
if (item.content != null)
|
|
8999
|
+
fail("unsupported_reasoning", "Reasoning content requires an explicit history mapping", `${messageParam}.content`);
|
|
9000
|
+
const summary = list(item.summary, "reasoning summary");
|
|
9001
|
+
pendingReasoning += summary.map((part) => {
|
|
9002
|
+
const p = record(part, "reasoning summary");
|
|
9003
|
+
if (p.type !== "summary_text")
|
|
9004
|
+
fail("unsupported_reasoning", "Unknown reasoning summary");
|
|
9005
|
+
return string(p.text, "summary text");
|
|
9006
|
+
}).join("");
|
|
9007
|
+
return;
|
|
9008
|
+
}
|
|
9009
|
+
if (item.type === "function_call" || item.type === "custom_tool_call") {
|
|
9010
|
+
const custom = item.type === "custom_tool_call";
|
|
9011
|
+
const id = string(item.call_id, "call_id", true);
|
|
9012
|
+
if (callIds.has(id))
|
|
9013
|
+
fail("invalid_tool_history", "Duplicate tool call identifier");
|
|
9014
|
+
callIds.add(id);
|
|
9015
|
+
const args = custom ? JSON.stringify({ input: string(item.input, "custom input") }) : string(item.arguments, "arguments");
|
|
9016
|
+
bounded(args, budget.maxArgumentBytes, "arguments");
|
|
9017
|
+
argumentObject(args);
|
|
9018
|
+
const call = { id, type: "function", function: { name: resolve(item, custom), arguments: args } };
|
|
9019
|
+
const previous = chat.at(-1);
|
|
9020
|
+
if (previous?.role === "assistant") {
|
|
9021
|
+
previous.tool_calls = [...previous.tool_calls ?? [], call];
|
|
9022
|
+
if (pendingReasoning)
|
|
9023
|
+
previous.reasoning_content = pendingReasoning;
|
|
9024
|
+
} else
|
|
9025
|
+
addMessage({ role: "assistant", content: null, tool_calls: [call], ...pendingReasoning ? { reasoning_content: pendingReasoning } : {} });
|
|
9026
|
+
pendingReasoning = "";
|
|
9027
|
+
return;
|
|
9028
|
+
}
|
|
9029
|
+
if (item.type === "function_call_output" || item.type === "custom_tool_call_output") {
|
|
9030
|
+
const id = string(item.call_id, "call_id", true);
|
|
9031
|
+
if (!callIds.has(id) || answered.has(id))
|
|
9032
|
+
fail("invalid_tool_history", "Unmatched or duplicate tool result");
|
|
9033
|
+
answered.add(id);
|
|
9034
|
+
const output = typeof item.output === "string" ? item.output : normalizeContent(item.output, "tool");
|
|
9035
|
+
addMessage({ role: "tool", tool_call_id: id, content: output, ...item.is_error === true ? { is_error: true } : {} });
|
|
9036
|
+
return;
|
|
9037
|
+
}
|
|
9038
|
+
if (item.type !== undefined && item.type !== "message")
|
|
9039
|
+
fail("unsupported_content", "Unsupported input item type");
|
|
9040
|
+
const role = string(item.role, "message role", true);
|
|
9041
|
+
if (!["user", "assistant", "system", "developer"].includes(role))
|
|
9042
|
+
fail("unsupported_content", "Unknown message role");
|
|
9043
|
+
const message = { role, content: normalizeContent(item.content, role) };
|
|
9044
|
+
if (pendingReasoning) {
|
|
9045
|
+
if (role !== "assistant")
|
|
9046
|
+
fail("unsupported_reasoning", "Reasoning must precede an assistant item");
|
|
9047
|
+
message.reasoning_content = pendingReasoning;
|
|
9048
|
+
pendingReasoning = "";
|
|
9049
|
+
}
|
|
9050
|
+
addMessage(message);
|
|
9051
|
+
});
|
|
8894
9052
|
if (pendingReasoning)
|
|
8895
9053
|
fail("unsupported_reasoning", "Orphan reasoning history cannot be represented");
|
|
8896
9054
|
const result = { model, messages: chat };
|
|
@@ -8923,41 +9081,35 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8923
9081
|
result.tool_choice = protocol === "chat_completions" ? { type: "function", function: { name: wire } } : { type: "tool", name: wire };
|
|
8924
9082
|
}
|
|
8925
9083
|
}
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
if (protocol === "chat_completions")
|
|
8956
|
-
result.response_format = format;
|
|
8957
|
-
} else
|
|
8958
|
-
fail("unsupported_request", "Unknown text format");
|
|
8959
|
-
}
|
|
8960
|
-
}
|
|
9084
|
+
applyResponsesPreferences(body, result, protocol, capabilities, maxTokens, diagnostics);
|
|
9085
|
+
if (body.text !== undefined)
|
|
9086
|
+
atParam("text.format", () => {
|
|
9087
|
+
const text = record(body.text, "text");
|
|
9088
|
+
if (text.format !== undefined) {
|
|
9089
|
+
const format = record(text.format, "text format");
|
|
9090
|
+
const allowed = format.type === "json_schema" ? ["type", "name", "schema", "strict", "description"] : ["type"];
|
|
9091
|
+
for (const key of Object.keys(format))
|
|
9092
|
+
if (!allowed.includes(key))
|
|
9093
|
+
fail("unsupported_request", "Unsupported output format field", `text.format.${key}`);
|
|
9094
|
+
if (format.type === "json_schema") {
|
|
9095
|
+
atParam("text.format.name", () => string(format.name, "output schema name", true));
|
|
9096
|
+
atParam("text.format.schema", () => record(format.schema, "output schema"));
|
|
9097
|
+
if (format.strict !== undefined && typeof format.strict !== "boolean")
|
|
9098
|
+
fail("invalid_payload", "Output schema strict must be boolean", "text.format.strict");
|
|
9099
|
+
if (format.description !== undefined)
|
|
9100
|
+
atParam("text.format.description", () => string(format.description, "output schema description"));
|
|
9101
|
+
}
|
|
9102
|
+
if (protocol !== "chat_completions" && format.type !== "text")
|
|
9103
|
+
fail("unsupported_request", "Anthropic structured output requires a separate provider capability");
|
|
9104
|
+
if (format.type === "json_schema")
|
|
9105
|
+
result.response_format = { type: "json_schema", json_schema: { name: format.name, schema: format.schema, ...format.strict !== undefined ? { strict: format.strict } : {}, ...format.description !== undefined ? { description: format.description } : {} } };
|
|
9106
|
+
else if (format.type === "json_object" || format.type === "text") {
|
|
9107
|
+
if (protocol === "chat_completions")
|
|
9108
|
+
result.response_format = format;
|
|
9109
|
+
} else
|
|
9110
|
+
fail("unsupported_request", "Unknown text format");
|
|
9111
|
+
}
|
|
9112
|
+
});
|
|
8961
9113
|
if (protocol === "anthropic_messages") {
|
|
8962
9114
|
const system = [];
|
|
8963
9115
|
const messages = [];
|
|
@@ -8971,7 +9123,7 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8971
9123
|
for (const msg of chat) {
|
|
8972
9124
|
if (msg.role === "system" || msg.role === "developer") {
|
|
8973
9125
|
if (messages.length)
|
|
8974
|
-
fail("unsupported_content", "Mid-conversation system instructions cannot be relocated safely");
|
|
9126
|
+
fail("unsupported_content", "Mid-conversation system instructions cannot be relocated safely", `${messageParams.get(msg)}.role`);
|
|
8975
9127
|
system.push(typeof msg.content === "string" ? msg.content : msg.content.map((p) => string(p.text, "system text")).join(""));
|
|
8976
9128
|
continue;
|
|
8977
9129
|
}
|
|
@@ -8997,7 +9149,7 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8997
9149
|
delete result.parallel_tool_calls;
|
|
8998
9150
|
}
|
|
8999
9151
|
}
|
|
9000
|
-
return { body: result, toolNames };
|
|
9152
|
+
return { body: result, toolNames, canonicalInput, diagnostics };
|
|
9001
9153
|
}
|
|
9002
9154
|
// src/responses-codec/events.ts
|
|
9003
9155
|
class ResponsesEventEncoder {
|
package/dist/plugin-api.js
CHANGED
|
@@ -8515,14 +8515,25 @@ var DEFAULT_RESPONSES_CODEC_LIMITS = Object.freeze({
|
|
|
8515
8515
|
|
|
8516
8516
|
class ResponsesCodecError extends Error {
|
|
8517
8517
|
code;
|
|
8518
|
-
|
|
8518
|
+
param;
|
|
8519
|
+
constructor(code, message, param) {
|
|
8519
8520
|
super(message);
|
|
8520
8521
|
this.code = code;
|
|
8522
|
+
this.param = param;
|
|
8521
8523
|
this.name = "ResponsesCodecError";
|
|
8522
8524
|
}
|
|
8523
8525
|
}
|
|
8524
|
-
function fail(code, message) {
|
|
8525
|
-
throw new ResponsesCodecError(code, message);
|
|
8526
|
+
function fail(code, message, param) {
|
|
8527
|
+
throw new ResponsesCodecError(code, message, param);
|
|
8528
|
+
}
|
|
8529
|
+
function atParam(param, run) {
|
|
8530
|
+
try {
|
|
8531
|
+
return run();
|
|
8532
|
+
} catch (error) {
|
|
8533
|
+
if (error instanceof ResponsesCodecError && error.param === undefined)
|
|
8534
|
+
throw new ResponsesCodecError(error.code, error.message, param);
|
|
8535
|
+
throw error;
|
|
8536
|
+
}
|
|
8526
8537
|
}
|
|
8527
8538
|
function record(value, where) {
|
|
8528
8539
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -8685,8 +8696,94 @@ function responsePayload(id, model, output, end, usage, error, createdAt = Math.
|
|
|
8685
8696
|
return result;
|
|
8686
8697
|
}
|
|
8687
8698
|
|
|
8699
|
+
// src/responses-codec/request-preferences.ts
|
|
8700
|
+
function fields(value, allowed, path) {
|
|
8701
|
+
for (const key of Object.keys(value))
|
|
8702
|
+
if (!allowed.includes(key)) {
|
|
8703
|
+
fail("unsupported_request", "Unrecognized request option", `${path}.${key}`);
|
|
8704
|
+
}
|
|
8705
|
+
}
|
|
8706
|
+
function applyResponsesPreferences(body, result, protocol, capabilities, maxTokens, diagnostics) {
|
|
8707
|
+
const note = (param, action, reason) => diagnostics.push({ param, action, reason });
|
|
8708
|
+
if (body.stream_options !== undefined)
|
|
8709
|
+
atParam("stream_options", () => {
|
|
8710
|
+
const options = record(body.stream_options, "stream_options");
|
|
8711
|
+
fields(options, ["include_usage", "reasoning_summary_delivery"], "stream_options");
|
|
8712
|
+
if (options.include_usage !== undefined) {
|
|
8713
|
+
if (typeof options.include_usage !== "boolean")
|
|
8714
|
+
fail("invalid_payload", "Usage preference must be boolean", "stream_options.include_usage");
|
|
8715
|
+
note("stream_options.include_usage", protocol === "chat_completions" && body.stream === true ? "mapped" : "omitted", protocol === "chat_completions" && body.stream === true ? "stream_usage_required" : "target_usage_events");
|
|
8716
|
+
}
|
|
8717
|
+
if (options.reasoning_summary_delivery !== undefined) {
|
|
8718
|
+
if (options.reasoning_summary_delivery !== "sequential_cutoff")
|
|
8719
|
+
fail("unsupported_request", "Unsupported summary delivery preference", "stream_options.reasoning_summary_delivery");
|
|
8720
|
+
note("stream_options.reasoning_summary_delivery", "omitted", "source_summary_delivery_not_applicable");
|
|
8721
|
+
}
|
|
8722
|
+
});
|
|
8723
|
+
if (body.access_programs !== undefined)
|
|
8724
|
+
atParam("access_programs", () => {
|
|
8725
|
+
const programs = record(body.access_programs, "access_programs");
|
|
8726
|
+
fields(programs, ["cyber"], "access_programs");
|
|
8727
|
+
if (programs.cyber !== undefined && programs.cyber !== "standard") {
|
|
8728
|
+
if (programs.cyber === "daybreak_blue" || programs.cyber === "daybreak_red") {
|
|
8729
|
+
fail("unsupported_access_program", "Selected access program cannot be represented by the target protocol", "access_programs.cyber");
|
|
8730
|
+
}
|
|
8731
|
+
fail("invalid_payload", "Invalid access program", "access_programs.cyber");
|
|
8732
|
+
}
|
|
8733
|
+
note("access_programs", "omitted", "target_uses_own_access_policy");
|
|
8734
|
+
});
|
|
8735
|
+
if (body.reasoning !== undefined)
|
|
8736
|
+
atParam("reasoning", () => {
|
|
8737
|
+
const reasoning = record(body.reasoning, "reasoning");
|
|
8738
|
+
fields(reasoning, ["effort", "summary", "context"], "reasoning");
|
|
8739
|
+
if (reasoning.summary !== undefined) {
|
|
8740
|
+
if (typeof reasoning.summary !== "string" || !["none", "auto", "concise", "detailed"].includes(reasoning.summary))
|
|
8741
|
+
fail("invalid_payload", "Invalid reasoning summary preference", "reasoning.summary");
|
|
8742
|
+
note("reasoning.summary", "omitted", "target_summary_not_guaranteed");
|
|
8743
|
+
}
|
|
8744
|
+
if (reasoning.context !== undefined) {
|
|
8745
|
+
if (reasoning.context !== "all_turns")
|
|
8746
|
+
fail("unsupported_reasoning", "Unsupported reasoning context preference", "reasoning.context");
|
|
8747
|
+
note("reasoning.context", "omitted", "source_reasoning_context_not_applicable");
|
|
8748
|
+
}
|
|
8749
|
+
if (reasoning.effort !== undefined)
|
|
8750
|
+
atParam("reasoning.effort", () => {
|
|
8751
|
+
const effort = string(reasoning.effort, "reasoning effort", true);
|
|
8752
|
+
if (protocol === "chat_completions" && capabilities.reasoningEffort) {
|
|
8753
|
+
result.reasoning_effort = effort;
|
|
8754
|
+
note("reasoning.effort", "mapped", "target_reasoning_effort");
|
|
8755
|
+
} else if (protocol === "anthropic_messages" && capabilities.anthropicThinkingBudget !== undefined && effort !== "none") {
|
|
8756
|
+
const budget = capabilities.anthropicThinkingBudget;
|
|
8757
|
+
if (!Number.isSafeInteger(budget) || budget < 1024 || typeof maxTokens !== "number" || budget >= maxTokens) {
|
|
8758
|
+
fail("unsupported_reasoning", "Anthropic thinking requires an explicit budget below max_output_tokens");
|
|
8759
|
+
}
|
|
8760
|
+
result.thinking = { type: "enabled", budget_tokens: budget };
|
|
8761
|
+
note("reasoning.effort", "mapped", "explicit_target_thinking_budget");
|
|
8762
|
+
} else
|
|
8763
|
+
note("reasoning.effort", "omitted", "target_reasoning_effort_unavailable");
|
|
8764
|
+
});
|
|
8765
|
+
});
|
|
8766
|
+
if (body.text !== undefined)
|
|
8767
|
+
atParam("text", () => {
|
|
8768
|
+
const text = record(body.text, "text");
|
|
8769
|
+
fields(text, ["verbosity", "format"], "text");
|
|
8770
|
+
if (text.verbosity !== undefined) {
|
|
8771
|
+
if (typeof text.verbosity !== "string" || !["low", "medium", "high"].includes(text.verbosity))
|
|
8772
|
+
fail("invalid_payload", "Invalid text verbosity preference", "text.verbosity");
|
|
8773
|
+
note("text.verbosity", "omitted", "target_verbosity_not_available");
|
|
8774
|
+
}
|
|
8775
|
+
});
|
|
8776
|
+
}
|
|
8777
|
+
|
|
8688
8778
|
// src/responses-codec/request.ts
|
|
8689
8779
|
var contentNormalizer = new OpenAIProtocolConversion({ trimWhitespace: false });
|
|
8780
|
+
function definitionKey(value) {
|
|
8781
|
+
if (Array.isArray(value))
|
|
8782
|
+
return `[${value.map(definitionKey).join(",")}]`;
|
|
8783
|
+
if (value && typeof value === "object")
|
|
8784
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${definitionKey(value[key])}`).join(",")}}`;
|
|
8785
|
+
return JSON.stringify(value);
|
|
8786
|
+
}
|
|
8690
8787
|
function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
8691
8788
|
const body = record(raw, "Responses request");
|
|
8692
8789
|
const budget = limits(capabilities.limits);
|
|
@@ -8712,11 +8809,15 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8712
8809
|
"n",
|
|
8713
8810
|
"truncation",
|
|
8714
8811
|
"include",
|
|
8715
|
-
"store"
|
|
8812
|
+
"store",
|
|
8813
|
+
"prompt_cache_key",
|
|
8814
|
+
"client_metadata",
|
|
8815
|
+
"stream_options",
|
|
8816
|
+
"access_programs"
|
|
8716
8817
|
]);
|
|
8717
8818
|
for (const key of Object.keys(body))
|
|
8718
8819
|
if (!supported.has(key))
|
|
8719
|
-
fail("unsupported_request",
|
|
8820
|
+
fail("unsupported_request", "Unrecognized Responses request field", key);
|
|
8720
8821
|
if (body.store === true)
|
|
8721
8822
|
fail("unsupported_request", "Stored Responses require caller-owned persistence");
|
|
8722
8823
|
for (const key of ["previous_response_id", "conversation", "response_id"]) {
|
|
@@ -8729,17 +8830,28 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8729
8830
|
fail("unsupported_request", "Only one response candidate is supported");
|
|
8730
8831
|
if (body.truncation !== undefined && body.truncation !== "disabled")
|
|
8731
8832
|
fail("unsupported_request", "Automatic input truncation cannot be represented");
|
|
8732
|
-
if (body.include !== undefined && list(body.include, "include").
|
|
8833
|
+
if (body.include !== undefined && list(body.include, "include").some((field) => field !== "reasoning.encrypted_content"))
|
|
8733
8834
|
fail("unsupported_request", "Requested extra Responses output fields cannot be represented");
|
|
8835
|
+
if (body.prompt_cache_key !== undefined)
|
|
8836
|
+
string(body.prompt_cache_key, "prompt_cache_key");
|
|
8837
|
+
if (body.client_metadata !== undefined)
|
|
8838
|
+
record(body.client_metadata, "client_metadata");
|
|
8734
8839
|
const model = string(body.model, "model", true);
|
|
8840
|
+
const diagnostics = [];
|
|
8841
|
+
const input = atParam("input", () => typeof body.input === "string" ? [{ role: "user", content: body.input }] : list(body.input, "input"));
|
|
8842
|
+
if (input.length > budget.maxItems)
|
|
8843
|
+
fail("resource_limit", "Too many input items", "input");
|
|
8844
|
+
const canonicalInput = [];
|
|
8735
8845
|
const toolNames = new Map;
|
|
8736
8846
|
const byOriginal = new Map;
|
|
8847
|
+
const definitions = new Map;
|
|
8848
|
+
let examinedTools = 0;
|
|
8737
8849
|
const tools = [];
|
|
8738
8850
|
const identity = (name, namespace) => JSON.stringify([namespace ?? null, name]);
|
|
8739
8851
|
const register = (name, namespace, custom) => {
|
|
8740
8852
|
const key = identity(name, namespace);
|
|
8741
8853
|
if (byOriginal.has(key))
|
|
8742
|
-
fail("tool_name_collision",
|
|
8854
|
+
fail("tool_name_collision", "Duplicate tool identity");
|
|
8743
8855
|
if (toolNames.size >= budget.maxItems)
|
|
8744
8856
|
fail("resource_limit", "Too many tools");
|
|
8745
8857
|
const wire = `bungee_tool_${toolNames.size}`;
|
|
@@ -8747,18 +8859,29 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8747
8859
|
byOriginal.set(key, wire);
|
|
8748
8860
|
return wire;
|
|
8749
8861
|
};
|
|
8750
|
-
const expandTool = (rawTool, namespace) => {
|
|
8862
|
+
const expandTool = (rawTool, namespace, path = "tools") => {
|
|
8863
|
+
if (++examinedTools > budget.maxItems)
|
|
8864
|
+
fail("resource_limit", "Too many tool declarations");
|
|
8751
8865
|
const tool = record(rawTool, "tool");
|
|
8866
|
+
if (namespace === undefined && capabilities.omitOptionalWebSearch && ["web_search", "web_search_preview"].includes(String(tool.type)) && (body.tool_choice === undefined || body.tool_choice === "auto" || body.tool_choice === "none"))
|
|
8867
|
+
return;
|
|
8868
|
+
const allowed = tool.type === "namespace" ? ["type", "name", "description", "tools"] : tool.type === "custom" ? ["type", "name", "description", "format"] : ["type", "name", "description", "parameters", "strict"];
|
|
8869
|
+
for (const key of Object.keys(tool))
|
|
8870
|
+
if (!allowed.includes(key))
|
|
8871
|
+
fail("unsupported_tool", "Unsupported tool definition field", `${path}.${key}`);
|
|
8752
8872
|
if (tool.type === "namespace") {
|
|
8753
8873
|
if (namespace !== undefined)
|
|
8754
8874
|
fail("unsupported_tool", "Nested tool namespaces are not supported");
|
|
8755
8875
|
const name = string(tool.name, "namespace name", true);
|
|
8756
|
-
for (const child of list(tool.tools, "namespace tools"))
|
|
8757
|
-
|
|
8876
|
+
for (const [index, child] of list(tool.tools, "namespace tools").entries()) {
|
|
8877
|
+
const childPath = `${path}.tools[${index}]`;
|
|
8878
|
+
atParam(childPath, () => expandTool(child, name, childPath));
|
|
8879
|
+
}
|
|
8758
8880
|
return;
|
|
8759
8881
|
}
|
|
8760
8882
|
if (tool.type !== "function" && tool.type !== "custom")
|
|
8761
|
-
fail("unsupported_tool",
|
|
8883
|
+
fail("unsupported_tool", "This tool type cannot be executed by Codex through the target protocol");
|
|
8884
|
+
serialized(tool, budget.maxArgumentBytes, "tool definition");
|
|
8762
8885
|
const name = string(tool.name, "tool name", true);
|
|
8763
8886
|
const custom = tool.type === "custom";
|
|
8764
8887
|
if (tool.strict !== undefined && typeof tool.strict !== "boolean")
|
|
@@ -8770,7 +8893,15 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8770
8893
|
if (format.type !== "text" && format.type !== "grammar")
|
|
8771
8894
|
fail("unsupported_tool", "Unknown custom tool format");
|
|
8772
8895
|
}
|
|
8896
|
+
const key = identity(name, namespace);
|
|
8897
|
+
const signature = definitionKey(tool);
|
|
8898
|
+
if (definitions.has(key)) {
|
|
8899
|
+
if (definitions.get(key) !== signature)
|
|
8900
|
+
fail("tool_name_collision", "Conflicting definitions for the same tool identity");
|
|
8901
|
+
return;
|
|
8902
|
+
}
|
|
8773
8903
|
const wire = register(name, namespace, custom);
|
|
8904
|
+
definitions.set(key, signature);
|
|
8774
8905
|
const parameters = custom ? { type: "object", properties: { input: { type: "string" } }, required: ["input"], additionalProperties: false } : record(tool.parameters ?? { type: "object", properties: {} }, "tool parameters");
|
|
8775
8906
|
const description = tool.description === undefined ? undefined : string(tool.description, "tool description");
|
|
8776
8907
|
const customHint = custom ? `Pass the complete original custom tool input verbatim in the input string.${tool.format ? ` Original input format: ${JSON.stringify(tool.format)}` : ""}` : undefined;
|
|
@@ -8783,8 +8914,24 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8783
8914
|
}
|
|
8784
8915
|
};
|
|
8785
8916
|
if (body.tools !== undefined)
|
|
8786
|
-
|
|
8787
|
-
|
|
8917
|
+
atParam("tools", () => {
|
|
8918
|
+
for (const [index, tool] of list(body.tools, "tools").entries())
|
|
8919
|
+
atParam(`tools[${index}]`, () => expandTool(tool, undefined, `tools[${index}]`));
|
|
8920
|
+
});
|
|
8921
|
+
for (const [index, rawItem] of input.entries())
|
|
8922
|
+
atParam(`input[${index}]`, () => {
|
|
8923
|
+
const item = record(rawItem, "input item");
|
|
8924
|
+
if (item.type !== "additional_tools") {
|
|
8925
|
+
canonicalInput.push(item);
|
|
8926
|
+
return;
|
|
8927
|
+
}
|
|
8928
|
+
if (item.content !== undefined)
|
|
8929
|
+
fail("invalid_payload", "A tool declaration carrier cannot also contain message content", `input[${index}].content`);
|
|
8930
|
+
for (const [toolIndex, tool] of list(item.tools, "additional tools").entries()) {
|
|
8931
|
+
const toolPath = `input[${index}].tools[${toolIndex}]`;
|
|
8932
|
+
atParam(toolPath, () => expandTool(tool, undefined, toolPath));
|
|
8933
|
+
}
|
|
8934
|
+
});
|
|
8788
8935
|
const resolve = (item, custom) => {
|
|
8789
8936
|
const name = string(item.name, "call name", true);
|
|
8790
8937
|
const namespace = item.namespace === undefined ? undefined : string(item.namespace, "call namespace", true);
|
|
@@ -8792,7 +8939,7 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8792
8939
|
if (wire === undefined)
|
|
8793
8940
|
wire = register(name, namespace, custom);
|
|
8794
8941
|
if (toolNames.get(wire).custom !== custom)
|
|
8795
|
-
fail("tool_name_collision",
|
|
8942
|
+
fail("tool_name_collision", "Historical tool kind conflicts with its current declaration");
|
|
8796
8943
|
return wire;
|
|
8797
8944
|
};
|
|
8798
8945
|
const normalizeContent = (rawContent, role) => {
|
|
@@ -8803,6 +8950,8 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8803
8950
|
const part = record(rawPart, "content part");
|
|
8804
8951
|
if (part.type === "input_text" || part.type === "output_text" || part.type === "text")
|
|
8805
8952
|
string(part.text, "content text");
|
|
8953
|
+
else if (part.type === "refusal" && role === "assistant")
|
|
8954
|
+
string(part.refusal, "refusal");
|
|
8806
8955
|
else if (part.type === "input_image" && role === "user") {
|
|
8807
8956
|
if (protocol !== "chat_completions")
|
|
8808
8957
|
fail("unsupported_content", "Anthropic image conversion requires provider-specific URL/data support");
|
|
@@ -8810,11 +8959,14 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8810
8959
|
if (part.detail !== undefined && !["auto", "low", "high"].includes(String(part.detail)))
|
|
8811
8960
|
fail("unsupported_content", "Unknown image detail");
|
|
8812
8961
|
} else
|
|
8813
|
-
fail("unsupported_content",
|
|
8962
|
+
fail("unsupported_content", "Unsupported message content type");
|
|
8814
8963
|
if (part.annotations !== undefined && list(part.annotations, "annotations").length)
|
|
8815
8964
|
fail("unsupported_content", "Output annotations cannot be represented in input history");
|
|
8816
8965
|
}
|
|
8817
|
-
const normalized = contentNormalizer.normalizeResponsesMessageContent(parts)
|
|
8966
|
+
const normalized = contentNormalizer.normalizeResponsesMessageContent(parts.map((raw) => {
|
|
8967
|
+
const part = raw;
|
|
8968
|
+
return part.type === "refusal" ? { type: "output_text", text: part.refusal } : part;
|
|
8969
|
+
}));
|
|
8818
8970
|
for (let i = 0;i < parts.length; i++) {
|
|
8819
8971
|
const part = parts[i];
|
|
8820
8972
|
if (part.type === "input_image" && part.detail !== undefined)
|
|
@@ -8823,6 +8975,8 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8823
8975
|
return normalized;
|
|
8824
8976
|
};
|
|
8825
8977
|
const chat = [];
|
|
8978
|
+
const messageParams = new WeakMap;
|
|
8979
|
+
let messageParam = "instructions";
|
|
8826
8980
|
const callIds = new Set;
|
|
8827
8981
|
const answered = new Set;
|
|
8828
8982
|
let pendingReasoning = "";
|
|
@@ -8830,73 +8984,77 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8830
8984
|
if (chat.length >= budget.maxItems)
|
|
8831
8985
|
fail("resource_limit", "Too many input messages");
|
|
8832
8986
|
chat.push(message);
|
|
8987
|
+
messageParams.set(message, messageParam);
|
|
8833
8988
|
};
|
|
8834
8989
|
if (body.instructions !== undefined)
|
|
8835
8990
|
addMessage({ role: "system", content: string(body.instructions, "instructions") });
|
|
8836
|
-
const
|
|
8837
|
-
|
|
8838
|
-
|
|
8839
|
-
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
if (
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
if (
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
|
|
8878
|
-
|
|
8879
|
-
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8991
|
+
for (const [inputIndex, rawItem] of input.entries())
|
|
8992
|
+
atParam(`input[${inputIndex}]`, () => {
|
|
8993
|
+
messageParam = `input[${inputIndex}]`;
|
|
8994
|
+
const item = record(rawItem, "input item");
|
|
8995
|
+
if (item.type === "additional_tools")
|
|
8996
|
+
return;
|
|
8997
|
+
if (item.encrypted_content != null || item.type === "compaction")
|
|
8998
|
+
fail("unsupported_content", "Encrypted reasoning and compaction cannot be restored");
|
|
8999
|
+
if (item.type === "item_reference")
|
|
9000
|
+
fail("unresolved_reference", "Input item references must be resolved before conversion");
|
|
9001
|
+
if (item.type === "reasoning") {
|
|
9002
|
+
if (protocol !== "chat_completions" || !(capabilities.reasoningHistory || capabilities.reasoningEffort))
|
|
9003
|
+
fail("unsupported_reasoning", "Reasoning history requires an explicit compatible reasoning capability");
|
|
9004
|
+
if (item.content != null)
|
|
9005
|
+
fail("unsupported_reasoning", "Reasoning content requires an explicit history mapping", `${messageParam}.content`);
|
|
9006
|
+
const summary = list(item.summary, "reasoning summary");
|
|
9007
|
+
pendingReasoning += summary.map((part) => {
|
|
9008
|
+
const p = record(part, "reasoning summary");
|
|
9009
|
+
if (p.type !== "summary_text")
|
|
9010
|
+
fail("unsupported_reasoning", "Unknown reasoning summary");
|
|
9011
|
+
return string(p.text, "summary text");
|
|
9012
|
+
}).join("");
|
|
9013
|
+
return;
|
|
9014
|
+
}
|
|
9015
|
+
if (item.type === "function_call" || item.type === "custom_tool_call") {
|
|
9016
|
+
const custom = item.type === "custom_tool_call";
|
|
9017
|
+
const id = string(item.call_id, "call_id", true);
|
|
9018
|
+
if (callIds.has(id))
|
|
9019
|
+
fail("invalid_tool_history", "Duplicate tool call identifier");
|
|
9020
|
+
callIds.add(id);
|
|
9021
|
+
const args = custom ? JSON.stringify({ input: string(item.input, "custom input") }) : string(item.arguments, "arguments");
|
|
9022
|
+
bounded(args, budget.maxArgumentBytes, "arguments");
|
|
9023
|
+
argumentObject(args);
|
|
9024
|
+
const call = { id, type: "function", function: { name: resolve(item, custom), arguments: args } };
|
|
9025
|
+
const previous = chat.at(-1);
|
|
9026
|
+
if (previous?.role === "assistant") {
|
|
9027
|
+
previous.tool_calls = [...previous.tool_calls ?? [], call];
|
|
9028
|
+
if (pendingReasoning)
|
|
9029
|
+
previous.reasoning_content = pendingReasoning;
|
|
9030
|
+
} else
|
|
9031
|
+
addMessage({ role: "assistant", content: null, tool_calls: [call], ...pendingReasoning ? { reasoning_content: pendingReasoning } : {} });
|
|
9032
|
+
pendingReasoning = "";
|
|
9033
|
+
return;
|
|
9034
|
+
}
|
|
9035
|
+
if (item.type === "function_call_output" || item.type === "custom_tool_call_output") {
|
|
9036
|
+
const id = string(item.call_id, "call_id", true);
|
|
9037
|
+
if (!callIds.has(id) || answered.has(id))
|
|
9038
|
+
fail("invalid_tool_history", "Unmatched or duplicate tool result");
|
|
9039
|
+
answered.add(id);
|
|
9040
|
+
const output = typeof item.output === "string" ? item.output : normalizeContent(item.output, "tool");
|
|
9041
|
+
addMessage({ role: "tool", tool_call_id: id, content: output, ...item.is_error === true ? { is_error: true } : {} });
|
|
9042
|
+
return;
|
|
9043
|
+
}
|
|
9044
|
+
if (item.type !== undefined && item.type !== "message")
|
|
9045
|
+
fail("unsupported_content", "Unsupported input item type");
|
|
9046
|
+
const role = string(item.role, "message role", true);
|
|
9047
|
+
if (!["user", "assistant", "system", "developer"].includes(role))
|
|
9048
|
+
fail("unsupported_content", "Unknown message role");
|
|
9049
|
+
const message = { role, content: normalizeContent(item.content, role) };
|
|
9050
|
+
if (pendingReasoning) {
|
|
9051
|
+
if (role !== "assistant")
|
|
9052
|
+
fail("unsupported_reasoning", "Reasoning must precede an assistant item");
|
|
9053
|
+
message.reasoning_content = pendingReasoning;
|
|
9054
|
+
pendingReasoning = "";
|
|
9055
|
+
}
|
|
9056
|
+
addMessage(message);
|
|
9057
|
+
});
|
|
8900
9058
|
if (pendingReasoning)
|
|
8901
9059
|
fail("unsupported_reasoning", "Orphan reasoning history cannot be represented");
|
|
8902
9060
|
const result = { model, messages: chat };
|
|
@@ -8929,41 +9087,35 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8929
9087
|
result.tool_choice = protocol === "chat_completions" ? { type: "function", function: { name: wire } } : { type: "tool", name: wire };
|
|
8930
9088
|
}
|
|
8931
9089
|
}
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
if (protocol === "chat_completions")
|
|
8962
|
-
result.response_format = format;
|
|
8963
|
-
} else
|
|
8964
|
-
fail("unsupported_request", "Unknown text format");
|
|
8965
|
-
}
|
|
8966
|
-
}
|
|
9090
|
+
applyResponsesPreferences(body, result, protocol, capabilities, maxTokens, diagnostics);
|
|
9091
|
+
if (body.text !== undefined)
|
|
9092
|
+
atParam("text.format", () => {
|
|
9093
|
+
const text = record(body.text, "text");
|
|
9094
|
+
if (text.format !== undefined) {
|
|
9095
|
+
const format = record(text.format, "text format");
|
|
9096
|
+
const allowed = format.type === "json_schema" ? ["type", "name", "schema", "strict", "description"] : ["type"];
|
|
9097
|
+
for (const key of Object.keys(format))
|
|
9098
|
+
if (!allowed.includes(key))
|
|
9099
|
+
fail("unsupported_request", "Unsupported output format field", `text.format.${key}`);
|
|
9100
|
+
if (format.type === "json_schema") {
|
|
9101
|
+
atParam("text.format.name", () => string(format.name, "output schema name", true));
|
|
9102
|
+
atParam("text.format.schema", () => record(format.schema, "output schema"));
|
|
9103
|
+
if (format.strict !== undefined && typeof format.strict !== "boolean")
|
|
9104
|
+
fail("invalid_payload", "Output schema strict must be boolean", "text.format.strict");
|
|
9105
|
+
if (format.description !== undefined)
|
|
9106
|
+
atParam("text.format.description", () => string(format.description, "output schema description"));
|
|
9107
|
+
}
|
|
9108
|
+
if (protocol !== "chat_completions" && format.type !== "text")
|
|
9109
|
+
fail("unsupported_request", "Anthropic structured output requires a separate provider capability");
|
|
9110
|
+
if (format.type === "json_schema")
|
|
9111
|
+
result.response_format = { type: "json_schema", json_schema: { name: format.name, schema: format.schema, ...format.strict !== undefined ? { strict: format.strict } : {}, ...format.description !== undefined ? { description: format.description } : {} } };
|
|
9112
|
+
else if (format.type === "json_object" || format.type === "text") {
|
|
9113
|
+
if (protocol === "chat_completions")
|
|
9114
|
+
result.response_format = format;
|
|
9115
|
+
} else
|
|
9116
|
+
fail("unsupported_request", "Unknown text format");
|
|
9117
|
+
}
|
|
9118
|
+
});
|
|
8967
9119
|
if (protocol === "anthropic_messages") {
|
|
8968
9120
|
const system = [];
|
|
8969
9121
|
const messages = [];
|
|
@@ -8977,7 +9129,7 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
8977
9129
|
for (const msg of chat) {
|
|
8978
9130
|
if (msg.role === "system" || msg.role === "developer") {
|
|
8979
9131
|
if (messages.length)
|
|
8980
|
-
fail("unsupported_content", "Mid-conversation system instructions cannot be relocated safely");
|
|
9132
|
+
fail("unsupported_content", "Mid-conversation system instructions cannot be relocated safely", `${messageParams.get(msg)}.role`);
|
|
8981
9133
|
system.push(typeof msg.content === "string" ? msg.content : msg.content.map((p) => string(p.text, "system text")).join(""));
|
|
8982
9134
|
continue;
|
|
8983
9135
|
}
|
|
@@ -9003,7 +9155,7 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
9003
9155
|
delete result.parallel_tool_calls;
|
|
9004
9156
|
}
|
|
9005
9157
|
}
|
|
9006
|
-
return { body: result, toolNames };
|
|
9158
|
+
return { body: result, toolNames, canonicalInput, diagnostics };
|
|
9007
9159
|
}
|
|
9008
9160
|
// src/responses-codec/events.ts
|
|
9009
9161
|
class ResponsesEventEncoder {
|
|
@@ -16,16 +16,27 @@ export interface ResponsesCodecLimits {
|
|
|
16
16
|
export declare const DEFAULT_RESPONSES_CODEC_LIMITS: Readonly<ResponsesCodecLimits>;
|
|
17
17
|
export interface ResponsesCodecCapabilities {
|
|
18
18
|
reasoningEffort?: boolean;
|
|
19
|
+
/** Plain reasoning history support is independent of effort parameter mapping. */
|
|
20
|
+
reasoningHistory?: boolean;
|
|
21
|
+
/** Codex declares optional hosted search even when the selected model has no search backend. */
|
|
22
|
+
omitOptionalWebSearch?: boolean;
|
|
19
23
|
/** Explicit provider-approved thinking budget; do not guess budgets from effort labels. */
|
|
20
24
|
anthropicThinkingBudget?: number;
|
|
21
25
|
maxOutputTokens?: number;
|
|
22
26
|
limits?: Partial<ResponsesCodecLimits>;
|
|
23
27
|
}
|
|
28
|
+
export interface ResponsesConversionDiagnostic {
|
|
29
|
+
param: string;
|
|
30
|
+
action: 'mapped' | 'omitted';
|
|
31
|
+
reason: string;
|
|
32
|
+
}
|
|
24
33
|
export declare class ResponsesCodecError extends Error {
|
|
25
34
|
readonly code: string;
|
|
26
|
-
|
|
35
|
+
readonly param?: string | undefined;
|
|
36
|
+
constructor(code: string, message: string, param?: string | undefined);
|
|
27
37
|
}
|
|
28
|
-
export declare function fail(code: string, message: string): never;
|
|
38
|
+
export declare function fail(code: string, message: string, param?: string): never;
|
|
39
|
+
export declare function atParam<T>(param: string, run: () => T): T;
|
|
29
40
|
export declare function record(value: unknown, where: string): JsonRecord;
|
|
30
41
|
export declare function string(value: unknown, where: string, nonempty?: boolean): string;
|
|
31
42
|
export declare function list(value: unknown, where: string): unknown[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { decodeResponsesRequest, type DecodedResponsesRequest } from './request';
|
|
2
2
|
export { encodeResponsesResult } from './result';
|
|
3
3
|
export { ResponsesEventEncoder } from './events';
|
|
4
|
-
export { ResponsesCodecError, DEFAULT_RESPONSES_CODEC_LIMITS, type ResponsesProtocol, type ResponsesToolName, type ResponsesToolNames, type ResponsesCodecCapabilities, type ResponsesCodecLimits } from './common';
|
|
4
|
+
export { ResponsesCodecError, DEFAULT_RESPONSES_CODEC_LIMITS, type ResponsesProtocol, type ResponsesToolName, type ResponsesToolNames, type ResponsesCodecCapabilities, type ResponsesCodecLimits, type ResponsesConversionDiagnostic } from './common';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type JsonRecord, type ResponsesCodecCapabilities, type ResponsesConversionDiagnostic, type ResponsesProtocol } from './common';
|
|
2
|
+
/** Known preferences may be omitted; output constraints are handled by the request renderer. */
|
|
3
|
+
export declare function applyResponsesPreferences(body: JsonRecord, result: JsonRecord, protocol: ResponsesProtocol, capabilities: ResponsesCodecCapabilities, maxTokens: unknown, diagnostics: ResponsesConversionDiagnostic[]): void;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { type JsonRecord, type ResponsesCodecCapabilities, type ResponsesProtocol, type ResponsesToolNames } from './common';
|
|
1
|
+
import { type JsonRecord, type ResponsesCodecCapabilities, type ResponsesConversionDiagnostic, type ResponsesProtocol, type ResponsesToolNames } from './common';
|
|
2
2
|
export interface DecodedResponsesRequest {
|
|
3
3
|
body: JsonRecord;
|
|
4
4
|
toolNames: ResponsesToolNames;
|
|
5
|
+
canonicalInput: JsonRecord[];
|
|
6
|
+
diagnostics: ResponsesConversionDiagnostic[];
|
|
5
7
|
}
|
|
6
8
|
/** Converts only self-contained history. Reference resolution belongs to the caller. */
|
|
7
9
|
export declare function decodeResponsesRequest(raw: unknown, protocol: ResponsesProtocol, capabilities?: ResponsesCodecCapabilities): DecodedResponsesRequest;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jeffusion/bungee-llms",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.21.0",
|
|
4
4
|
"description": "Extensible LLM adapters and protocol conversion package for Bungee",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"prepublishOnly": "bun run build"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@jeffusion/bungee-types": "^5.
|
|
14
|
+
"@jeffusion/bungee-types": "^5.21.0"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"bun-types": "^1.3.2",
|