@jeffusion/bungee-core 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/config-worker/private-transport.d.ts +2 -0
- package/dist/data-admission/errors.d.ts +6 -1
- package/dist/gateway/contracts.d.ts +15 -4
- package/dist/gateway/forward-plugin.d.ts +2 -0
- package/dist/gateway/plugin.js +12 -2
- package/dist/gateway/request-plugin.d.ts +6 -0
- package/dist/gateway/responses-websocket-session.d.ts +18 -0
- package/dist/gateway/runtime.d.ts +1 -0
- package/dist/gateway/websocket-contracts.d.ts +1 -0
- package/dist/hooks/plugin-hooks.d.ts +2 -3
- package/dist/logger/access-log-writer.d.ts +2 -2
- package/dist/main.js +1199 -176
- package/dist/plugins/ai-transformer/index.js +1 -1
- package/dist/plugins/ai-transformer/index.js.map +4 -4
- package/dist/plugins/codex-router/control.js +6 -2
- package/dist/plugins/codex-router/control.js.map +4 -4
- package/dist/plugins/codex-router/index.js +498 -165
- package/dist/plugins/codex-router/index.js.map +13 -11
- package/dist/plugins/codex-router/manifest.json +124 -8
- package/dist/plugins/key-access/index.js +12 -2
- package/dist/plugins/key-access/index.js.map +3 -3
- package/dist/plugins/key-access/ingress.js +12 -2
- package/dist/plugins/key-access/ingress.js.map +3 -3
- package/dist/plugins/openai-messages-to-chat/index.js +6 -4
- package/dist/plugins/openai-messages-to-chat/index.js.map +4 -4
- package/dist/plugins/token-metering/index.js +1 -1
- package/dist/plugins/token-metering/index.js.map +4 -4
- package/dist/plugins/token-stats/manifest.json +16 -6
- package/dist/plugins/token-stats/ui/ClientModelPicker.svelte +13 -61
- package/dist/plugins/token-stats/ui/PriceModelPicker.svelte +18 -74
- package/dist/plugins/token-stats/ui/TokenStatsSettings.svelte +3 -2
- package/dist/plugins/token-stats/ui/model-search.ts +2 -45
- package/dist/plugins/token-stats/ui/pricing-catalog.ts +2 -1
- package/dist/scoped-plugin-registry.d.ts +7 -0
- package/dist/websocket/index.d.ts +1 -1
- package/dist/websocket/transport.d.ts +40 -18
- package/package.json +2 -2
- package/dist/plugins/chatgpt-oauth/ui/account-model.test.js +0 -69
- package/dist/plugins/local-accounts/ui/login-errors.test.ts +0 -22
- package/dist/plugins/token-stats/ui/client-model-search.test.ts +0 -58
- package/dist/plugins/token-stats/ui/pricing-catalog.test.ts +0 -114
|
@@ -2256,14 +2256,25 @@ var DEFAULT_RESPONSES_CODEC_LIMITS = Object.freeze({
|
|
|
2256
2256
|
|
|
2257
2257
|
class ResponsesCodecError extends Error {
|
|
2258
2258
|
code;
|
|
2259
|
-
|
|
2259
|
+
param;
|
|
2260
|
+
constructor(code, message, param) {
|
|
2260
2261
|
super(message);
|
|
2261
2262
|
this.code = code;
|
|
2263
|
+
this.param = param;
|
|
2262
2264
|
this.name = "ResponsesCodecError";
|
|
2263
2265
|
}
|
|
2264
2266
|
}
|
|
2265
|
-
function fail(code, message) {
|
|
2266
|
-
throw new ResponsesCodecError(code, message);
|
|
2267
|
+
function fail(code, message, param) {
|
|
2268
|
+
throw new ResponsesCodecError(code, message, param);
|
|
2269
|
+
}
|
|
2270
|
+
function atParam(param, run) {
|
|
2271
|
+
try {
|
|
2272
|
+
return run();
|
|
2273
|
+
} catch (error) {
|
|
2274
|
+
if (error instanceof ResponsesCodecError && error.param === undefined)
|
|
2275
|
+
throw new ResponsesCodecError(error.code, error.message, param);
|
|
2276
|
+
throw error;
|
|
2277
|
+
}
|
|
2267
2278
|
}
|
|
2268
2279
|
function record(value, where) {
|
|
2269
2280
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -2426,8 +2437,94 @@ function responsePayload(id, model, output, end, usage, error, createdAt = Math.
|
|
|
2426
2437
|
return result;
|
|
2427
2438
|
}
|
|
2428
2439
|
|
|
2440
|
+
// packages/llms/src/responses-codec/request-preferences.ts
|
|
2441
|
+
function fields(value, allowed, path) {
|
|
2442
|
+
for (const key of Object.keys(value))
|
|
2443
|
+
if (!allowed.includes(key)) {
|
|
2444
|
+
fail("unsupported_request", "Unrecognized request option", `${path}.${key}`);
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
function applyResponsesPreferences(body, result, protocol, capabilities, maxTokens, diagnostics) {
|
|
2448
|
+
const note = (param, action, reason) => diagnostics.push({ param, action, reason });
|
|
2449
|
+
if (body.stream_options !== undefined)
|
|
2450
|
+
atParam("stream_options", () => {
|
|
2451
|
+
const options = record(body.stream_options, "stream_options");
|
|
2452
|
+
fields(options, ["include_usage", "reasoning_summary_delivery"], "stream_options");
|
|
2453
|
+
if (options.include_usage !== undefined) {
|
|
2454
|
+
if (typeof options.include_usage !== "boolean")
|
|
2455
|
+
fail("invalid_payload", "Usage preference must be boolean", "stream_options.include_usage");
|
|
2456
|
+
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");
|
|
2457
|
+
}
|
|
2458
|
+
if (options.reasoning_summary_delivery !== undefined) {
|
|
2459
|
+
if (options.reasoning_summary_delivery !== "sequential_cutoff")
|
|
2460
|
+
fail("unsupported_request", "Unsupported summary delivery preference", "stream_options.reasoning_summary_delivery");
|
|
2461
|
+
note("stream_options.reasoning_summary_delivery", "omitted", "source_summary_delivery_not_applicable");
|
|
2462
|
+
}
|
|
2463
|
+
});
|
|
2464
|
+
if (body.access_programs !== undefined)
|
|
2465
|
+
atParam("access_programs", () => {
|
|
2466
|
+
const programs = record(body.access_programs, "access_programs");
|
|
2467
|
+
fields(programs, ["cyber"], "access_programs");
|
|
2468
|
+
if (programs.cyber !== undefined && programs.cyber !== "standard") {
|
|
2469
|
+
if (programs.cyber === "daybreak_blue" || programs.cyber === "daybreak_red") {
|
|
2470
|
+
fail("unsupported_access_program", "Selected access program cannot be represented by the target protocol", "access_programs.cyber");
|
|
2471
|
+
}
|
|
2472
|
+
fail("invalid_payload", "Invalid access program", "access_programs.cyber");
|
|
2473
|
+
}
|
|
2474
|
+
note("access_programs", "omitted", "target_uses_own_access_policy");
|
|
2475
|
+
});
|
|
2476
|
+
if (body.reasoning !== undefined)
|
|
2477
|
+
atParam("reasoning", () => {
|
|
2478
|
+
const reasoning = record(body.reasoning, "reasoning");
|
|
2479
|
+
fields(reasoning, ["effort", "summary", "context"], "reasoning");
|
|
2480
|
+
if (reasoning.summary !== undefined) {
|
|
2481
|
+
if (typeof reasoning.summary !== "string" || !["none", "auto", "concise", "detailed"].includes(reasoning.summary))
|
|
2482
|
+
fail("invalid_payload", "Invalid reasoning summary preference", "reasoning.summary");
|
|
2483
|
+
note("reasoning.summary", "omitted", "target_summary_not_guaranteed");
|
|
2484
|
+
}
|
|
2485
|
+
if (reasoning.context !== undefined) {
|
|
2486
|
+
if (reasoning.context !== "all_turns")
|
|
2487
|
+
fail("unsupported_reasoning", "Unsupported reasoning context preference", "reasoning.context");
|
|
2488
|
+
note("reasoning.context", "omitted", "source_reasoning_context_not_applicable");
|
|
2489
|
+
}
|
|
2490
|
+
if (reasoning.effort !== undefined)
|
|
2491
|
+
atParam("reasoning.effort", () => {
|
|
2492
|
+
const effort = string(reasoning.effort, "reasoning effort", true);
|
|
2493
|
+
if (protocol === "chat_completions" && capabilities.reasoningEffort) {
|
|
2494
|
+
result.reasoning_effort = effort;
|
|
2495
|
+
note("reasoning.effort", "mapped", "target_reasoning_effort");
|
|
2496
|
+
} else if (protocol === "anthropic_messages" && capabilities.anthropicThinkingBudget !== undefined && effort !== "none") {
|
|
2497
|
+
const budget = capabilities.anthropicThinkingBudget;
|
|
2498
|
+
if (!Number.isSafeInteger(budget) || budget < 1024 || typeof maxTokens !== "number" || budget >= maxTokens) {
|
|
2499
|
+
fail("unsupported_reasoning", "Anthropic thinking requires an explicit budget below max_output_tokens");
|
|
2500
|
+
}
|
|
2501
|
+
result.thinking = { type: "enabled", budget_tokens: budget };
|
|
2502
|
+
note("reasoning.effort", "mapped", "explicit_target_thinking_budget");
|
|
2503
|
+
} else
|
|
2504
|
+
note("reasoning.effort", "omitted", "target_reasoning_effort_unavailable");
|
|
2505
|
+
});
|
|
2506
|
+
});
|
|
2507
|
+
if (body.text !== undefined)
|
|
2508
|
+
atParam("text", () => {
|
|
2509
|
+
const text = record(body.text, "text");
|
|
2510
|
+
fields(text, ["verbosity", "format"], "text");
|
|
2511
|
+
if (text.verbosity !== undefined) {
|
|
2512
|
+
if (typeof text.verbosity !== "string" || !["low", "medium", "high"].includes(text.verbosity))
|
|
2513
|
+
fail("invalid_payload", "Invalid text verbosity preference", "text.verbosity");
|
|
2514
|
+
note("text.verbosity", "omitted", "target_verbosity_not_available");
|
|
2515
|
+
}
|
|
2516
|
+
});
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2429
2519
|
// packages/llms/src/responses-codec/request.ts
|
|
2430
2520
|
var contentNormalizer = new OpenAIProtocolConversion({ trimWhitespace: false });
|
|
2521
|
+
function definitionKey(value) {
|
|
2522
|
+
if (Array.isArray(value))
|
|
2523
|
+
return `[${value.map(definitionKey).join(",")}]`;
|
|
2524
|
+
if (value && typeof value === "object")
|
|
2525
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${definitionKey(value[key])}`).join(",")}}`;
|
|
2526
|
+
return JSON.stringify(value);
|
|
2527
|
+
}
|
|
2431
2528
|
function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
2432
2529
|
const body = record(raw, "Responses request");
|
|
2433
2530
|
const budget = limits(capabilities.limits);
|
|
@@ -2453,11 +2550,15 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
2453
2550
|
"n",
|
|
2454
2551
|
"truncation",
|
|
2455
2552
|
"include",
|
|
2456
|
-
"store"
|
|
2553
|
+
"store",
|
|
2554
|
+
"prompt_cache_key",
|
|
2555
|
+
"client_metadata",
|
|
2556
|
+
"stream_options",
|
|
2557
|
+
"access_programs"
|
|
2457
2558
|
]);
|
|
2458
2559
|
for (const key of Object.keys(body))
|
|
2459
2560
|
if (!supported.has(key))
|
|
2460
|
-
fail("unsupported_request",
|
|
2561
|
+
fail("unsupported_request", "Unrecognized Responses request field", key);
|
|
2461
2562
|
if (body.store === true)
|
|
2462
2563
|
fail("unsupported_request", "Stored Responses require caller-owned persistence");
|
|
2463
2564
|
for (const key of ["previous_response_id", "conversation", "response_id"]) {
|
|
@@ -2470,17 +2571,28 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
2470
2571
|
fail("unsupported_request", "Only one response candidate is supported");
|
|
2471
2572
|
if (body.truncation !== undefined && body.truncation !== "disabled")
|
|
2472
2573
|
fail("unsupported_request", "Automatic input truncation cannot be represented");
|
|
2473
|
-
if (body.include !== undefined && list(body.include, "include").
|
|
2574
|
+
if (body.include !== undefined && list(body.include, "include").some((field) => field !== "reasoning.encrypted_content"))
|
|
2474
2575
|
fail("unsupported_request", "Requested extra Responses output fields cannot be represented");
|
|
2576
|
+
if (body.prompt_cache_key !== undefined)
|
|
2577
|
+
string(body.prompt_cache_key, "prompt_cache_key");
|
|
2578
|
+
if (body.client_metadata !== undefined)
|
|
2579
|
+
record(body.client_metadata, "client_metadata");
|
|
2475
2580
|
const model = string(body.model, "model", true);
|
|
2581
|
+
const diagnostics = [];
|
|
2582
|
+
const input = atParam("input", () => typeof body.input === "string" ? [{ role: "user", content: body.input }] : list(body.input, "input"));
|
|
2583
|
+
if (input.length > budget.maxItems)
|
|
2584
|
+
fail("resource_limit", "Too many input items", "input");
|
|
2585
|
+
const canonicalInput = [];
|
|
2476
2586
|
const toolNames = new Map;
|
|
2477
2587
|
const byOriginal = new Map;
|
|
2588
|
+
const definitions = new Map;
|
|
2589
|
+
let examinedTools = 0;
|
|
2478
2590
|
const tools = [];
|
|
2479
2591
|
const identity = (name, namespace) => JSON.stringify([namespace ?? null, name]);
|
|
2480
2592
|
const register = (name, namespace, custom) => {
|
|
2481
2593
|
const key = identity(name, namespace);
|
|
2482
2594
|
if (byOriginal.has(key))
|
|
2483
|
-
fail("tool_name_collision",
|
|
2595
|
+
fail("tool_name_collision", "Duplicate tool identity");
|
|
2484
2596
|
if (toolNames.size >= budget.maxItems)
|
|
2485
2597
|
fail("resource_limit", "Too many tools");
|
|
2486
2598
|
const wire = `bungee_tool_${toolNames.size}`;
|
|
@@ -2488,18 +2600,29 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
2488
2600
|
byOriginal.set(key, wire);
|
|
2489
2601
|
return wire;
|
|
2490
2602
|
};
|
|
2491
|
-
const expandTool = (rawTool, namespace) => {
|
|
2603
|
+
const expandTool = (rawTool, namespace, path = "tools") => {
|
|
2604
|
+
if (++examinedTools > budget.maxItems)
|
|
2605
|
+
fail("resource_limit", "Too many tool declarations");
|
|
2492
2606
|
const tool = record(rawTool, "tool");
|
|
2607
|
+
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"))
|
|
2608
|
+
return;
|
|
2609
|
+
const allowed = tool.type === "namespace" ? ["type", "name", "description", "tools"] : tool.type === "custom" ? ["type", "name", "description", "format"] : ["type", "name", "description", "parameters", "strict"];
|
|
2610
|
+
for (const key of Object.keys(tool))
|
|
2611
|
+
if (!allowed.includes(key))
|
|
2612
|
+
fail("unsupported_tool", "Unsupported tool definition field", `${path}.${key}`);
|
|
2493
2613
|
if (tool.type === "namespace") {
|
|
2494
2614
|
if (namespace !== undefined)
|
|
2495
2615
|
fail("unsupported_tool", "Nested tool namespaces are not supported");
|
|
2496
2616
|
const name = string(tool.name, "namespace name", true);
|
|
2497
|
-
for (const child of list(tool.tools, "namespace tools"))
|
|
2498
|
-
|
|
2617
|
+
for (const [index, child] of list(tool.tools, "namespace tools").entries()) {
|
|
2618
|
+
const childPath = `${path}.tools[${index}]`;
|
|
2619
|
+
atParam(childPath, () => expandTool(child, name, childPath));
|
|
2620
|
+
}
|
|
2499
2621
|
return;
|
|
2500
2622
|
}
|
|
2501
2623
|
if (tool.type !== "function" && tool.type !== "custom")
|
|
2502
|
-
fail("unsupported_tool",
|
|
2624
|
+
fail("unsupported_tool", "This tool type cannot be executed by Codex through the target protocol");
|
|
2625
|
+
serialized(tool, budget.maxArgumentBytes, "tool definition");
|
|
2503
2626
|
const name = string(tool.name, "tool name", true);
|
|
2504
2627
|
const custom = tool.type === "custom";
|
|
2505
2628
|
if (tool.strict !== undefined && typeof tool.strict !== "boolean")
|
|
@@ -2511,7 +2634,15 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
2511
2634
|
if (format.type !== "text" && format.type !== "grammar")
|
|
2512
2635
|
fail("unsupported_tool", "Unknown custom tool format");
|
|
2513
2636
|
}
|
|
2637
|
+
const key = identity(name, namespace);
|
|
2638
|
+
const signature = definitionKey(tool);
|
|
2639
|
+
if (definitions.has(key)) {
|
|
2640
|
+
if (definitions.get(key) !== signature)
|
|
2641
|
+
fail("tool_name_collision", "Conflicting definitions for the same tool identity");
|
|
2642
|
+
return;
|
|
2643
|
+
}
|
|
2514
2644
|
const wire = register(name, namespace, custom);
|
|
2645
|
+
definitions.set(key, signature);
|
|
2515
2646
|
const parameters = custom ? { type: "object", properties: { input: { type: "string" } }, required: ["input"], additionalProperties: false } : record(tool.parameters ?? { type: "object", properties: {} }, "tool parameters");
|
|
2516
2647
|
const description = tool.description === undefined ? undefined : string(tool.description, "tool description");
|
|
2517
2648
|
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;
|
|
@@ -2524,8 +2655,24 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
2524
2655
|
}
|
|
2525
2656
|
};
|
|
2526
2657
|
if (body.tools !== undefined)
|
|
2527
|
-
|
|
2528
|
-
|
|
2658
|
+
atParam("tools", () => {
|
|
2659
|
+
for (const [index, tool] of list(body.tools, "tools").entries())
|
|
2660
|
+
atParam(`tools[${index}]`, () => expandTool(tool, undefined, `tools[${index}]`));
|
|
2661
|
+
});
|
|
2662
|
+
for (const [index, rawItem] of input.entries())
|
|
2663
|
+
atParam(`input[${index}]`, () => {
|
|
2664
|
+
const item = record(rawItem, "input item");
|
|
2665
|
+
if (item.type !== "additional_tools") {
|
|
2666
|
+
canonicalInput.push(item);
|
|
2667
|
+
return;
|
|
2668
|
+
}
|
|
2669
|
+
if (item.content !== undefined)
|
|
2670
|
+
fail("invalid_payload", "A tool declaration carrier cannot also contain message content", `input[${index}].content`);
|
|
2671
|
+
for (const [toolIndex, tool] of list(item.tools, "additional tools").entries()) {
|
|
2672
|
+
const toolPath = `input[${index}].tools[${toolIndex}]`;
|
|
2673
|
+
atParam(toolPath, () => expandTool(tool, undefined, toolPath));
|
|
2674
|
+
}
|
|
2675
|
+
});
|
|
2529
2676
|
const resolve = (item, custom) => {
|
|
2530
2677
|
const name = string(item.name, "call name", true);
|
|
2531
2678
|
const namespace = item.namespace === undefined ? undefined : string(item.namespace, "call namespace", true);
|
|
@@ -2533,7 +2680,7 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
2533
2680
|
if (wire === undefined)
|
|
2534
2681
|
wire = register(name, namespace, custom);
|
|
2535
2682
|
if (toolNames.get(wire).custom !== custom)
|
|
2536
|
-
fail("tool_name_collision",
|
|
2683
|
+
fail("tool_name_collision", "Historical tool kind conflicts with its current declaration");
|
|
2537
2684
|
return wire;
|
|
2538
2685
|
};
|
|
2539
2686
|
const normalizeContent = (rawContent, role) => {
|
|
@@ -2544,6 +2691,8 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
2544
2691
|
const part = record(rawPart, "content part");
|
|
2545
2692
|
if (part.type === "input_text" || part.type === "output_text" || part.type === "text")
|
|
2546
2693
|
string(part.text, "content text");
|
|
2694
|
+
else if (part.type === "refusal" && role === "assistant")
|
|
2695
|
+
string(part.refusal, "refusal");
|
|
2547
2696
|
else if (part.type === "input_image" && role === "user") {
|
|
2548
2697
|
if (protocol !== "chat_completions")
|
|
2549
2698
|
fail("unsupported_content", "Anthropic image conversion requires provider-specific URL/data support");
|
|
@@ -2551,11 +2700,14 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
2551
2700
|
if (part.detail !== undefined && !["auto", "low", "high"].includes(String(part.detail)))
|
|
2552
2701
|
fail("unsupported_content", "Unknown image detail");
|
|
2553
2702
|
} else
|
|
2554
|
-
fail("unsupported_content",
|
|
2703
|
+
fail("unsupported_content", "Unsupported message content type");
|
|
2555
2704
|
if (part.annotations !== undefined && list(part.annotations, "annotations").length)
|
|
2556
2705
|
fail("unsupported_content", "Output annotations cannot be represented in input history");
|
|
2557
2706
|
}
|
|
2558
|
-
const normalized = contentNormalizer.normalizeResponsesMessageContent(parts)
|
|
2707
|
+
const normalized = contentNormalizer.normalizeResponsesMessageContent(parts.map((raw) => {
|
|
2708
|
+
const part = raw;
|
|
2709
|
+
return part.type === "refusal" ? { type: "output_text", text: part.refusal } : part;
|
|
2710
|
+
}));
|
|
2559
2711
|
for (let i = 0;i < parts.length; i++) {
|
|
2560
2712
|
const part = parts[i];
|
|
2561
2713
|
if (part.type === "input_image" && part.detail !== undefined)
|
|
@@ -2564,6 +2716,8 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
2564
2716
|
return normalized;
|
|
2565
2717
|
};
|
|
2566
2718
|
const chat = [];
|
|
2719
|
+
const messageParams = new WeakMap;
|
|
2720
|
+
let messageParam = "instructions";
|
|
2567
2721
|
const callIds = new Set;
|
|
2568
2722
|
const answered = new Set;
|
|
2569
2723
|
let pendingReasoning = "";
|
|
@@ -2571,73 +2725,77 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
2571
2725
|
if (chat.length >= budget.maxItems)
|
|
2572
2726
|
fail("resource_limit", "Too many input messages");
|
|
2573
2727
|
chat.push(message);
|
|
2728
|
+
messageParams.set(message, messageParam);
|
|
2574
2729
|
};
|
|
2575
2730
|
if (body.instructions !== undefined)
|
|
2576
2731
|
addMessage({ role: "system", content: string(body.instructions, "instructions") });
|
|
2577
|
-
const
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
if (
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
if (
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2732
|
+
for (const [inputIndex, rawItem] of input.entries())
|
|
2733
|
+
atParam(`input[${inputIndex}]`, () => {
|
|
2734
|
+
messageParam = `input[${inputIndex}]`;
|
|
2735
|
+
const item = record(rawItem, "input item");
|
|
2736
|
+
if (item.type === "additional_tools")
|
|
2737
|
+
return;
|
|
2738
|
+
if (item.encrypted_content != null || item.type === "compaction")
|
|
2739
|
+
fail("unsupported_content", "Encrypted reasoning and compaction cannot be restored");
|
|
2740
|
+
if (item.type === "item_reference")
|
|
2741
|
+
fail("unresolved_reference", "Input item references must be resolved before conversion");
|
|
2742
|
+
if (item.type === "reasoning") {
|
|
2743
|
+
if (protocol !== "chat_completions" || !(capabilities.reasoningHistory || capabilities.reasoningEffort))
|
|
2744
|
+
fail("unsupported_reasoning", "Reasoning history requires an explicit compatible reasoning capability");
|
|
2745
|
+
if (item.content != null)
|
|
2746
|
+
fail("unsupported_reasoning", "Reasoning content requires an explicit history mapping", `${messageParam}.content`);
|
|
2747
|
+
const summary = list(item.summary, "reasoning summary");
|
|
2748
|
+
pendingReasoning += summary.map((part) => {
|
|
2749
|
+
const p = record(part, "reasoning summary");
|
|
2750
|
+
if (p.type !== "summary_text")
|
|
2751
|
+
fail("unsupported_reasoning", "Unknown reasoning summary");
|
|
2752
|
+
return string(p.text, "summary text");
|
|
2753
|
+
}).join("");
|
|
2754
|
+
return;
|
|
2755
|
+
}
|
|
2756
|
+
if (item.type === "function_call" || item.type === "custom_tool_call") {
|
|
2757
|
+
const custom = item.type === "custom_tool_call";
|
|
2758
|
+
const id = string(item.call_id, "call_id", true);
|
|
2759
|
+
if (callIds.has(id))
|
|
2760
|
+
fail("invalid_tool_history", "Duplicate tool call identifier");
|
|
2761
|
+
callIds.add(id);
|
|
2762
|
+
const args = custom ? JSON.stringify({ input: string(item.input, "custom input") }) : string(item.arguments, "arguments");
|
|
2763
|
+
bounded(args, budget.maxArgumentBytes, "arguments");
|
|
2764
|
+
argumentObject(args);
|
|
2765
|
+
const call = { id, type: "function", function: { name: resolve(item, custom), arguments: args } };
|
|
2766
|
+
const previous = chat.at(-1);
|
|
2767
|
+
if (previous?.role === "assistant") {
|
|
2768
|
+
previous.tool_calls = [...previous.tool_calls ?? [], call];
|
|
2769
|
+
if (pendingReasoning)
|
|
2770
|
+
previous.reasoning_content = pendingReasoning;
|
|
2771
|
+
} else
|
|
2772
|
+
addMessage({ role: "assistant", content: null, tool_calls: [call], ...pendingReasoning ? { reasoning_content: pendingReasoning } : {} });
|
|
2773
|
+
pendingReasoning = "";
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
if (item.type === "function_call_output" || item.type === "custom_tool_call_output") {
|
|
2777
|
+
const id = string(item.call_id, "call_id", true);
|
|
2778
|
+
if (!callIds.has(id) || answered.has(id))
|
|
2779
|
+
fail("invalid_tool_history", "Unmatched or duplicate tool result");
|
|
2780
|
+
answered.add(id);
|
|
2781
|
+
const output = typeof item.output === "string" ? item.output : normalizeContent(item.output, "tool");
|
|
2782
|
+
addMessage({ role: "tool", tool_call_id: id, content: output, ...item.is_error === true ? { is_error: true } : {} });
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
if (item.type !== undefined && item.type !== "message")
|
|
2786
|
+
fail("unsupported_content", "Unsupported input item type");
|
|
2787
|
+
const role = string(item.role, "message role", true);
|
|
2788
|
+
if (!["user", "assistant", "system", "developer"].includes(role))
|
|
2789
|
+
fail("unsupported_content", "Unknown message role");
|
|
2790
|
+
const message = { role, content: normalizeContent(item.content, role) };
|
|
2791
|
+
if (pendingReasoning) {
|
|
2792
|
+
if (role !== "assistant")
|
|
2793
|
+
fail("unsupported_reasoning", "Reasoning must precede an assistant item");
|
|
2794
|
+
message.reasoning_content = pendingReasoning;
|
|
2795
|
+
pendingReasoning = "";
|
|
2796
|
+
}
|
|
2797
|
+
addMessage(message);
|
|
2798
|
+
});
|
|
2641
2799
|
if (pendingReasoning)
|
|
2642
2800
|
fail("unsupported_reasoning", "Orphan reasoning history cannot be represented");
|
|
2643
2801
|
const result = { model, messages: chat };
|
|
@@ -2670,41 +2828,35 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
2670
2828
|
result.tool_choice = protocol === "chat_completions" ? { type: "function", function: { name: wire } } : { type: "tool", name: wire };
|
|
2671
2829
|
}
|
|
2672
2830
|
}
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2831
|
+
applyResponsesPreferences(body, result, protocol, capabilities, maxTokens, diagnostics);
|
|
2832
|
+
if (body.text !== undefined)
|
|
2833
|
+
atParam("text.format", () => {
|
|
2834
|
+
const text = record(body.text, "text");
|
|
2835
|
+
if (text.format !== undefined) {
|
|
2836
|
+
const format = record(text.format, "text format");
|
|
2837
|
+
const allowed = format.type === "json_schema" ? ["type", "name", "schema", "strict", "description"] : ["type"];
|
|
2838
|
+
for (const key of Object.keys(format))
|
|
2839
|
+
if (!allowed.includes(key))
|
|
2840
|
+
fail("unsupported_request", "Unsupported output format field", `text.format.${key}`);
|
|
2841
|
+
if (format.type === "json_schema") {
|
|
2842
|
+
atParam("text.format.name", () => string(format.name, "output schema name", true));
|
|
2843
|
+
atParam("text.format.schema", () => record(format.schema, "output schema"));
|
|
2844
|
+
if (format.strict !== undefined && typeof format.strict !== "boolean")
|
|
2845
|
+
fail("invalid_payload", "Output schema strict must be boolean", "text.format.strict");
|
|
2846
|
+
if (format.description !== undefined)
|
|
2847
|
+
atParam("text.format.description", () => string(format.description, "output schema description"));
|
|
2848
|
+
}
|
|
2849
|
+
if (protocol !== "chat_completions" && format.type !== "text")
|
|
2850
|
+
fail("unsupported_request", "Anthropic structured output requires a separate provider capability");
|
|
2851
|
+
if (format.type === "json_schema")
|
|
2852
|
+
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 } : {} } };
|
|
2853
|
+
else if (format.type === "json_object" || format.type === "text") {
|
|
2854
|
+
if (protocol === "chat_completions")
|
|
2855
|
+
result.response_format = format;
|
|
2856
|
+
} else
|
|
2857
|
+
fail("unsupported_request", "Unknown text format");
|
|
2688
2858
|
}
|
|
2689
|
-
}
|
|
2690
|
-
}
|
|
2691
|
-
if (body.text !== undefined) {
|
|
2692
|
-
const text = record(body.text, "text");
|
|
2693
|
-
if (text.verbosity !== undefined)
|
|
2694
|
-
fail("unsupported_request", "Text verbosity cannot be represented");
|
|
2695
|
-
if (text.format !== undefined) {
|
|
2696
|
-
const format = record(text.format, "text format");
|
|
2697
|
-
if (protocol !== "chat_completions" && format.type !== "text")
|
|
2698
|
-
fail("unsupported_request", "Anthropic structured output requires a separate provider capability");
|
|
2699
|
-
if (format.type === "json_schema")
|
|
2700
|
-
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 } : {} } };
|
|
2701
|
-
else if (format.type === "json_object" || format.type === "text") {
|
|
2702
|
-
if (protocol === "chat_completions")
|
|
2703
|
-
result.response_format = format;
|
|
2704
|
-
} else
|
|
2705
|
-
fail("unsupported_request", "Unknown text format");
|
|
2706
|
-
}
|
|
2707
|
-
}
|
|
2859
|
+
});
|
|
2708
2860
|
if (protocol === "anthropic_messages") {
|
|
2709
2861
|
const system = [];
|
|
2710
2862
|
const messages = [];
|
|
@@ -2718,7 +2870,7 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
2718
2870
|
for (const msg of chat) {
|
|
2719
2871
|
if (msg.role === "system" || msg.role === "developer") {
|
|
2720
2872
|
if (messages.length)
|
|
2721
|
-
fail("unsupported_content", "Mid-conversation system instructions cannot be relocated safely");
|
|
2873
|
+
fail("unsupported_content", "Mid-conversation system instructions cannot be relocated safely", `${messageParams.get(msg)}.role`);
|
|
2722
2874
|
system.push(typeof msg.content === "string" ? msg.content : msg.content.map((p) => string(p.text, "system text")).join(""));
|
|
2723
2875
|
continue;
|
|
2724
2876
|
}
|
|
@@ -2744,7 +2896,7 @@ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
|
|
|
2744
2896
|
delete result.parallel_tool_calls;
|
|
2745
2897
|
}
|
|
2746
2898
|
}
|
|
2747
|
-
return { body: result, toolNames };
|
|
2899
|
+
return { body: result, toolNames, canonicalInput, diagnostics };
|
|
2748
2900
|
}
|
|
2749
2901
|
// packages/llms/src/responses-codec/events.ts
|
|
2750
2902
|
class ResponsesEventEncoder {
|
|
@@ -3157,6 +3309,11 @@ function encodeResponsesResult(raw, protocol, model, toolNames = new Map, config
|
|
|
3157
3309
|
fail("missing_terminal", "JSON response has no terminal result");
|
|
3158
3310
|
return record(end.response, "Responses result");
|
|
3159
3311
|
}
|
|
3312
|
+
// plugins/codex-router/server/history.ts
|
|
3313
|
+
function serializeHistory(value) {
|
|
3314
|
+
return JSON.stringify(value).replace(/[\uD800-\uDFFF]/g, (unit) => "\\u" + unit.charCodeAt(0).toString(16).padStart(4, "0"));
|
|
3315
|
+
}
|
|
3316
|
+
|
|
3160
3317
|
// packages/core/src/plugin-logger.ts
|
|
3161
3318
|
var sinkKey = Symbol.for("@jeffusion/bungee/plugin-log-sink");
|
|
3162
3319
|
function createLogger(bindings = {}) {
|
|
@@ -3210,16 +3367,26 @@ function protocolSSEOutput(payloads, protocol, source) {
|
|
|
3210
3367
|
// packages/core/src/plugin-extensions.ts
|
|
3211
3368
|
var ANONYMOUS_PRINCIPAL = Object.freeze({ domain: "anonymous", keyId: "", credentialVersion: 0 });
|
|
3212
3369
|
// packages/core/src/data-admission/errors.ts
|
|
3370
|
+
function safeDetails(details) {
|
|
3371
|
+
if (!details)
|
|
3372
|
+
return;
|
|
3373
|
+
const message = typeof details.message === "string" && details.message.length <= 512 && !/[\x00-\x1f\x7f]/.test(details.message) ? details.message : undefined;
|
|
3374
|
+
const param = typeof details.param === "string" && details.param.length <= 256 && /^[a-zA-Z_$][\w.$\[\]-]*$/.test(details.param) ? details.param : undefined;
|
|
3375
|
+
return message === undefined && param === undefined ? undefined : Object.freeze({ message, param });
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3213
3378
|
class DataAdmissionError extends Error {
|
|
3214
3379
|
status;
|
|
3215
3380
|
code;
|
|
3216
3381
|
retryAfter;
|
|
3217
3382
|
name = "DataAdmissionError";
|
|
3218
|
-
|
|
3383
|
+
details;
|
|
3384
|
+
constructor(status, code, retryAfter, details) {
|
|
3219
3385
|
super(code);
|
|
3220
3386
|
this.status = status;
|
|
3221
3387
|
this.code = code;
|
|
3222
3388
|
this.retryAfter = retryAfter;
|
|
3389
|
+
this.details = safeDetails(details);
|
|
3223
3390
|
}
|
|
3224
3391
|
}
|
|
3225
3392
|
// packages/core/src/plugin-durable-state.ts
|
|
@@ -3751,7 +3918,7 @@ function historyClient(rpc) {
|
|
|
3751
3918
|
throw new Error("codex_router_history_limit");
|
|
3752
3919
|
},
|
|
3753
3920
|
async put({ scope, id, value }, options) {
|
|
3754
|
-
const text =
|
|
3921
|
+
const text = serializeHistory(value), total = Math.ceil(text.length / 8192);
|
|
3755
3922
|
if (total > 1024 || Buffer.byteLength(text) > 8 * 1024 * 1024)
|
|
3756
3923
|
throw new Error("codex_router_history_limit");
|
|
3757
3924
|
for (let index = 0;index < total; index++) {
|
|
@@ -3766,12 +3933,13 @@ function historyClient(rpc) {
|
|
|
3766
3933
|
function protocolAdapter(input) {
|
|
3767
3934
|
const encoder = input.protocol ? new ResponsesEventEncoder(input.protocol, input.model, input.toolNames) : undefined;
|
|
3768
3935
|
let nativeTerminal;
|
|
3936
|
+
let sourceContext;
|
|
3769
3937
|
let flushed = false;
|
|
3770
3938
|
const terminal = async (events) => {
|
|
3771
3939
|
for (const event of events)
|
|
3772
3940
|
if (event.response && ["response.completed", "response.incomplete"].includes(event.type)) {
|
|
3773
3941
|
input.signal.throwIfAborted();
|
|
3774
|
-
await input.save(event.response);
|
|
3942
|
+
await input.save(event.response, sourceContext);
|
|
3775
3943
|
}
|
|
3776
3944
|
return events;
|
|
3777
3945
|
};
|
|
@@ -3787,7 +3955,7 @@ function protocolAdapter(input) {
|
|
|
3787
3955
|
const body = input.protocol ? encodeResponsesResult(raw, input.protocol, input.model, input.toolNames) : { ...raw, model: input.model };
|
|
3788
3956
|
if (["completed", "incomplete"].includes(body.status)) {
|
|
3789
3957
|
input.signal.throwIfAborted();
|
|
3790
|
-
await input.save(body);
|
|
3958
|
+
await input.save(body, context);
|
|
3791
3959
|
}
|
|
3792
3960
|
const headers = new Headers(response.headers);
|
|
3793
3961
|
headers.delete("content-length");
|
|
@@ -3795,7 +3963,8 @@ function protocolAdapter(input) {
|
|
|
3795
3963
|
headers.set("content-type", "application/json");
|
|
3796
3964
|
return new Response(JSON.stringify(body), { status: response.status, headers });
|
|
3797
3965
|
});
|
|
3798
|
-
hooks.onStreamChunk.tapPromise("codex-router.protocol", async (envelope) => {
|
|
3966
|
+
hooks.onStreamChunk.tapPromise("codex-router.protocol", async (envelope, context) => {
|
|
3967
|
+
sourceContext = context;
|
|
3799
3968
|
if (!encoder) {
|
|
3800
3969
|
const event = envelope.json;
|
|
3801
3970
|
if (event?.response && ["response.completed", "response.incomplete"].includes(event.type))
|
|
@@ -3817,7 +3986,7 @@ function protocolAdapter(input) {
|
|
|
3817
3986
|
return [...chunks, ...protocolSSEOutput(await terminal(encoder.finish()), "responses")];
|
|
3818
3987
|
if (nativeTerminal) {
|
|
3819
3988
|
input.signal.throwIfAborted();
|
|
3820
|
-
await input.save(nativeTerminal);
|
|
3989
|
+
await input.save(nativeTerminal, sourceContext);
|
|
3821
3990
|
}
|
|
3822
3991
|
return chunks;
|
|
3823
3992
|
});
|
|
@@ -3862,7 +4031,6 @@ var manifest_default = {
|
|
|
3862
4031
|
"dynamicRuntimeLoad",
|
|
3863
4032
|
"controlPlane"
|
|
3864
4033
|
],
|
|
3865
|
-
uiExtensionMode: "none",
|
|
3866
4034
|
engines: {
|
|
3867
4035
|
bungee: "^4.2.0 || ^5.0.0"
|
|
3868
4036
|
},
|
|
@@ -3935,10 +4103,41 @@ var manifest_default = {
|
|
|
3935
4103
|
type: "object",
|
|
3936
4104
|
label: "Model",
|
|
3937
4105
|
properties: [
|
|
4106
|
+
{
|
|
4107
|
+
name: "source",
|
|
4108
|
+
type: "string",
|
|
4109
|
+
label: "editor.source",
|
|
4110
|
+
required: false,
|
|
4111
|
+
validation: {
|
|
4112
|
+
trimmed: true,
|
|
4113
|
+
max: 512
|
|
4114
|
+
}
|
|
4115
|
+
},
|
|
4116
|
+
{
|
|
4117
|
+
name: "sourceProvider",
|
|
4118
|
+
type: "string",
|
|
4119
|
+
label: "editor.sourceProvider",
|
|
4120
|
+
required: false,
|
|
4121
|
+
validation: {
|
|
4122
|
+
trimmed: true,
|
|
4123
|
+
max: 512
|
|
4124
|
+
}
|
|
4125
|
+
},
|
|
4126
|
+
{
|
|
4127
|
+
name: "sourceProtocol",
|
|
4128
|
+
type: "select",
|
|
4129
|
+
label: "editor.sourceProtocol",
|
|
4130
|
+
options: [
|
|
4131
|
+
{
|
|
4132
|
+
value: "responses",
|
|
4133
|
+
label: "Responses"
|
|
4134
|
+
}
|
|
4135
|
+
]
|
|
4136
|
+
},
|
|
3938
4137
|
{
|
|
3939
4138
|
name: "provider",
|
|
3940
4139
|
type: "string",
|
|
3941
|
-
label: "provider",
|
|
4140
|
+
label: "editor.provider",
|
|
3942
4141
|
required: true,
|
|
3943
4142
|
validation: {
|
|
3944
4143
|
trimmed: true,
|
|
@@ -3948,7 +4147,7 @@ var manifest_default = {
|
|
|
3948
4147
|
{
|
|
3949
4148
|
name: "model",
|
|
3950
4149
|
type: "string",
|
|
3951
|
-
label: "model",
|
|
4150
|
+
label: "editor.model",
|
|
3952
4151
|
required: true,
|
|
3953
4152
|
validation: {
|
|
3954
4153
|
trimmed: true,
|
|
@@ -3968,20 +4167,19 @@ var manifest_default = {
|
|
|
3968
4167
|
{
|
|
3969
4168
|
name: "target",
|
|
3970
4169
|
type: "gateway_target",
|
|
3971
|
-
label: "
|
|
4170
|
+
label: "editor.target",
|
|
3972
4171
|
required: true
|
|
3973
4172
|
},
|
|
3974
4173
|
{
|
|
3975
4174
|
name: "capabilityOverrides",
|
|
3976
4175
|
type: "json",
|
|
3977
|
-
label: "
|
|
4176
|
+
label: "editor.advanced"
|
|
3978
4177
|
}
|
|
3979
4178
|
]
|
|
3980
4179
|
}
|
|
3981
4180
|
}
|
|
3982
4181
|
],
|
|
3983
4182
|
contributes: {
|
|
3984
|
-
settings: "/catalog",
|
|
3985
4183
|
api: [
|
|
3986
4184
|
{
|
|
3987
4185
|
path: "/catalog",
|
|
@@ -3996,19 +4194,109 @@ var manifest_default = {
|
|
|
3996
4194
|
},
|
|
3997
4195
|
translations: {
|
|
3998
4196
|
"zh-CN": {
|
|
4197
|
+
"editor.loaded": "\u5DF2\u52A0\u8F7D",
|
|
4198
|
+
"editor.complete": "\u5DF2\u5168\u90E8\u52A0\u8F7D",
|
|
4199
|
+
"editor.loadMore": "\u52A0\u8F7D\u66F4\u591A",
|
|
3999
4200
|
"metadata.name": "Codex \u6A21\u578B\u8DEF\u7531",
|
|
4000
4201
|
"plugin.description": "\u4ECE\u5171\u4EAB\u76EE\u5F55\u9009\u62E9\u6A21\u578B\u5E76\u7ED1\u5B9A\u5DF2\u6709\u8DEF\u7531\u6216\u670D\u52A1",
|
|
4001
|
-
"models.label": "\u6A21\u578B\u7ED1\u5B9A"
|
|
4202
|
+
"models.label": "\u6A21\u578B\u7ED1\u5B9A",
|
|
4203
|
+
"editor.description": "\u6309\u539F\u59CB\u6A21\u578B\u5339\u914D\u8BF7\u6C42\uFF0C\u5C06\u76EE\u6807\u6A21\u578B\u8F6C\u53D1\u5230\u6307\u5B9A\u8DEF\u7531\u6216\u670D\u52A1\u3002",
|
|
4204
|
+
"editor.binding": "\u7ED1\u5B9A",
|
|
4205
|
+
"editor.match": "\u5339\u914D\u8BF7\u6C42",
|
|
4206
|
+
"editor.sourceProvider": "\u539F\u59CB\u63D0\u4F9B\u5546",
|
|
4207
|
+
"editor.manual": "\u624B\u52A8\u8F93\u5165",
|
|
4208
|
+
"editor.fromCatalog": "\u4ECE\u76EE\u5F55\u9009\u62E9",
|
|
4209
|
+
"editor.source": "\u539F\u59CB\u6A21\u578B",
|
|
4210
|
+
"editor.sourceHint": "\u7CBE\u786E\u5339\u914D\u5BA2\u6237\u7AEF\u8BF7\u6C42\u4E2D\u7684\u6A21\u578B\u6807\u8BC6\uFF1B\u4F9B\u5E94\u5546\u4EC5\u7528\u4E8E\u76EE\u5F55\u7B5B\u9009\u3002",
|
|
4211
|
+
"editor.sourcePlaceholder": "\u4F8B\u5982 gpt-5.4",
|
|
4212
|
+
"editor.provider": "\u76EE\u6807\u63D0\u4F9B\u5546",
|
|
4213
|
+
"editor.model": "\u76EE\u6807\u6A21\u578B",
|
|
4214
|
+
"editor.target": "\u8F6C\u53D1\u8DEF\u7531 / \u670D\u52A1",
|
|
4215
|
+
"editor.targetPlaceholder": "\u9009\u62E9\u5DF2\u6709\u8DEF\u7531\u6216\u670D\u52A1",
|
|
4216
|
+
"editor.search": "\u641C\u7D22\u6A21\u578B",
|
|
4217
|
+
"editor.empty": "\u6CA1\u6709\u5339\u914D\u7684\u6A21\u578B",
|
|
4218
|
+
"editor.loading": "\u6B63\u5728\u52A0\u8F7D\u6A21\u578B\u76EE\u5F55",
|
|
4219
|
+
"editor.error": "\u76EE\u5F55\u4E0D\u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5 models.dev \u6216\u91CD\u8BD5\u3002",
|
|
4220
|
+
"editor.targetsError": "\u65E0\u6CD5\u8BFB\u53D6\u8F6C\u53D1\u76EE\u6807\uFF0C\u8BF7\u91CD\u8BD5\u3002",
|
|
4221
|
+
"editor.retry": "\u91CD\u8BD5",
|
|
4222
|
+
"editor.add": "\u6DFB\u52A0\u6A21\u578B\u7ED1\u5B9A",
|
|
4223
|
+
"editor.remove": "\u79FB\u9664",
|
|
4224
|
+
"editor.noBindings": "\u5C1A\u672A\u914D\u7F6E\u6A21\u578B\u7ED1\u5B9A\u3002",
|
|
4225
|
+
"editor.noCatalog": "\u76EE\u5F55\u4E3A\u7A7A\uFF0C\u8BF7\u5148\u542F\u7528 models.dev \u5E76\u5237\u65B0\u76EE\u5F55\u3002",
|
|
4226
|
+
"editor.advanced": "\u80FD\u529B\u9650\u5236\uFF08\u53EF\u9009\uFF09",
|
|
4227
|
+
"editor.context": "\u4E0A\u4E0B\u6587\u957F\u5EA6\u4E0A\u9650",
|
|
4228
|
+
"editor.contextHint": "\u7559\u7A7A\u4F7F\u7528\u76EE\u6807\u6A21\u578B\u7684\u4E0A\u4E0B\u6587\u957F\u5EA6\u3002",
|
|
4229
|
+
"editor.toolsOff": "\u7981\u7528\u5DE5\u5177\u8C03\u7528",
|
|
4230
|
+
"editor.imagesOff": "\u7981\u7528\u56FE\u7247\u8F93\u5165",
|
|
4231
|
+
"editor.reasoningOff": "\u7981\u7528\u63A8\u7406\u80FD\u529B",
|
|
4232
|
+
"editor.reasoningEffort": "\u4F20\u9012\u63A8\u7406\u5F3A\u5EA6\uFF08\u76EE\u6807\u9700\u652F\u6301\uFF09",
|
|
4233
|
+
"editor.increase": "\u589E\u52A0\u4E0A\u4E0B\u6587\u4E0A\u9650",
|
|
4234
|
+
"editor.decrease": "\u51CF\u5C11\u4E0A\u4E0B\u6587\u4E0A\u9650",
|
|
4235
|
+
"editor.contextInvalid": "\u8BF7\u8F93\u5165\u6B63\u6574\u6570\u3002",
|
|
4236
|
+
"editor.sourceProtocol": "\u6E90\u6A21\u578B\u534F\u8BAE",
|
|
4237
|
+
"editor.targetProtocol": "\u76EE\u6807\u6A21\u578B\u534F\u8BAE",
|
|
4238
|
+
"editor.targetProtocolPlaceholder": "\u9009\u62E9\u76EE\u6807\u63A5\u6536\u534F\u8BAE",
|
|
4239
|
+
"editor.route": "\u8DEF\u7531",
|
|
4240
|
+
"editor.service": "\u670D\u52A1",
|
|
4241
|
+
"editor.targetUnavailable": "\u76EE\u6807\u4E0D\u53EF\u7528",
|
|
4242
|
+
"editor.noOptions": "\u6CA1\u6709\u5339\u914D\u7684\u9009\u9879"
|
|
4002
4243
|
},
|
|
4003
4244
|
en: {
|
|
4245
|
+
"editor.loaded": "Loaded",
|
|
4246
|
+
"editor.complete": "All loaded",
|
|
4247
|
+
"editor.loadMore": "Load more",
|
|
4004
4248
|
"metadata.name": "Codex Router",
|
|
4005
4249
|
"plugin.description": "Bind catalog models to existing routes or services",
|
|
4006
|
-
"models.label": "Model bindings"
|
|
4250
|
+
"models.label": "Model bindings",
|
|
4251
|
+
"editor.description": "Match the original model and forward the target model through an existing route or service.",
|
|
4252
|
+
"editor.binding": "Binding",
|
|
4253
|
+
"editor.match": "Match request",
|
|
4254
|
+
"editor.sourceProvider": "Original provider",
|
|
4255
|
+
"editor.manual": "Enter manually",
|
|
4256
|
+
"editor.fromCatalog": "Choose from catalog",
|
|
4257
|
+
"editor.source": "Original model",
|
|
4258
|
+
"editor.sourceHint": "Match the exact model ID in client requests; the provider only filters the catalog.",
|
|
4259
|
+
"editor.sourcePlaceholder": "For example, gpt-5.4",
|
|
4260
|
+
"editor.provider": "Target provider",
|
|
4261
|
+
"editor.model": "Target model",
|
|
4262
|
+
"editor.target": "Forwarding route / service",
|
|
4263
|
+
"editor.targetPlaceholder": "Select an existing route or service",
|
|
4264
|
+
"editor.search": "Search models",
|
|
4265
|
+
"editor.empty": "No matching models",
|
|
4266
|
+
"editor.loading": "Loading model catalog",
|
|
4267
|
+
"editor.error": "Catalog unavailable. Check models.dev or retry.",
|
|
4268
|
+
"editor.targetsError": "Unable to load forwarding targets. Please retry.",
|
|
4269
|
+
"editor.retry": "Retry",
|
|
4270
|
+
"editor.add": "Add model binding",
|
|
4271
|
+
"editor.remove": "Remove",
|
|
4272
|
+
"editor.noBindings": "No model bindings configured.",
|
|
4273
|
+
"editor.noCatalog": "The catalog is empty. Enable models.dev and refresh it first.",
|
|
4274
|
+
"editor.advanced": "Capability limits (optional)",
|
|
4275
|
+
"editor.context": "Context length limit",
|
|
4276
|
+
"editor.contextHint": "Leave empty to use the target model context length.",
|
|
4277
|
+
"editor.toolsOff": "Disable tool calls",
|
|
4278
|
+
"editor.imagesOff": "Disable image input",
|
|
4279
|
+
"editor.reasoningOff": "Disable reasoning",
|
|
4280
|
+
"editor.reasoningEffort": "Forward reasoning effort (requires target support)",
|
|
4281
|
+
"editor.increase": "Increase context limit",
|
|
4282
|
+
"editor.decrease": "Decrease context limit",
|
|
4283
|
+
"editor.contextInvalid": "Enter a positive whole number.",
|
|
4284
|
+
"editor.sourceProtocol": "Source protocol",
|
|
4285
|
+
"editor.targetProtocol": "Target protocol",
|
|
4286
|
+
"editor.targetProtocolPlaceholder": "Select the target receiving protocol",
|
|
4287
|
+
"editor.route": "Route",
|
|
4288
|
+
"editor.service": "Service",
|
|
4289
|
+
"editor.targetUnavailable": "Target unavailable",
|
|
4290
|
+
"editor.noOptions": "No matching options"
|
|
4007
4291
|
}
|
|
4008
|
-
}
|
|
4292
|
+
},
|
|
4293
|
+
uiExtensionMode: "none"
|
|
4009
4294
|
};
|
|
4010
4295
|
|
|
4011
4296
|
// plugins/codex-router/server/config.ts
|
|
4297
|
+
function bindingSource(binding) {
|
|
4298
|
+
return binding.source ?? binding.alias ?? binding.model;
|
|
4299
|
+
}
|
|
4012
4300
|
function parseBindings(value) {
|
|
4013
4301
|
if (value === undefined)
|
|
4014
4302
|
return [];
|
|
@@ -4016,16 +4304,22 @@ function parseBindings(value) {
|
|
|
4016
4304
|
throw new Error("codex_router_invalid_bindings");
|
|
4017
4305
|
const ids = new Set;
|
|
4018
4306
|
return Object.freeze(value.map((entry) => {
|
|
4019
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry) || Object.keys(entry).some((key) => !["provider", "model", "alias", "target", "capabilityOverrides"].includes(key)))
|
|
4307
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry) || Object.keys(entry).some((key) => !["provider", "model", "source", "sourceProvider", "sourceProtocol", "alias", "target", "capabilityOverrides"].includes(key)) || entry.source !== undefined && entry.alias !== undefined || entry.sourceProvider !== undefined && entry.source === undefined)
|
|
4020
4308
|
throw new Error("codex_router_invalid_bindings");
|
|
4021
|
-
for (const text of [entry.provider, entry.model, ...entry.alias === undefined ? [] : [entry.alias]]) {
|
|
4309
|
+
for (const text of [entry.provider, entry.model, ...entry.source === undefined ? [] : [entry.source], ...entry.sourceProvider === undefined ? [] : [entry.sourceProvider], ...entry.alias === undefined ? [] : [entry.alias]]) {
|
|
4022
4310
|
if (typeof text !== "string" || !text || text !== text.trim() || text.length > 512 || /[\x00-\x1f\x7f]/.test(text))
|
|
4023
4311
|
throw new Error("codex_router_invalid_bindings");
|
|
4024
4312
|
}
|
|
4313
|
+
if (entry.sourceProtocol !== undefined && entry.sourceProtocol !== "responses")
|
|
4314
|
+
throw new Error("codex_router_source_protocol_unsupported");
|
|
4025
4315
|
const target = entry.target;
|
|
4026
|
-
if (!target || !["route", "service"].includes(target.type) || typeof target.id !== "string" || !target.id || Object.keys(target).some((key) => !["type", "id"].includes(key)))
|
|
4316
|
+
if (!target || !["route", "service"].includes(target.type) || typeof target.id !== "string" || !target.id || Object.keys(target).some((key) => !["type", "id", "protocol"].includes(key)))
|
|
4027
4317
|
throw new Error("codex_router_invalid_target");
|
|
4028
|
-
|
|
4318
|
+
if (target.protocol === undefined)
|
|
4319
|
+
throw new Error("codex_router_target_protocol_required");
|
|
4320
|
+
if (!["responses", "chat_completions", "anthropic_messages"].includes(target.protocol))
|
|
4321
|
+
throw new Error("codex_router_target_protocol_invalid");
|
|
4322
|
+
const publicId = bindingSource(entry);
|
|
4029
4323
|
if (ids.has(publicId))
|
|
4030
4324
|
throw new Error("codex_router_model_conflict");
|
|
4031
4325
|
ids.add(publicId);
|
|
@@ -4068,6 +4362,14 @@ var model_template_0_160_1_default = {
|
|
|
4068
4362
|
priority: 100,
|
|
4069
4363
|
default_reasoning_level: "none",
|
|
4070
4364
|
supported_reasoning_levels: [],
|
|
4365
|
+
experimental_supported_tools: [],
|
|
4366
|
+
upgrade: null,
|
|
4367
|
+
availability_nux: null,
|
|
4368
|
+
model_messages: null,
|
|
4369
|
+
include_skills_usage_instructions: false,
|
|
4370
|
+
include_plugin_usage_instructions: false,
|
|
4371
|
+
include_apps_usage_instructions: false,
|
|
4372
|
+
supports_reasoning_summary_parameter: false,
|
|
4071
4373
|
base_instructions: "",
|
|
4072
4374
|
supports_search_tool: false,
|
|
4073
4375
|
supports_parallel_tool_calls: false,
|
|
@@ -4090,22 +4392,34 @@ function mergeCatalog(native, bindings, catalog) {
|
|
|
4090
4392
|
const existing = rich ? body.models : body.data;
|
|
4091
4393
|
const ids = new Set(existing.map((entry) => rich ? entry.slug : entry.id));
|
|
4092
4394
|
const added = [];
|
|
4395
|
+
const replacements = new Map;
|
|
4396
|
+
const unavailable = new Set;
|
|
4397
|
+
const bound = new Set;
|
|
4093
4398
|
for (const binding of bindings) {
|
|
4094
|
-
const id = binding
|
|
4095
|
-
if (ids.has(id))
|
|
4399
|
+
const id = bindingSource(binding);
|
|
4400
|
+
if (bound.has(id) || ids.has(id) && binding.source === undefined)
|
|
4096
4401
|
throw new Error("codex_router_model_conflict");
|
|
4402
|
+
bound.add(id);
|
|
4403
|
+
const replace = ids.has(id);
|
|
4097
4404
|
ids.add(id);
|
|
4098
4405
|
const info = catalog.model(binding);
|
|
4099
|
-
if (!info || info.contextWindow === null || !info.inputModalities.includes("text"))
|
|
4406
|
+
if (!info || info.contextWindow === null || !info.inputModalities.includes("text")) {
|
|
4407
|
+
if (replace)
|
|
4408
|
+
unavailable.add(id);
|
|
4100
4409
|
continue;
|
|
4410
|
+
}
|
|
4101
4411
|
if (!rich) {
|
|
4102
|
-
|
|
4412
|
+
const model = { id, object: "model", owned_by: binding.provider };
|
|
4413
|
+
if (replace)
|
|
4414
|
+
replacements.set(id, model);
|
|
4415
|
+
else
|
|
4416
|
+
added.push(model);
|
|
4103
4417
|
continue;
|
|
4104
4418
|
}
|
|
4105
4419
|
const overrides = binding.capabilityOverrides;
|
|
4106
4420
|
const context = Math.min(info.contextWindow, overrides?.contextWindow ?? info.contextWindow);
|
|
4107
4421
|
const reasoning = info.reasoning && overrides?.reasoning !== false;
|
|
4108
|
-
|
|
4422
|
+
const model = {
|
|
4109
4423
|
...model_template_0_160_1_default,
|
|
4110
4424
|
slug: id,
|
|
4111
4425
|
display_name: info.name,
|
|
@@ -4117,9 +4431,13 @@ function mergeCatalog(native, bindings, catalog) {
|
|
|
4117
4431
|
supported_reasoning_levels: reasoning ? ["low", "medium", "high"].map((effort) => ({ effort, description: effort })) : [],
|
|
4118
4432
|
default_reasoning_level: reasoning ? "medium" : "none",
|
|
4119
4433
|
support_verbosity: false
|
|
4120
|
-
}
|
|
4434
|
+
};
|
|
4435
|
+
if (replace)
|
|
4436
|
+
replacements.set(id, model);
|
|
4437
|
+
else
|
|
4438
|
+
added.push(model);
|
|
4121
4439
|
}
|
|
4122
|
-
return { ...body, [rich ? "models" : "data"]: [...existing, ...added] };
|
|
4440
|
+
return { ...body, [rich ? "models" : "data"]: [...existing.filter((entry) => !unavailable.has(rich ? entry.slug : entry.id)).map((entry) => replacements.get(rich ? entry.slug : entry.id) ?? entry), ...added] };
|
|
4123
4441
|
}
|
|
4124
4442
|
function catalogEtag(body, version, clientVersion) {
|
|
4125
4443
|
return `"${createHash("sha256").update(JSON.stringify([body, version, clientVersion])).digest("hex")}"`;
|
|
@@ -4132,7 +4450,6 @@ var CodexRouterPlugin = definePlugin(class {
|
|
|
4132
4450
|
static configSchema = manifest_default.configSchema;
|
|
4133
4451
|
bindings;
|
|
4134
4452
|
history;
|
|
4135
|
-
targets = [];
|
|
4136
4453
|
catalog;
|
|
4137
4454
|
constructor(options) {
|
|
4138
4455
|
this.bindings = parseBindings(options?.models);
|
|
@@ -4140,7 +4457,6 @@ var CodexRouterPlugin = definePlugin(class {
|
|
|
4140
4457
|
async init(context) {
|
|
4141
4458
|
if (context.scope?.type !== "route")
|
|
4142
4459
|
throw new Error("codex_router_requires_route_scope");
|
|
4143
|
-
this.targets = context.dispatchTargets ?? [];
|
|
4144
4460
|
this.history = historyClient(context.services.rpc.consume("codex-router", historyRpc));
|
|
4145
4461
|
this.catalog = context.services.consume("models-dev", MODELS_DEV_CAPABILITIES_SERVICE_ID, MODELS_DEV_CAPABILITIES_CONTRACT_VERSION);
|
|
4146
4462
|
}
|
|
@@ -4148,20 +4464,23 @@ var CodexRouterPlugin = definePlugin(class {
|
|
|
4148
4464
|
return { request: context.method === "POST" && /\/responses$/.test(context.url.pathname) ? "json-write" : "none", response: context.method === "GET" && /\/models$/.test(context.url.pathname) ? ["json"] : [] };
|
|
4149
4465
|
}
|
|
4150
4466
|
register(hooks) {
|
|
4467
|
+
hooks.onWebSocketSessionMode.tap("codex-router.session", (url) => /\/responses$/.test(url.pathname) ? "responses" : undefined);
|
|
4151
4468
|
hooks.onDispatchRequest.tapPromise("codex-router.binding", async ({ context, targets, principal, servingRevision, signal }) => {
|
|
4152
4469
|
if (context.method !== "POST" || !/\/responses$/.test(context.originalUrl.pathname))
|
|
4153
4470
|
return;
|
|
4154
|
-
const binding = this.bindings.find((binding) => (binding
|
|
4471
|
+
const binding = this.bindings.find((binding) => bindingSource(binding) === context.body?.model);
|
|
4155
4472
|
if (!binding)
|
|
4156
4473
|
return;
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4474
|
+
if (!targets.some((target) => target.type === binding.target.type && target.id === binding.target.id))
|
|
4475
|
+
throw new DataAdmissionError(422, "codex_router_target_unavailable");
|
|
4476
|
+
const protocol = binding.target.protocol;
|
|
4160
4477
|
const capabilities = this.catalog.model(binding);
|
|
4161
|
-
if (!capabilities)
|
|
4478
|
+
if (!capabilities || capabilities.contextWindow === null || !capabilities.inputModalities.includes("text"))
|
|
4162
4479
|
throw new DataAdmissionError(503, "codex_router_model_unavailable");
|
|
4163
4480
|
const scope = principal?.domain !== "anonymous" && principal?.keyId ? createHash2("sha256").update(JSON.stringify([principal, context.routeId, servingRevision ?? null, this.bindings])).digest("hex") : null;
|
|
4164
4481
|
const input = structuredClone(context.body);
|
|
4482
|
+
let requiredUpstreamId;
|
|
4483
|
+
let opaqueRestored = false;
|
|
4165
4484
|
if (input.previous_response_id) {
|
|
4166
4485
|
if (!scope)
|
|
4167
4486
|
throw new DataAdmissionError(422, "codex_router_history_identity_required");
|
|
@@ -4171,46 +4490,60 @@ var CodexRouterPlugin = definePlugin(class {
|
|
|
4171
4490
|
const delta = typeof input.input === "string" ? [{ role: "user", content: input.input }] : input.input;
|
|
4172
4491
|
if (!Array.isArray(delta))
|
|
4173
4492
|
throw new DataAdmissionError(422, "codex_router_invalid_history");
|
|
4174
|
-
if (
|
|
4175
|
-
|
|
4493
|
+
if (prior.items.some((item) => item.encrypted_content || item.type === "compaction")) {
|
|
4494
|
+
if (protocol !== "responses" || JSON.stringify(prior.target) !== JSON.stringify(binding.target) || prior.origin?.provider !== binding.provider || prior.origin?.model !== binding.model || !prior.origin?.upstreamId)
|
|
4495
|
+
throw new DataAdmissionError(422, "codex_router_unrestorable_history_start_new_conversation");
|
|
4496
|
+
requiredUpstreamId = prior.origin.upstreamId;
|
|
4497
|
+
opaqueRestored = true;
|
|
4498
|
+
}
|
|
4176
4499
|
input.input = [...prior.items, ...delta];
|
|
4177
4500
|
delete input.previous_response_id;
|
|
4178
4501
|
}
|
|
4179
4502
|
if (input.conversation || input.response_id)
|
|
4180
4503
|
throw new DataAdmissionError(422, "codex_router_unsupported_history_reference");
|
|
4181
4504
|
input.model = binding.model;
|
|
4182
|
-
|
|
4505
|
+
let logicalInput = typeof input.input === "string" ? [{ role: "user", content: input.input }] : input.input;
|
|
4183
4506
|
if (!Array.isArray(logicalInput))
|
|
4184
4507
|
throw new DataAdmissionError(422, "codex_router_invalid_history");
|
|
4508
|
+
if (!opaqueRestored && logicalInput.some((item) => item?.encrypted_content || item?.type === "compaction"))
|
|
4509
|
+
throw new DataAdmissionError(422, "codex_router_unrestorable_history_start_new_conversation");
|
|
4510
|
+
const hasTools = input.tools?.length || logicalInput.some((item) => item?.type === "additional_tools" && item.tools?.length);
|
|
4511
|
+
if (hasTools && (!capabilities.toolCall || binding.capabilityOverrides?.tools === false))
|
|
4512
|
+
throw new DataAdmissionError(422, "codex_router_tools_unsupported");
|
|
4185
4513
|
let toolNames;
|
|
4186
|
-
|
|
4514
|
+
let diagnostics;
|
|
4515
|
+
if (protocol !== "responses") {
|
|
4187
4516
|
try {
|
|
4188
|
-
const decoded = decodeResponsesRequest(input,
|
|
4517
|
+
const decoded = decodeResponsesRequest(input, protocol, {
|
|
4518
|
+
omitOptionalWebSearch: true,
|
|
4189
4519
|
reasoningEffort: capabilities.reasoning && binding.capabilityOverrides?.reasoningEffort === true,
|
|
4190
|
-
|
|
4520
|
+
reasoningHistory: capabilities.reasoning && binding.capabilityOverrides?.reasoning !== false,
|
|
4521
|
+
anthropicThinkingBudget: undefined,
|
|
4191
4522
|
maxOutputTokens: capabilities.outputLimit ?? undefined
|
|
4192
4523
|
});
|
|
4193
4524
|
context.body = decoded.body;
|
|
4194
4525
|
toolNames = decoded.toolNames;
|
|
4526
|
+
logicalInput = decoded.canonicalInput;
|
|
4527
|
+
diagnostics = decoded.diagnostics;
|
|
4195
4528
|
} catch (error) {
|
|
4196
4529
|
if (error instanceof ResponsesCodecError)
|
|
4197
|
-
throw new DataAdmissionError(422, `codex_router_${error.code}
|
|
4530
|
+
throw new DataAdmissionError(422, `codex_router_${error.code}`, undefined, { message: error.message, param: error.param });
|
|
4198
4531
|
throw error;
|
|
4199
4532
|
}
|
|
4200
|
-
context.url.pathname = context.url.pathname.replace(/\/responses$/,
|
|
4533
|
+
context.url.pathname = context.url.pathname.replace(/\/responses$/, protocol === "chat_completions" ? "/chat/completions" : "/messages");
|
|
4201
4534
|
} else
|
|
4202
4535
|
context.body = input;
|
|
4203
4536
|
const adapter = protocolAdapter({
|
|
4204
|
-
protocol:
|
|
4205
|
-
model: binding
|
|
4537
|
+
protocol: protocol === "responses" ? undefined : protocol,
|
|
4538
|
+
model: bindingSource(binding),
|
|
4206
4539
|
toolNames,
|
|
4207
4540
|
signal,
|
|
4208
|
-
save: async (response) => {
|
|
4541
|
+
save: async (response, responseContext) => {
|
|
4209
4542
|
if (scope && typeof response.id === "string")
|
|
4210
|
-
await this.history.put({ scope, id: response.id, value: { target: binding.target, items: [...logicalInput, ...response.output] } }, { signal, operationId: crypto.randomUUID() });
|
|
4543
|
+
await this.history.put({ scope, id: response.id, value: { target: binding.target, origin: { provider: binding.provider, model: binding.model, upstreamId: responseContext?.upstreamId }, items: [...logicalInput, ...response.output] } }, { signal, operationId: crypto.randomUUID() });
|
|
4211
4544
|
}
|
|
4212
4545
|
});
|
|
4213
|
-
return { target: binding.target, adapter };
|
|
4546
|
+
return { target: binding.target, protocol, requiredUpstreamId, adapter, ...protocol !== "responses" ? { canonicalInput: logicalInput, diagnostics } : {} };
|
|
4214
4547
|
});
|
|
4215
4548
|
hooks.onBeforeRequest.tap("codex-router.catalog-validator", (context) => {
|
|
4216
4549
|
if (context.method === "GET" && /\/models$/.test(context.originalUrl.pathname)) {
|
|
@@ -4225,11 +4558,11 @@ var CodexRouterPlugin = definePlugin(class {
|
|
|
4225
4558
|
return response;
|
|
4226
4559
|
const native = await context.bodyHandle.json({ id: "codex-router.catalog", mandatory: true });
|
|
4227
4560
|
const bindings = this.bindings.map((binding) => {
|
|
4228
|
-
const protocol =
|
|
4561
|
+
const protocol = binding.target.protocol;
|
|
4229
4562
|
return { ...binding, capabilityOverrides: {
|
|
4230
4563
|
...binding.capabilityOverrides,
|
|
4231
4564
|
...protocol === "anthropic_messages" ? { images: false } : {},
|
|
4232
|
-
...protocol
|
|
4565
|
+
...protocol !== "responses" ? { reasoning: protocol === "chat_completions" ? binding.capabilityOverrides?.reasoningEffort === true : false } : {}
|
|
4233
4566
|
} };
|
|
4234
4567
|
});
|
|
4235
4568
|
const body = mergeCatalog(native, bindings, this.catalog);
|
|
@@ -4249,4 +4582,4 @@ export {
|
|
|
4249
4582
|
server_default as default
|
|
4250
4583
|
};
|
|
4251
4584
|
|
|
4252
|
-
//# debugId=
|
|
4585
|
+
//# debugId=3BEC03CB648CDF5064756E2164756E21
|