@jsonstudio/llms 0.6.802 → 0.6.938
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/bridge/routecodex-adapter.d.ts +74 -0
- package/dist/config-unified/enhanced-path-resolver.d.ts +5 -0
- package/dist/config-unified/unified-config.d.ts +26 -0
- package/dist/conversion/codec-registry.d.ts +10 -0
- package/dist/conversion/codecs/gemini-openai-codec.d.ts +16 -0
- package/dist/conversion/codecs/openai-openai-codec.d.ts +12 -0
- package/dist/conversion/codecs/responses-openai-codec.d.ts +12 -0
- package/dist/conversion/compat/profiles/chat-gemini.json +12 -0
- package/dist/conversion/config/config-manager.d.ts +212 -0
- package/dist/conversion/hub/config/types.d.ts +26 -0
- package/dist/conversion/hub/core/detour-registry.d.ts +9 -0
- package/dist/conversion/hub/core/hub-context.d.ts +21 -0
- package/dist/conversion/hub/core/index.d.ts +3 -0
- package/dist/conversion/hub/core/stage-driver.d.ts +30 -0
- package/dist/conversion/hub/format-adapters/anthropic-format-adapter.d.ts +16 -0
- package/dist/conversion/hub/format-adapters/chat-format-adapter.d.ts +17 -0
- package/dist/conversion/hub/format-adapters/gemini-format-adapter.d.ts +16 -0
- package/dist/conversion/hub/format-adapters/index.d.ts +21 -0
- package/dist/conversion/hub/hub-feature.d.ts +1 -0
- package/dist/conversion/hub/node-support.d.ts +19 -0
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +11 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +7 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +71 -14
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +4 -0
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +23 -1
- package/dist/conversion/hub/pipelines/inbound.d.ts +22 -0
- package/dist/conversion/hub/pipelines/outbound.d.ts +22 -0
- package/dist/conversion/hub/policy/policy-engine.d.ts +46 -0
- package/dist/conversion/hub/policy/policy-engine.js +176 -0
- package/dist/conversion/hub/policy/protocol-spec.d.ts +50 -0
- package/dist/conversion/hub/policy/protocol-spec.js +105 -0
- package/dist/conversion/hub/process/chat-process.d.ts +32 -0
- package/dist/conversion/hub/registry.d.ts +28 -0
- package/dist/conversion/hub/response/chat-response-utils.d.ts +6 -0
- package/dist/conversion/hub/response/provider-response.js +31 -0
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +7 -0
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +87 -1
- package/dist/conversion/hub/semantic-mappers/index.d.ts +4 -0
- package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +21 -0
- package/dist/conversion/hub/standardized-bridge.d.ts +12 -0
- package/dist/conversion/hub/types/chat-schema.d.ts +112 -0
- package/dist/conversion/hub/types/errors.d.ts +5 -0
- package/dist/conversion/hub/types/format-envelope.d.ts +7 -0
- package/dist/conversion/hub/types/index.d.ts +6 -0
- package/dist/conversion/hub/types/json.d.ts +9 -0
- package/dist/conversion/hub/types/node.d.ts +31 -0
- package/dist/conversion/responses/responses-openai-bridge.js +263 -10
- package/dist/conversion/schema-validator.d.ts +7 -0
- package/dist/conversion/shared/args-mapping.d.ts +18 -0
- package/dist/conversion/shared/chat-request-filters.d.ts +9 -0
- package/dist/conversion/shared/errors.d.ts +1 -1
- package/dist/conversion/shared/gemini-tool-utils.js +61 -0
- package/dist/conversion/shared/jsonish.d.ts +3 -0
- package/dist/conversion/shared/mcp-injection.d.ts +2 -0
- package/dist/conversion/shared/media.d.ts +1 -0
- package/dist/conversion/shared/openai-message-normalize.d.ts +1 -0
- package/dist/conversion/shared/payload-budget.d.ts +13 -0
- package/dist/conversion/shared/reasoning-mapping.d.ts +5 -0
- package/dist/conversion/shared/responses-request-adapter.d.ts +1 -28
- package/dist/conversion/shared/responses-request-adapter.js +1 -430
- package/dist/conversion/shared/snapshot-hooks.js +58 -3
- package/dist/conversion/shared/tool-governor.js +8 -2
- package/dist/conversion/shared/tool-harvester.d.ts +31 -0
- package/dist/conversion/shared/tool-mapping.js +10 -29
- package/dist/conversion/types.d.ts +33 -0
- package/dist/filters/builtin/add-fields-filter.d.ts +8 -0
- package/dist/filters/builtin/blacklist-filter.d.ts +8 -0
- package/dist/filters/builtin/whitelist-filter.d.ts +8 -0
- package/dist/filters/engine.d.ts +16 -0
- package/dist/filters/special/request-tool-choice-policy.d.ts +11 -0
- package/dist/filters/special/response-finish-invariants.d.ts +11 -0
- package/dist/filters/special/response-openai-to-responses-bridge.d.ts +13 -0
- package/dist/filters/special/response-tool-arguments-blacklist.d.ts +12 -0
- package/dist/filters/special/response-tool-arguments-schema-converge.d.ts +13 -0
- package/dist/filters/special/response-tool-arguments-stringify.d.ts +9 -0
- package/dist/filters/special/response-tool-arguments-whitelist.d.ts +11 -0
- package/dist/filters/special/tool-filter-hooks.d.ts +19 -0
- package/dist/filters/special/tool-post-constraints.d.ts +31 -0
- package/dist/filters/types.d.ts +68 -0
- package/dist/filters/utils/fieldmap-loader.d.ts +2 -0
- package/dist/filters/utils/snapshot-writer.d.ts +10 -0
- package/dist/guidance/index.d.ts +3 -0
- package/dist/guidance/index.js +78 -83
- package/dist/http/sse-response.d.ts +22 -0
- package/dist/router/virtual-router/bootstrap.d.ts +6 -0
- package/dist/router/virtual-router/bootstrap.js +49 -5
- package/dist/router/virtual-router/classifier.d.ts +10 -0
- package/dist/router/virtual-router/engine-selection.js +98 -11
- package/dist/router/virtual-router/engine.js +177 -31
- package/dist/router/virtual-router/error-center.d.ts +10 -0
- package/dist/router/virtual-router/features.d.ts +3 -0
- package/dist/router/virtual-router/routing-instructions.d.ts +23 -1
- package/dist/router/virtual-router/routing-instructions.js +120 -30
- package/dist/router/virtual-router/types.d.ts +11 -0
- package/dist/servertool/engine.js +189 -16
- package/dist/servertool/handlers/apply-patch-guard.js +269 -0
- package/dist/servertool/handlers/exec-command-guard.js +558 -0
- package/dist/servertool/handlers/followup-message-trimmer.d.ts +16 -0
- package/dist/servertool/handlers/followup-message-trimmer.js +198 -0
- package/dist/servertool/handlers/followup-request-builder.d.ts +17 -0
- package/dist/servertool/handlers/followup-request-builder.js +122 -0
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +252 -51
- package/dist/servertool/handlers/iflow-model-error-retry.js +12 -22
- package/dist/servertool/handlers/stop-message-auto.js +237 -75
- package/dist/servertool/handlers/vision.js +15 -27
- package/dist/servertool/handlers/web-search.js +17 -43
- package/dist/servertool/server-side-tools.d.ts +3 -0
- package/dist/servertool/server-side-tools.js +3 -0
- package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +2 -1
- package/dist/sse/json-to-sse/chat-json-to-sse-converter.d.ts +80 -0
- package/dist/sse/json-to-sse/event-generators/chat.d.ts +55 -0
- package/dist/sse/json-to-sse/event-generators/responses.d.ts +99 -0
- package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +2 -1
- package/dist/sse/json-to-sse/responses-json-to-sse-converter.d.ts +80 -0
- package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +1 -1
- package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +2 -2
- package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +1 -1
- package/dist/sse/json-to-sse/sequencers/responses-sequencer.d.ts +40 -0
- package/dist/sse/shared/chat-serializer.d.ts +4 -0
- package/dist/sse/shared/constants.d.ts +272 -0
- package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +1 -1
- package/dist/sse/shared/serializers/base-serializer.d.ts +158 -0
- package/dist/sse/shared/serializers/chat-event-serializer.d.ts +82 -0
- package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +1 -1
- package/dist/sse/shared/serializers/index.d.ts +2 -1
- package/dist/sse/shared/serializers/responses-event-serializer.d.ts +123 -0
- package/dist/sse/shared/serializers/types.d.ts +51 -0
- package/dist/sse/shared/utils.d.ts +254 -0
- package/dist/sse/shared/writer.d.ts +2 -2
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -1
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +1 -1
- package/dist/sse/sse-to-json/builders/response-builder.d.ts +1 -1
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +2 -1
- package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +2 -1
- package/dist/sse/sse-to-json/parsers/sse-parser.d.ts +73 -0
- package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
- package/dist/sse/types/chat-types.d.ts +1 -1
- package/dist/sse/types/responses-types.d.ts +1 -1
- package/dist/tools/apply-patch/execution-capturer.d.ts +13 -0
- package/dist/tools/apply-patch/execution-capturer.js +158 -0
- package/dist/tools/apply-patch/regression-capturer.d.ts +1 -0
- package/dist/tools/apply-patch/regression-capturer.js +5 -4
- package/dist/tools/apply-patch/structured.js +109 -13
- package/dist/tools/apply-patch/validator.js +112 -18
- package/dist/tools/tool-registry.d.ts +8 -0
- package/dist/tools/tool-registry.js +2 -1
- package/package.json +4 -4
- package/dist/conversion/compat/actions/apply-patch-format-fixer.js +0 -233
- package/dist/conversion/config/compat-profiles.json +0 -38
- package/dist/conversion/hub/pipeline/context-limit.d.ts +0 -13
- package/dist/conversion/hub/pipeline/context-limit.js +0 -55
- package/dist/conversion/hub/response/server-side-tools.d.ts +0 -26
- package/dist/conversion/hub/response/server-side-tools.js +0 -383
- package/dist/conversion/shared/bridge-conversation-store.d.ts +0 -41
- package/dist/conversion/shared/bridge-conversation-store.js +0 -279
- package/dist/conversion/shared/bridge-request-adapter.d.ts +0 -28
- package/dist/conversion/shared/bridge-request-adapter.js +0 -430
- package/dist/conversion/shared/responses-id-utils.js +0 -42
- package/dist/conversion/shared/responses-instructions.js +0 -113
- package/dist/conversion/shared/responses-message-utils.d.ts +0 -15
- package/dist/conversion/shared/responses-message-utils.js +0 -206
- package/dist/conversion/shared/responses-metadata.js +0 -1
- package/dist/conversion/shared/responses-output-utils.d.ts +0 -7
- package/dist/conversion/shared/responses-output-utils.js +0 -108
- package/dist/conversion/shared/responses-types.d.ts +0 -33
- package/dist/conversion/shared/tool-normalizers.d.ts +0 -4
- package/dist/conversion/shared/tool-normalizers.js +0 -84
- package/dist/filters/special/request-streaming-to-nonstreaming.d.ts +0 -13
- package/dist/filters/special/request-streaming-to-nonstreaming.js +0 -39
- package/dist/filters/special/response-apply-patch-toon-decode.d.ts +0 -23
- package/dist/filters/special/response-apply-patch-toon-decode.js +0 -460
- package/dist/filters/special/response-tool-arguments-toon-decode.d.ts +0 -10
- package/dist/filters/special/response-tool-arguments-toon-decode.js +0 -154
- package/dist/servertool/flow-types.d.ts +0 -40
- package/dist/servertool/flow-types.js +0 -1
- package/dist/servertool/orchestration-types.d.ts +0 -33
- package/dist/servertool/orchestration-types.js +0 -1
- package/dist/servertool/vision-tool.d.ts +0 -2
- package/dist/servertool/vision-tool.js +0 -185
- package/dist/tools/patch-args-normalizer.d.ts +0 -15
- package/dist/tools/patch-args-normalizer.js +0 -472
- package/dist/utils/toon.d.ts +0 -4
- package/dist/utils/toon.js +0 -75
- /package/dist/{conversion/compat/actions/apply-patch-format-fixer.d.ts → servertool/handlers/apply-patch-guard.d.ts} +0 -0
- /package/dist/{conversion/shared/responses-types.js → servertool/handlers/exec-command-guard.d.ts} +0 -0
|
@@ -424,14 +424,40 @@ export function buildResponsesRequestFromChat(payload, ctx, extras) {
|
|
|
424
424
|
else if (isPlainObject(metadataExtraFields?.metadata)) {
|
|
425
425
|
out.metadata = { ...metadataExtraFields.metadata };
|
|
426
426
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
427
|
+
// Some upstream `/v1/responses` providers reject an unknown top-level `parameters` object
|
|
428
|
+
// (e.g. HTTP 400: Unsupported parameter: parameters).
|
|
429
|
+
// We accept `parameters` as an internal carrier (capturedChatRequest.parameters, followups),
|
|
430
|
+
// but must flatten supported fields into the top-level request shape before sending upstream.
|
|
431
|
+
const parametersCandidate = (ctx?.parameters && Object.keys(ctx.parameters).length ? { ...ctx.parameters } : undefined) ??
|
|
432
|
+
(chat.parameters && typeof chat.parameters === 'object' && !Array.isArray(chat.parameters) ? { ...chat.parameters } : undefined) ??
|
|
433
|
+
(isPlainObject(metadataExtraFields?.parameters) ? { ...metadataExtraFields.parameters } : undefined);
|
|
434
|
+
if (parametersCandidate) {
|
|
435
|
+
const allowed = new Set([
|
|
436
|
+
// Common OpenAI Responses parameters
|
|
437
|
+
'temperature',
|
|
438
|
+
'top_p',
|
|
439
|
+
'max_output_tokens',
|
|
440
|
+
'seed',
|
|
441
|
+
'logit_bias',
|
|
442
|
+
'user',
|
|
443
|
+
'parallel_tool_calls',
|
|
444
|
+
'tool_choice',
|
|
445
|
+
'response_format',
|
|
446
|
+
'stream'
|
|
447
|
+
]);
|
|
448
|
+
// Back-compat: StandardizedRequest uses max_tokens; map it to Responses max_output_tokens.
|
|
449
|
+
if (parametersCandidate.max_output_tokens === undefined && parametersCandidate.max_tokens !== undefined) {
|
|
450
|
+
parametersCandidate.max_output_tokens = parametersCandidate.max_tokens;
|
|
451
|
+
}
|
|
452
|
+
for (const [key, value] of Object.entries(parametersCandidate)) {
|
|
453
|
+
if (!allowed.has(key))
|
|
454
|
+
continue;
|
|
455
|
+
if (key === 'stream')
|
|
456
|
+
continue; // handled by resolvedStream above
|
|
457
|
+
if (out[key] === undefined) {
|
|
458
|
+
out[key] = value;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
435
461
|
}
|
|
436
462
|
ensureBridgeInstructions(out);
|
|
437
463
|
return { request: out, originalSystemMessages };
|
|
@@ -590,8 +616,21 @@ export function buildResponsesPayloadFromChat(payload, context) {
|
|
|
590
616
|
});
|
|
591
617
|
}
|
|
592
618
|
}
|
|
593
|
-
catch {
|
|
594
|
-
|
|
619
|
+
catch (error) {
|
|
620
|
+
const message = error instanceof Error
|
|
621
|
+
? error.message
|
|
622
|
+
: typeof error === 'string'
|
|
623
|
+
? error
|
|
624
|
+
: 'unknown_error';
|
|
625
|
+
try {
|
|
626
|
+
// eslint-disable-next-line no-console
|
|
627
|
+
console.error(`\x1b[31m[responses-bridge][response_outbound] bridge action pipeline failed requestId=${context?.requestId ?? 'unknown'} error=${message}\x1b[0m`);
|
|
628
|
+
}
|
|
629
|
+
catch {
|
|
630
|
+
// ignore logging failures
|
|
631
|
+
}
|
|
632
|
+
// Do not fail-close here: preserve protocol-correct payload shape and let the client
|
|
633
|
+
// handle any downstream validation errors. The bridge policy pipeline is best-effort.
|
|
595
634
|
}
|
|
596
635
|
}
|
|
597
636
|
if (message) {
|
|
@@ -625,6 +664,10 @@ export function buildResponsesPayloadFromChat(payload, context) {
|
|
|
625
664
|
out.usage = outputBuild.usage;
|
|
626
665
|
if (outputBuild.requiredAction)
|
|
627
666
|
out.required_action = outputBuild.requiredAction;
|
|
667
|
+
// Normalize tool_call arguments for client-declared tool formats (e.g. format:"freeform").
|
|
668
|
+
// If we cannot repair/match the declared schema/format, we fall back to returning the
|
|
669
|
+
// original tool-call arguments and let the client surface the error.
|
|
670
|
+
normalizeResponsesToolCallArgumentsForClient(out, context);
|
|
628
671
|
// Do not inject captured tool results here; keep Chat back-half behavior standard.
|
|
629
672
|
if (context) {
|
|
630
673
|
for (const k of ['metadata', 'parallel_tool_calls', 'tool_choice', 'include']) {
|
|
@@ -649,6 +692,216 @@ export function buildResponsesPayloadFromChat(payload, context) {
|
|
|
649
692
|
}
|
|
650
693
|
return out;
|
|
651
694
|
}
|
|
695
|
+
function normalizeResponsesToolCallArgumentsForClient(responsesPayload, context) {
|
|
696
|
+
const toolsRaw = Array.isArray(context?.toolsRaw) ? context?.toolsRaw : [];
|
|
697
|
+
if (!toolsRaw.length) {
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
const toolIndex = buildClientToolIndex(toolsRaw);
|
|
701
|
+
if (!toolIndex.size) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
const normalizeCallArgs = (toolName, argsRaw) => {
|
|
705
|
+
const spec = toolIndex.get(toolName);
|
|
706
|
+
if (!spec)
|
|
707
|
+
return argsRaw;
|
|
708
|
+
// format:"freeform" means client expects raw text (not JSON) in the arguments field.
|
|
709
|
+
if (typeof spec.format === 'string' && spec.format.trim().toLowerCase() === 'freeform') {
|
|
710
|
+
const rawText = extractFreeformTextFromArgs(argsRaw);
|
|
711
|
+
if (rawText && rawText.trim().length) {
|
|
712
|
+
return rawText;
|
|
713
|
+
}
|
|
714
|
+
return argsRaw;
|
|
715
|
+
}
|
|
716
|
+
// Schema-aware key alignment for common server-side tools.
|
|
717
|
+
// If we cannot repair, keep original args and let the client surface the error.
|
|
718
|
+
const params = spec.parameters;
|
|
719
|
+
if (!params)
|
|
720
|
+
return argsRaw;
|
|
721
|
+
const schema = extractJsonSchemaLike(params);
|
|
722
|
+
if (!schema)
|
|
723
|
+
return argsRaw;
|
|
724
|
+
const parsed = tryParseJson(argsRaw);
|
|
725
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
726
|
+
return argsRaw;
|
|
727
|
+
}
|
|
728
|
+
const record = parsed;
|
|
729
|
+
const repaired = repairToolArgsBySchemaKeys(toolName, record, schema);
|
|
730
|
+
if (!repaired) {
|
|
731
|
+
return argsRaw;
|
|
732
|
+
}
|
|
733
|
+
try {
|
|
734
|
+
return JSON.stringify(repaired);
|
|
735
|
+
}
|
|
736
|
+
catch {
|
|
737
|
+
return argsRaw;
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
// 1) output[] function_call items
|
|
741
|
+
const output = Array.isArray(responsesPayload.output) ? responsesPayload.output : [];
|
|
742
|
+
for (const item of output) {
|
|
743
|
+
if (!item || typeof item !== 'object')
|
|
744
|
+
continue;
|
|
745
|
+
const type = typeof item.type === 'string' ? String(item.type).trim().toLowerCase() : '';
|
|
746
|
+
if (type !== 'function_call')
|
|
747
|
+
continue;
|
|
748
|
+
const name = typeof item.name === 'string' ? String(item.name).trim() : '';
|
|
749
|
+
if (!name)
|
|
750
|
+
continue;
|
|
751
|
+
if (!toolIndex.has(name))
|
|
752
|
+
continue;
|
|
753
|
+
item.arguments = normalizeCallArgs(name, item.arguments);
|
|
754
|
+
}
|
|
755
|
+
// 2) required_action.submit_tool_outputs.tool_calls[] tool calls
|
|
756
|
+
const toolCalls = responsesPayload?.required_action?.submit_tool_outputs?.tool_calls;
|
|
757
|
+
const calls = Array.isArray(toolCalls) ? toolCalls : [];
|
|
758
|
+
for (const call of calls) {
|
|
759
|
+
if (!call || typeof call !== 'object')
|
|
760
|
+
continue;
|
|
761
|
+
const fn = call.function && typeof call.function === 'object' && !Array.isArray(call.function) ? call.function : null;
|
|
762
|
+
const name = typeof fn?.name === 'string' ? String(fn.name).trim() : '';
|
|
763
|
+
if (!name)
|
|
764
|
+
continue;
|
|
765
|
+
if (!toolIndex.has(name))
|
|
766
|
+
continue;
|
|
767
|
+
fn.arguments = normalizeCallArgs(name, fn.arguments);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
function buildClientToolIndex(toolsRaw) {
|
|
771
|
+
const index = new Map();
|
|
772
|
+
for (const tool of toolsRaw) {
|
|
773
|
+
if (!tool || typeof tool !== 'object' || Array.isArray(tool))
|
|
774
|
+
continue;
|
|
775
|
+
const t = tool;
|
|
776
|
+
const fn = t.function && typeof t.function === 'object' && !Array.isArray(t.function) ? t.function : undefined;
|
|
777
|
+
const nameRaw = (fn && typeof fn.name === 'string' ? fn.name : undefined) ?? (typeof t.name === 'string' ? t.name : undefined);
|
|
778
|
+
const name = typeof nameRaw === 'string' ? nameRaw.trim() : '';
|
|
779
|
+
if (!name)
|
|
780
|
+
continue;
|
|
781
|
+
const formatRaw = (typeof t.format === 'string' ? t.format : undefined) ??
|
|
782
|
+
(fn && typeof fn.format === 'string' ? fn.format : undefined);
|
|
783
|
+
const parametersRaw = (fn && fn.parameters && typeof fn.parameters === 'object' && !Array.isArray(fn.parameters) ? fn.parameters : undefined) ??
|
|
784
|
+
(t.parameters && typeof t.parameters === 'object' && !Array.isArray(t.parameters) ? t.parameters : undefined) ??
|
|
785
|
+
undefined;
|
|
786
|
+
index.set(name, {
|
|
787
|
+
name,
|
|
788
|
+
...(typeof formatRaw === 'string' && formatRaw.trim().length ? { format: formatRaw.trim() } : {}),
|
|
789
|
+
...(parametersRaw ? { parameters: parametersRaw } : {})
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
return index;
|
|
793
|
+
}
|
|
794
|
+
function tryParseJson(value) {
|
|
795
|
+
if (typeof value !== 'string')
|
|
796
|
+
return null;
|
|
797
|
+
const trimmed = value.trim();
|
|
798
|
+
if (!trimmed)
|
|
799
|
+
return null;
|
|
800
|
+
if (!(trimmed.startsWith('{') || trimmed.startsWith('[')))
|
|
801
|
+
return null;
|
|
802
|
+
try {
|
|
803
|
+
return JSON.parse(trimmed);
|
|
804
|
+
}
|
|
805
|
+
catch {
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
function looksLikeApplyPatchText(value) {
|
|
810
|
+
const trimmed = value.trim();
|
|
811
|
+
if (!trimmed)
|
|
812
|
+
return false;
|
|
813
|
+
return trimmed.includes('*** Begin Patch') && trimmed.includes('*** End Patch');
|
|
814
|
+
}
|
|
815
|
+
function extractFreeformTextFromArgs(argsRaw) {
|
|
816
|
+
if (typeof argsRaw === 'string') {
|
|
817
|
+
const trimmed = argsRaw.trim();
|
|
818
|
+
if (!trimmed)
|
|
819
|
+
return null;
|
|
820
|
+
// If JSON wrapper, extract common fields first.
|
|
821
|
+
const parsed = tryParseJson(trimmed);
|
|
822
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
823
|
+
const rec = parsed;
|
|
824
|
+
for (const key of ['instructions', 'patch', 'input', 'text']) {
|
|
825
|
+
if (typeof rec[key] === 'string' && rec[key].trim().length) {
|
|
826
|
+
return String(rec[key]).trim();
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
// If it's already freeform (not a JSON wrapper), keep.
|
|
831
|
+
if (!trimmed.startsWith('{') && !trimmed.startsWith('[') && looksLikeApplyPatchText(trimmed)) {
|
|
832
|
+
return trimmed;
|
|
833
|
+
}
|
|
834
|
+
// Unknown freeform: return as-is so client can error.
|
|
835
|
+
return trimmed;
|
|
836
|
+
}
|
|
837
|
+
// If arguments is an object (malformed), try to extract patch-like fields.
|
|
838
|
+
if (argsRaw && typeof argsRaw === 'object' && !Array.isArray(argsRaw)) {
|
|
839
|
+
const rec = argsRaw;
|
|
840
|
+
for (const key of ['instructions', 'patch', 'input', 'text']) {
|
|
841
|
+
if (typeof rec[key] === 'string' && rec[key].trim().length) {
|
|
842
|
+
return String(rec[key]).trim();
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return null;
|
|
847
|
+
}
|
|
848
|
+
function extractJsonSchemaLike(parameters) {
|
|
849
|
+
const required = Array.isArray(parameters.required)
|
|
850
|
+
? parameters.required.filter((v) => typeof v === 'string' && v.trim().length > 0).map((v) => v.trim())
|
|
851
|
+
: [];
|
|
852
|
+
const props = parameters.properties && typeof parameters.properties === 'object' && !Array.isArray(parameters.properties)
|
|
853
|
+
? parameters.properties
|
|
854
|
+
: {};
|
|
855
|
+
const properties = Object.keys(props);
|
|
856
|
+
if (!required.length && !properties.length) {
|
|
857
|
+
return null;
|
|
858
|
+
}
|
|
859
|
+
const additionalProperties = typeof parameters.additionalProperties === 'boolean' ? parameters.additionalProperties : true;
|
|
860
|
+
return { required, properties, additionalProperties };
|
|
861
|
+
}
|
|
862
|
+
function repairToolArgsBySchemaKeys(toolName, record, schema) {
|
|
863
|
+
// Apply known alias mappings only if the target keys are present in schema properties.
|
|
864
|
+
const wants = new Set(schema.properties);
|
|
865
|
+
const out = { ...record };
|
|
866
|
+
if (toolName === 'exec_command') {
|
|
867
|
+
if (wants.has('cmd') && out.command !== undefined && out.cmd === undefined)
|
|
868
|
+
out.cmd = out.command;
|
|
869
|
+
if (wants.has('command') && out.cmd !== undefined && out.command === undefined)
|
|
870
|
+
out.command = out.cmd;
|
|
871
|
+
}
|
|
872
|
+
if (toolName === 'write_stdin') {
|
|
873
|
+
if (wants.has('chars') && out.text !== undefined && out.chars === undefined)
|
|
874
|
+
out.chars = out.text;
|
|
875
|
+
if (wants.has('text') && out.chars !== undefined && out.text === undefined)
|
|
876
|
+
out.text = out.chars;
|
|
877
|
+
}
|
|
878
|
+
if (toolName === 'apply_patch') {
|
|
879
|
+
if (wants.has('instructions') && out.instructions === undefined) {
|
|
880
|
+
if (typeof out.patch === 'string' && out.patch.trim().length)
|
|
881
|
+
out.instructions = out.patch;
|
|
882
|
+
else if (typeof out.input === 'string' && out.input.trim().length)
|
|
883
|
+
out.instructions = out.input;
|
|
884
|
+
}
|
|
885
|
+
if (wants.has('patch') && out.patch === undefined) {
|
|
886
|
+
if (typeof out.instructions === 'string' && out.instructions.trim().length)
|
|
887
|
+
out.patch = out.instructions;
|
|
888
|
+
else if (typeof out.input === 'string' && out.input.trim().length)
|
|
889
|
+
out.patch = out.input;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
for (const key of schema.required) {
|
|
893
|
+
if (!Object.prototype.hasOwnProperty.call(out, key)) {
|
|
894
|
+
return null;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
if (schema.additionalProperties === false && schema.properties.length > 0) {
|
|
898
|
+
for (const key of Object.keys(out)) {
|
|
899
|
+
if (!wants.has(key))
|
|
900
|
+
delete out[key];
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
return out;
|
|
904
|
+
}
|
|
652
905
|
function unwrapData(value) {
|
|
653
906
|
let current = value;
|
|
654
907
|
const seen = new Set();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type Unknown = Record<string, unknown>;
|
|
2
|
+
export interface NormalizeResult<T = Record<string, unknown>> {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
value?: T;
|
|
5
|
+
errors?: string[];
|
|
6
|
+
}
|
|
7
|
+
type JsonSchema = {
|
|
8
|
+
type?: string | string[];
|
|
9
|
+
properties?: Record<string, JsonSchema & {
|
|
10
|
+
['x-aliases']?: string[];
|
|
11
|
+
}>;
|
|
12
|
+
required?: string[];
|
|
13
|
+
items?: JsonSchema;
|
|
14
|
+
additionalProperties?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export declare function normalizeArgsBySchema(input: any, schema?: JsonSchema): NormalizeResult;
|
|
17
|
+
export declare function normalizeTools(tools: any[]): Unknown[];
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ConversionContext, ConversionProfile } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* 统一的 Chat 请求侧过滤链路。
|
|
4
|
+
*
|
|
5
|
+
* 目标:
|
|
6
|
+
* - 所有进入 Provider 的 Chat 形状请求(无论入口为 /v1/chat、/v1/responses 还是 /v1/messages),
|
|
7
|
+
* 都在这里走同一套工具治理与参数标准化逻辑。
|
|
8
|
+
*/
|
|
9
|
+
export declare function runStandardChatRequestFilters(chatRequest: any, profile: ConversionProfile, context: ConversionContext): Promise<any>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ProviderProtocolErrorCode = 'TOOL_PROTOCOL_ERROR' | 'SSE_DECODE_ERROR' | 'MALFORMED_RESPONSE' | 'MALFORMED_REQUEST' | 'SERVERTOOL_FOLLOWUP_FAILED' | 'SERVERTOOL_TIMEOUT' | 'SERVERTOOL_HANDLER_FAILED';
|
|
1
|
+
export type ProviderProtocolErrorCode = 'TOOL_PROTOCOL_ERROR' | 'SSE_DECODE_ERROR' | 'MALFORMED_RESPONSE' | 'MALFORMED_REQUEST' | 'SERVERTOOL_FOLLOWUP_FAILED' | 'SERVERTOOL_EMPTY_FOLLOWUP' | 'SERVERTOOL_TIMEOUT' | 'SERVERTOOL_HANDLER_FAILED';
|
|
2
2
|
export type ProviderErrorCategory = 'EXTERNAL_ERROR' | 'TOOL_ERROR' | 'INTERNAL_ERROR';
|
|
3
3
|
export interface ProviderProtocolErrorOptions {
|
|
4
4
|
code: ProviderProtocolErrorCode;
|
|
@@ -2,6 +2,38 @@ import { jsonClone } from '../hub/types/json.js';
|
|
|
2
2
|
function isPlainRecord(value) {
|
|
3
3
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
4
4
|
}
|
|
5
|
+
function pickBestSchemaVariant(variants) {
|
|
6
|
+
if (!Array.isArray(variants) || variants.length === 0) {
|
|
7
|
+
return { type: 'object', properties: {} };
|
|
8
|
+
}
|
|
9
|
+
const score = (value) => {
|
|
10
|
+
if (!isPlainRecord(value)) {
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
const type = typeof value.type === 'string' ? value.type.trim().toLowerCase() : '';
|
|
14
|
+
// Prefer 'string' to maximize compatibility (can carry arbitrary serialized content),
|
|
15
|
+
// then object/array, then primitives.
|
|
16
|
+
if (type === 'string')
|
|
17
|
+
return 100;
|
|
18
|
+
if (type === 'object')
|
|
19
|
+
return 80;
|
|
20
|
+
if (type === 'array')
|
|
21
|
+
return 60;
|
|
22
|
+
if (type === 'integer' || type === 'number' || type === 'boolean')
|
|
23
|
+
return 50;
|
|
24
|
+
return 10;
|
|
25
|
+
};
|
|
26
|
+
let best = variants[0];
|
|
27
|
+
let bestScore = score(best);
|
|
28
|
+
for (const candidate of variants.slice(1)) {
|
|
29
|
+
const candidateScore = score(candidate);
|
|
30
|
+
if (candidateScore > bestScore) {
|
|
31
|
+
best = candidate;
|
|
32
|
+
bestScore = candidateScore;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return best;
|
|
36
|
+
}
|
|
5
37
|
function cloneParameters(value) {
|
|
6
38
|
if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
7
39
|
return value;
|
|
@@ -10,6 +42,28 @@ function cloneParameters(value) {
|
|
|
10
42
|
return value.map((entry) => cloneParameters(entry));
|
|
11
43
|
}
|
|
12
44
|
if (isPlainRecord(value)) {
|
|
45
|
+
// Gemini function_declarations.parameters only support a subset of JSON Schema.
|
|
46
|
+
// Additionally, recent Antigravity/Gemini backends may produce MALFORMED_FUNCTION_CALL
|
|
47
|
+
// when schemas include combinators (oneOf/anyOf/allOf). Prefer a safe single-variant schema.
|
|
48
|
+
const combinator = Array.isArray(value.oneOf)
|
|
49
|
+
? 'oneOf'
|
|
50
|
+
: Array.isArray(value.anyOf)
|
|
51
|
+
? 'anyOf'
|
|
52
|
+
: Array.isArray(value.allOf)
|
|
53
|
+
? 'allOf'
|
|
54
|
+
: undefined;
|
|
55
|
+
if (combinator) {
|
|
56
|
+
const variants = value[combinator];
|
|
57
|
+
const chosen = pickBestSchemaVariant(Array.isArray(variants) ? variants : []);
|
|
58
|
+
const simplified = cloneParameters(chosen);
|
|
59
|
+
// Preserve description if present on the wrapper node.
|
|
60
|
+
if (isPlainRecord(simplified) &&
|
|
61
|
+
typeof value.description === 'string' &&
|
|
62
|
+
typeof simplified.description !== 'string') {
|
|
63
|
+
simplified.description = String(value.description);
|
|
64
|
+
}
|
|
65
|
+
return simplified;
|
|
66
|
+
}
|
|
13
67
|
const cloned = {};
|
|
14
68
|
for (const [key, entry] of Object.entries(value)) {
|
|
15
69
|
// Gemini function_declarations.parameters only support a subset of JSON Schema.
|
|
@@ -19,6 +73,13 @@ function cloneParameters(value) {
|
|
|
19
73
|
continue;
|
|
20
74
|
if (key === 'exclusiveMinimum' || key === 'exclusiveMaximum' || key === 'propertyNames')
|
|
21
75
|
continue;
|
|
76
|
+
// Keep Gemini tool schemas permissive to avoid upstream MALFORMED_FUNCTION_CALL on strict validation.
|
|
77
|
+
// Validation is enforced by llmswitch-core / servertool layers instead.
|
|
78
|
+
if (key === 'required' || key === 'additionalProperties')
|
|
79
|
+
continue;
|
|
80
|
+
// Combinators are handled at the node level above.
|
|
81
|
+
if (key === 'oneOf' || key === 'anyOf' || key === 'allOf')
|
|
82
|
+
continue;
|
|
22
83
|
}
|
|
23
84
|
cloned[key] = cloneParameters(entry);
|
|
24
85
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isImagePath(p: unknown): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function normalizeChatRequest(request: any): any;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function resolveBudgetForModelSync(modelId: string): {
|
|
2
|
+
maxBytes: number;
|
|
3
|
+
safetyRatio: number;
|
|
4
|
+
allowedBytes: number;
|
|
5
|
+
source: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const resolveBudgetForModel: (modelId: string) => Promise<{
|
|
8
|
+
maxBytes: number;
|
|
9
|
+
safetyRatio: number;
|
|
10
|
+
allowedBytes: number;
|
|
11
|
+
source: string;
|
|
12
|
+
}>;
|
|
13
|
+
export declare function enforceChatBudget(chat: any, modelId: string): any;
|
|
@@ -1,28 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export interface ResponsesRequestContext {
|
|
3
|
-
requestId?: string;
|
|
4
|
-
instructions?: string;
|
|
5
|
-
originalSystemMessages?: string[];
|
|
6
|
-
input?: BridgeInputItem[];
|
|
7
|
-
include?: unknown;
|
|
8
|
-
store?: unknown;
|
|
9
|
-
toolChoice?: unknown;
|
|
10
|
-
parallelToolCalls?: boolean;
|
|
11
|
-
metadata?: Record<string, unknown>;
|
|
12
|
-
responseFormat?: unknown;
|
|
13
|
-
toolsRaw?: BridgeToolDefinition[];
|
|
14
|
-
stream?: boolean;
|
|
15
|
-
instructionsIsRaw?: boolean;
|
|
16
|
-
isChatPayload?: boolean;
|
|
17
|
-
isResponsesPayload?: boolean;
|
|
18
|
-
}
|
|
19
|
-
export interface BuildChatRequestResult {
|
|
20
|
-
request: Record<string, unknown>;
|
|
21
|
-
toolsNormalized?: Array<Record<string, unknown>>;
|
|
22
|
-
}
|
|
23
|
-
export declare function captureResponsesContext(payload: Record<string, unknown>, dto?: {
|
|
24
|
-
route?: {
|
|
25
|
-
requestId?: string;
|
|
26
|
-
};
|
|
27
|
-
}): ResponsesRequestContext;
|
|
28
|
-
export declare function buildChatRequestFromResponses(payload: Record<string, unknown>, context: ResponsesRequestContext): BuildChatRequestResult;
|
|
1
|
+
export { captureResponsesContext, buildChatRequestFromResponses } from '../responses/responses-openai-bridge.js';
|