@jsonstudio/llms 0.6.938 → 0.6.1164

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.
Files changed (131) hide show
  1. package/dist/conversion/hub/operation-table/operation-table-runner.d.ts +18 -0
  2. package/dist/conversion/hub/operation-table/operation-table-runner.js +158 -0
  3. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.d.ts +8 -0
  4. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +303 -0
  5. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.d.ts +8 -0
  6. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +413 -0
  7. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.d.ts +7 -0
  8. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +841 -0
  9. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.d.ts +21 -0
  10. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +535 -0
  11. package/dist/conversion/hub/ops/operations.d.ts +19 -0
  12. package/dist/conversion/hub/ops/operations.js +126 -0
  13. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +9 -0
  14. package/dist/conversion/hub/pipeline/hub-pipeline.js +533 -24
  15. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +6 -0
  16. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +6 -3
  17. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +11 -0
  18. package/dist/conversion/hub/policy/policy-engine.js +41 -9
  19. package/dist/conversion/hub/policy/protocol-spec.d.ts +25 -0
  20. package/dist/conversion/hub/policy/protocol-spec.js +73 -23
  21. package/dist/conversion/hub/process/chat-process.js +252 -41
  22. package/dist/conversion/hub/response/provider-response.js +175 -2
  23. package/dist/conversion/hub/response/response-runtime.js +1 -1
  24. package/dist/conversion/hub/semantic-mappers/anthropic-mapper.d.ts +1 -8
  25. package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +1 -365
  26. package/dist/conversion/hub/semantic-mappers/chat-mapper.d.ts +1 -8
  27. package/dist/conversion/hub/semantic-mappers/chat-mapper.js +1 -436
  28. package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +1 -7
  29. package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +1 -894
  30. package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +1 -21
  31. package/dist/conversion/hub/semantic-mappers/responses-mapper.js +1 -593
  32. package/dist/conversion/hub/tool-surface/tool-surface-engine.d.ts +18 -0
  33. package/dist/conversion/hub/tool-surface/tool-surface-engine.js +571 -0
  34. package/dist/conversion/responses/responses-openai-bridge.js +14 -2
  35. package/dist/conversion/shared/bridge-message-utils.js +2 -8
  36. package/dist/conversion/shared/bridge-policies.js +5 -105
  37. package/dist/conversion/shared/gemini-tool-utils.js +121 -4
  38. package/dist/conversion/shared/protocol-field-allowlists.d.ts +7 -0
  39. package/dist/conversion/shared/protocol-field-allowlists.js +145 -0
  40. package/dist/conversion/shared/reasoning-tool-normalizer.js +4 -2
  41. package/dist/conversion/shared/snapshot-hooks.js +166 -3
  42. package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -0
  43. package/dist/conversion/shared/text-markup-normalizer.js +345 -9
  44. package/dist/conversion/shared/thought-signature-validator.d.ts +52 -0
  45. package/dist/conversion/shared/thought-signature-validator.js +170 -0
  46. package/dist/conversion/shared/tool-argument-repairer.d.ts +39 -0
  47. package/dist/conversion/shared/tool-argument-repairer.js +56 -0
  48. package/dist/conversion/shared/tool-call-id-manager.d.ts +113 -0
  49. package/dist/conversion/shared/tool-call-id-manager.js +231 -0
  50. package/dist/conversion/shared/tool-canonicalizer.js +2 -11
  51. package/dist/router/virtual-router/bootstrap.js +54 -5
  52. package/dist/router/virtual-router/engine-selection.js +132 -42
  53. package/dist/router/virtual-router/engine.d.ts +3 -0
  54. package/dist/router/virtual-router/engine.js +142 -33
  55. package/dist/router/virtual-router/health-weighted.d.ts +25 -0
  56. package/dist/router/virtual-router/health-weighted.js +63 -0
  57. package/dist/router/virtual-router/load-balancer.d.ts +2 -0
  58. package/dist/router/virtual-router/load-balancer.js +45 -16
  59. package/dist/router/virtual-router/routing-instructions.js +17 -1
  60. package/dist/router/virtual-router/sticky-session-store.js +136 -24
  61. package/dist/router/virtual-router/stop-message-file-resolver.d.ts +1 -0
  62. package/dist/router/virtual-router/stop-message-file-resolver.js +74 -0
  63. package/dist/router/virtual-router/stop-message-state-sync.d.ts +15 -0
  64. package/dist/router/virtual-router/stop-message-state-sync.js +57 -0
  65. package/dist/router/virtual-router/types.d.ts +70 -0
  66. package/dist/servertool/clock/config.d.ts +7 -0
  67. package/dist/servertool/clock/config.js +27 -0
  68. package/dist/servertool/clock/daemon.d.ts +3 -0
  69. package/dist/servertool/clock/daemon.js +79 -0
  70. package/dist/servertool/clock/io.d.ts +2 -0
  71. package/dist/servertool/clock/io.js +13 -0
  72. package/dist/servertool/clock/paths.d.ts +4 -0
  73. package/dist/servertool/clock/paths.js +25 -0
  74. package/dist/servertool/clock/session-store.d.ts +3 -0
  75. package/dist/servertool/clock/session-store.js +56 -0
  76. package/dist/servertool/clock/state.d.ts +5 -0
  77. package/dist/servertool/clock/state.js +62 -0
  78. package/dist/servertool/clock/task-store.d.ts +5 -0
  79. package/dist/servertool/clock/task-store.js +4 -0
  80. package/dist/servertool/clock/tasks.d.ts +17 -0
  81. package/dist/servertool/clock/tasks.js +221 -0
  82. package/dist/servertool/clock/types.d.ts +36 -0
  83. package/dist/servertool/clock/types.js +1 -0
  84. package/dist/servertool/engine.d.ts +2 -0
  85. package/dist/servertool/engine.js +164 -8
  86. package/dist/servertool/followup-shadow.d.ts +16 -0
  87. package/dist/servertool/followup-shadow.js +145 -0
  88. package/dist/servertool/handlers/apply-patch-guard.js +1 -265
  89. package/dist/servertool/handlers/clock-auto.d.ts +1 -0
  90. package/dist/servertool/handlers/clock-auto.js +160 -0
  91. package/dist/servertool/handlers/clock.d.ts +1 -0
  92. package/dist/servertool/handlers/clock.js +197 -0
  93. package/dist/servertool/handlers/exec-command-guard.js +7 -555
  94. package/dist/servertool/handlers/followup-request-builder.d.ts +15 -7
  95. package/dist/servertool/handlers/followup-request-builder.js +248 -28
  96. package/dist/servertool/handlers/gemini-empty-reply-continue.js +62 -169
  97. package/dist/servertool/handlers/iflow-model-error-retry.js +18 -28
  98. package/dist/servertool/handlers/recursive-detection-guard.d.ts +1 -0
  99. package/dist/servertool/handlers/recursive-detection-guard.js +333 -0
  100. package/dist/servertool/handlers/stop-message-auto.js +47 -175
  101. package/dist/servertool/handlers/vision.d.ts +7 -1
  102. package/dist/servertool/handlers/vision.js +61 -117
  103. package/dist/servertool/handlers/web-search.d.ts +7 -1
  104. package/dist/servertool/handlers/web-search.js +122 -105
  105. package/dist/servertool/reenter-backend.d.ts +23 -0
  106. package/dist/servertool/reenter-backend.js +18 -0
  107. package/dist/servertool/server-side-tools.d.ts +3 -2
  108. package/dist/servertool/server-side-tools.js +64 -10
  109. package/dist/servertool/types.d.ts +92 -3
  110. package/dist/sse/json-to-sse/event-generators/responses.js +3 -21
  111. package/dist/sse/shared/serializers/responses-event-serializer.d.ts +8 -0
  112. package/dist/sse/shared/serializers/responses-event-serializer.js +19 -0
  113. package/dist/sse/shared/writer.js +24 -7
  114. package/dist/tools/apply-patch/execution-capturer.js +3 -1
  115. package/dist/tools/apply-patch/json/parse-loose.d.ts +3 -0
  116. package/dist/tools/apply-patch/json/parse-loose.js +139 -0
  117. package/dist/tools/apply-patch/patch-text/context-diff.d.ts +1 -0
  118. package/dist/tools/apply-patch/patch-text/context-diff.js +173 -0
  119. package/dist/tools/apply-patch/patch-text/git-diff.d.ts +1 -0
  120. package/dist/tools/apply-patch/patch-text/git-diff.js +138 -0
  121. package/dist/tools/apply-patch/patch-text/looks-like-patch.d.ts +1 -0
  122. package/dist/tools/apply-patch/patch-text/looks-like-patch.js +13 -0
  123. package/dist/tools/apply-patch/patch-text/normalize.d.ts +3 -0
  124. package/dist/tools/apply-patch/patch-text/normalize.js +262 -0
  125. package/dist/tools/apply-patch/structured/coercion.d.ts +3 -0
  126. package/dist/tools/apply-patch/structured/coercion.js +82 -0
  127. package/dist/tools/apply-patch/validation/shared.d.ts +3 -0
  128. package/dist/tools/apply-patch/validation/shared.js +6 -0
  129. package/dist/tools/apply-patch/validator.d.ts +2 -2
  130. package/dist/tools/apply-patch/validator.js +6 -556
  131. package/package.json +1 -1
@@ -0,0 +1,18 @@
1
+ import type { AdapterContext, ChatEnvelope } from '../types/chat-envelope.js';
2
+ import type { FormatEnvelope } from '../types/format-envelope.js';
3
+ import type { JsonObject } from '../types/json.js';
4
+ export declare function applyHubOperationTableInbound(options: {
5
+ formatEnvelope: FormatEnvelope<JsonObject>;
6
+ chatEnvelope: ChatEnvelope;
7
+ adapterContext: AdapterContext;
8
+ }): void;
9
+ export declare function applyHubOperationTableOutboundPreMap(options: {
10
+ protocol: string;
11
+ chatEnvelope: ChatEnvelope;
12
+ adapterContext: AdapterContext;
13
+ }): Promise<void>;
14
+ export declare function applyHubOperationTableOutboundPostMap(options: {
15
+ chatEnvelope: ChatEnvelope;
16
+ formatEnvelope: FormatEnvelope<JsonObject>;
17
+ adapterContext: AdapterContext;
18
+ }): void;
@@ -0,0 +1,158 @@
1
+ import { createBridgeActionState, runBridgeActionPipeline } from '../../shared/bridge-actions.js';
2
+ import { resolveBridgePolicy, resolvePolicyActions } from '../../shared/bridge-policies.js';
3
+ const INBOUND_BRIDGE_SPECS = {
4
+ 'openai-chat': { protocol: 'openai-chat', stage: 'request_inbound', messages: 'chat_envelope' },
5
+ // Keep parity with the legacy semantic mapper behavior: do not pass messages[] into the action state
6
+ // for openai-responses, since the bridge actions here are used as metadata hooks only.
7
+ 'openai-responses': { protocol: 'openai-responses', stage: 'request_inbound', messages: 'none', includeCapturedToolResults: true, moduleType: 'openai-responses' },
8
+ 'anthropic-messages': { protocol: 'anthropic-messages', stage: 'request_inbound', messages: 'chat_envelope' },
9
+ 'gemini-chat': { protocol: 'gemini-chat', stage: 'request_inbound', messages: 'chat_envelope' }
10
+ };
11
+ const OUTBOUND_BRIDGE_SPECS = {
12
+ 'openai-chat': { protocol: 'openai-chat', stage: 'request_outbound', messages: 'format_payload_messages', includeCapturedToolResults: true },
13
+ // Keep parity: openai-responses outbound actions should not touch normalized messages.
14
+ 'openai-responses': { protocol: 'openai-responses', stage: 'request_outbound', messages: 'none', moduleType: 'openai-responses' },
15
+ 'anthropic-messages': { protocol: 'anthropic-messages', stage: 'request_outbound', messages: 'format_payload_messages', includeCapturedToolResults: true },
16
+ // Keep parity with legacy gemini mapper: outbound hooks operate on ChatEnvelope.messages (not Gemini contents).
17
+ 'gemini-chat': { protocol: 'gemini-chat', stage: 'request_outbound', messages: 'chat_envelope', includeCapturedToolResults: true }
18
+ };
19
+ function isSupportedProtocol(protocol) {
20
+ return protocol === 'openai-chat' ||
21
+ protocol === 'openai-responses' ||
22
+ protocol === 'anthropic-messages' ||
23
+ protocol === 'gemini-chat';
24
+ }
25
+ function extractPayloadMessages(payload) {
26
+ const value = payload?.messages;
27
+ if (!Array.isArray(value)) {
28
+ return undefined;
29
+ }
30
+ return value;
31
+ }
32
+ function buildCapturedToolResults(toolOutputs) {
33
+ if (!Array.isArray(toolOutputs) || toolOutputs.length === 0) {
34
+ return undefined;
35
+ }
36
+ return toolOutputs.map((entry) => ({
37
+ tool_call_id: entry.tool_call_id,
38
+ output: entry.content,
39
+ name: entry.name
40
+ }));
41
+ }
42
+ function applyBridgePolicy(spec, options) {
43
+ const bridgePolicy = resolveBridgePolicy({ protocol: spec.protocol, moduleType: spec.moduleType ?? spec.protocol });
44
+ const actions = resolvePolicyActions(bridgePolicy, spec.stage);
45
+ if (!actions?.length) {
46
+ return;
47
+ }
48
+ const metadata = options.chatEnvelope.metadata;
49
+ const messages = spec.messages === 'chat_envelope'
50
+ ? options.chatEnvelope.messages
51
+ : spec.messages === 'format_payload_messages'
52
+ ? extractPayloadMessages(options.payload)
53
+ : undefined;
54
+ const capturedToolResults = spec.includeCapturedToolResults
55
+ ? buildCapturedToolResults(options.chatEnvelope.toolOutputs)
56
+ : undefined;
57
+ const actionState = createBridgeActionState({
58
+ ...(messages ? { messages } : {}),
59
+ rawRequest: options.payload,
60
+ metadata,
61
+ ...(capturedToolResults ? { capturedToolResults } : {})
62
+ });
63
+ runBridgeActionPipeline({
64
+ stage: spec.stage,
65
+ actions,
66
+ protocol: bridgePolicy?.protocol ?? spec.protocol,
67
+ moduleType: bridgePolicy?.moduleType ?? spec.moduleType ?? spec.protocol,
68
+ requestId: options.requestId,
69
+ state: actionState
70
+ });
71
+ if (spec.messages === 'chat_envelope') {
72
+ options.chatEnvelope.messages = actionState.messages;
73
+ }
74
+ }
75
+ function normalizeToolContent(value) {
76
+ if (typeof value === 'string')
77
+ return value;
78
+ if (value == null)
79
+ return '';
80
+ try {
81
+ return JSON.stringify(value);
82
+ }
83
+ catch {
84
+ return String(value ?? '');
85
+ }
86
+ }
87
+ function rebuildGeminiToolOutputsFromMessages(chatEnvelope) {
88
+ const messages = Array.isArray(chatEnvelope.messages) ? chatEnvelope.messages : [];
89
+ const missing = Array.isArray(chatEnvelope.metadata?.missingFields) ? chatEnvelope.metadata.missingFields : undefined;
90
+ const outputs = [];
91
+ messages.forEach((msg, index) => {
92
+ if (!msg || typeof msg !== 'object')
93
+ return;
94
+ if (msg.role !== 'tool')
95
+ return;
96
+ const callId = msg.tool_call_id || msg.id;
97
+ if (typeof callId !== 'string' || !callId.trim()) {
98
+ if (missing) {
99
+ missing.push({ path: `messages[${index}].tool_call_id`, reason: 'missing_tool_call_id' });
100
+ }
101
+ return;
102
+ }
103
+ outputs.push({
104
+ tool_call_id: callId.trim(),
105
+ content: normalizeToolContent(msg.content),
106
+ name: typeof msg.name === 'string' ? msg.name : undefined
107
+ });
108
+ });
109
+ chatEnvelope.toolOutputs = outputs.length ? outputs : undefined;
110
+ }
111
+ export function applyHubOperationTableInbound(options) {
112
+ const protocol = options.formatEnvelope.protocol;
113
+ if (!isSupportedProtocol(protocol)) {
114
+ return;
115
+ }
116
+ const payload = (options.formatEnvelope.payload ?? {});
117
+ const spec = INBOUND_BRIDGE_SPECS[protocol];
118
+ applyBridgePolicy(spec, {
119
+ requestId: options.adapterContext.requestId,
120
+ chatEnvelope: options.chatEnvelope,
121
+ payload,
122
+ adapterContext: options.adapterContext
123
+ });
124
+ if (protocol === 'gemini-chat') {
125
+ // Keep parity: gemini mapper rebuilds toolOutputs after inbound policy adjustments.
126
+ rebuildGeminiToolOutputsFromMessages(options.chatEnvelope);
127
+ }
128
+ }
129
+ export function applyHubOperationTableOutboundPreMap(options) {
130
+ if (options.protocol !== 'anthropic-messages' && options.protocol !== 'gemini-chat') {
131
+ return Promise.resolve();
132
+ }
133
+ // Ensure tool_use/tool_result ordering and per-session history for protocols that depend on it.
134
+ return (async () => {
135
+ try {
136
+ const { applyToolSessionCompat } = await import('../tool-session-compat.js');
137
+ await applyToolSessionCompat(options.chatEnvelope, options.adapterContext);
138
+ }
139
+ catch {
140
+ // best-effort compat; never block outbound mapping
141
+ }
142
+ })();
143
+ }
144
+ export function applyHubOperationTableOutboundPostMap(options) {
145
+ const protocol = options.formatEnvelope.protocol;
146
+ if (!isSupportedProtocol(protocol)) {
147
+ return;
148
+ }
149
+ const payload = (options.formatEnvelope.payload ?? {});
150
+ const spec = OUTBOUND_BRIDGE_SPECS[protocol];
151
+ applyBridgePolicy(spec, {
152
+ requestId: options.adapterContext.requestId,
153
+ chatEnvelope: options.chatEnvelope,
154
+ payload,
155
+ adapterContext: options.adapterContext
156
+ });
157
+ options.formatEnvelope.payload = payload;
158
+ }
@@ -0,0 +1,8 @@
1
+ import type { SemanticMapper } from '../../format-adapters/index.js';
2
+ import type { AdapterContext, ChatEnvelope } from '../../types/chat-envelope.js';
3
+ import type { FormatEnvelope } from '../../types/format-envelope.js';
4
+ export declare class AnthropicSemanticMapper implements SemanticMapper {
5
+ private readonly chatMapper;
6
+ toChat(format: FormatEnvelope, ctx: AdapterContext): Promise<ChatEnvelope>;
7
+ fromChat(chat: ChatEnvelope, ctx: AdapterContext): Promise<FormatEnvelope>;
8
+ }
@@ -0,0 +1,303 @@
1
+ import { isJsonObject, jsonClone } from '../../types/json.js';
2
+ import { buildOpenAIChatFromAnthropic, buildAnthropicRequestFromOpenAIChat } from '../../../codecs/anthropic-openai-codec.js';
3
+ import { encodeMetadataPassthrough, extractMetadataPassthrough } from '../../../shared/metadata-passthrough.js';
4
+ import { buildAnthropicToolAliasMap } from '../../../shared/anthropic-message-utils.js';
5
+ import { ChatSemanticMapper } from './chat-mapper.js';
6
+ import { ensureProtocolState, getProtocolState } from '../../../shared/protocol-state.js';
7
+ const ANTHROPIC_PARAMETER_KEYS = [
8
+ 'model',
9
+ 'temperature',
10
+ 'top_p',
11
+ 'top_k',
12
+ 'max_tokens',
13
+ 'max_output_tokens',
14
+ 'metadata',
15
+ 'stream',
16
+ 'tool_choice'
17
+ ];
18
+ const ANTHROPIC_TOP_LEVEL_FIELDS = new Set([
19
+ 'model',
20
+ 'messages',
21
+ 'tools',
22
+ 'system',
23
+ 'stop_sequences',
24
+ 'temperature',
25
+ 'top_p',
26
+ 'top_k',
27
+ 'max_tokens',
28
+ 'max_output_tokens',
29
+ 'metadata',
30
+ 'stream',
31
+ 'tool_choice'
32
+ ]);
33
+ const PASSTHROUGH_METADATA_PREFIX = 'rcc_passthrough_';
34
+ const PASSTHROUGH_PARAMETERS = ['tool_choice'];
35
+ function ensureSemantics(chat) {
36
+ if (!chat.semantics || typeof chat.semantics !== 'object') {
37
+ chat.semantics = {};
38
+ }
39
+ return chat.semantics;
40
+ }
41
+ function ensureAnthropicSemanticsNode(chat) {
42
+ const semantics = ensureSemantics(chat);
43
+ if (!semantics.anthropic || !isJsonObject(semantics.anthropic)) {
44
+ semantics.anthropic = {};
45
+ }
46
+ return semantics.anthropic;
47
+ }
48
+ function markExplicitEmptyTools(chat) {
49
+ const semantics = ensureSemantics(chat);
50
+ if (!semantics.tools || !isJsonObject(semantics.tools)) {
51
+ semantics.tools = {};
52
+ }
53
+ semantics.tools.explicitEmpty = true;
54
+ }
55
+ function readAnthropicSemantics(chat) {
56
+ if (!chat.semantics || typeof chat.semantics !== 'object') {
57
+ return undefined;
58
+ }
59
+ const node = chat.semantics.anthropic;
60
+ return node && isJsonObject(node) ? node : undefined;
61
+ }
62
+ function hasExplicitEmptyToolsSemantics(chat) {
63
+ if (!chat.semantics || typeof chat.semantics !== 'object') {
64
+ return false;
65
+ }
66
+ const toolsNode = chat.semantics.tools;
67
+ if (!toolsNode || !isJsonObject(toolsNode)) {
68
+ return false;
69
+ }
70
+ return Boolean(toolsNode.explicitEmpty);
71
+ }
72
+ function sanitizeAnthropicPayload(payload) {
73
+ for (const key of Object.keys(payload)) {
74
+ if (!ANTHROPIC_TOP_LEVEL_FIELDS.has(key)) {
75
+ delete payload[key];
76
+ }
77
+ }
78
+ return payload;
79
+ }
80
+ function collectParameters(payload) {
81
+ const params = {};
82
+ for (const key of ANTHROPIC_PARAMETER_KEYS) {
83
+ if (payload[key] !== undefined) {
84
+ params[key] = payload[key];
85
+ }
86
+ }
87
+ if (Array.isArray(payload.stop_sequences)) {
88
+ params.stop = payload.stop_sequences;
89
+ }
90
+ return Object.keys(params).length ? params : undefined;
91
+ }
92
+ function cloneAnthropicSystemBlocks(value) {
93
+ if (value === undefined || value === null) {
94
+ return undefined;
95
+ }
96
+ const blocks = Array.isArray(value) ? value : [value];
97
+ if (!blocks.length) {
98
+ return undefined;
99
+ }
100
+ return blocks.map((entry) => jsonClone(entry));
101
+ }
102
+ export class AnthropicSemanticMapper {
103
+ chatMapper = new ChatSemanticMapper();
104
+ async toChat(format, ctx) {
105
+ const payload = (format.payload ?? {});
106
+ const missing = [];
107
+ if (!Array.isArray(payload.messages))
108
+ missing.push({ path: 'messages', reason: 'absent' });
109
+ if (typeof payload.model !== 'string')
110
+ missing.push({ path: 'model', reason: 'absent' });
111
+ const passthrough = extractMetadataPassthrough(payload.metadata, {
112
+ prefix: PASSTHROUGH_METADATA_PREFIX,
113
+ keys: PASSTHROUGH_PARAMETERS
114
+ });
115
+ const openaiPayload = buildOpenAIChatFromAnthropic(payload);
116
+ const canonicalContext = {
117
+ ...ctx,
118
+ providerProtocol: 'openai-chat',
119
+ entryEndpoint: ctx.entryEndpoint || '/v1/chat/completions'
120
+ };
121
+ const chatEnvelope = await this.chatMapper.toChat({
122
+ protocol: 'openai-chat',
123
+ direction: 'request',
124
+ payload: openaiPayload
125
+ }, canonicalContext);
126
+ const metadata = chatEnvelope.metadata ?? { context: canonicalContext };
127
+ chatEnvelope.metadata = metadata;
128
+ metadata.context = canonicalContext;
129
+ let semanticsNode;
130
+ const resolveExtraFields = () => {
131
+ if (!isJsonObject(metadata.extraFields)) {
132
+ metadata.extraFields = {};
133
+ }
134
+ return metadata.extraFields;
135
+ };
136
+ const protocolState = ensureProtocolState(metadata, 'anthropic');
137
+ const systemBlocks = cloneAnthropicSystemBlocks(payload.system);
138
+ if (systemBlocks) {
139
+ protocolState.systemBlocks = systemBlocks;
140
+ semanticsNode = semanticsNode ?? ensureAnthropicSemanticsNode(chatEnvelope);
141
+ semanticsNode.systemBlocks = jsonClone(systemBlocks);
142
+ }
143
+ if (payload.tools && Array.isArray(payload.tools) && payload.tools.length === 0) {
144
+ metadata.toolsFieldPresent = true;
145
+ resolveExtraFields().toolsFieldPresent = true;
146
+ markExplicitEmptyTools(chatEnvelope);
147
+ }
148
+ const aliasMap = buildAnthropicToolAliasMap(payload.tools);
149
+ if (aliasMap) {
150
+ const extraFields = resolveExtraFields();
151
+ ctx.anthropicToolNameMap = aliasMap;
152
+ canonicalContext.anthropicToolNameMap = aliasMap;
153
+ metadata.anthropicToolNameMap = aliasMap;
154
+ extraFields.anthropicToolNameMap = aliasMap;
155
+ semanticsNode = semanticsNode ?? ensureAnthropicSemanticsNode(chatEnvelope);
156
+ semanticsNode.toolAliasMap = jsonClone(aliasMap);
157
+ }
158
+ if (Array.isArray(payload.messages) && payload.messages.length) {
159
+ const shapes = payload.messages.map((entry) => {
160
+ if (!entry || typeof entry !== 'object') {
161
+ return 'unknown';
162
+ }
163
+ const rawContent = entry.content;
164
+ if (typeof rawContent === 'string') {
165
+ return 'string';
166
+ }
167
+ if (Array.isArray(rawContent)) {
168
+ return 'array';
169
+ }
170
+ if (rawContent === null || rawContent === undefined) {
171
+ return 'null';
172
+ }
173
+ return typeof rawContent;
174
+ });
175
+ const extraFields = resolveExtraFields();
176
+ const mirrorNode = extraFields.anthropicMirror && typeof extraFields.anthropicMirror === 'object'
177
+ ? extraFields.anthropicMirror
178
+ : {};
179
+ mirrorNode.messageContentShape = shapes;
180
+ extraFields.anthropicMirror = mirrorNode;
181
+ semanticsNode = semanticsNode ?? ensureAnthropicSemanticsNode(chatEnvelope);
182
+ semanticsNode.mirror = jsonClone(mirrorNode);
183
+ }
184
+ if (missing.length) {
185
+ metadata.missingFields = Array.isArray(metadata.missingFields)
186
+ ? [...metadata.missingFields, ...missing]
187
+ : missing;
188
+ }
189
+ const providerMetadata = passthrough.metadata ??
190
+ (payload.metadata && isJsonObject(payload.metadata) ? jsonClone(payload.metadata) : undefined);
191
+ if (providerMetadata) {
192
+ metadata.providerMetadata = providerMetadata;
193
+ semanticsNode = semanticsNode ?? ensureAnthropicSemanticsNode(chatEnvelope);
194
+ semanticsNode.providerMetadata = jsonClone(providerMetadata);
195
+ }
196
+ const mergedParameters = { ...(chatEnvelope.parameters ?? {}) };
197
+ const mergeParameters = (source) => {
198
+ if (!source) {
199
+ return;
200
+ }
201
+ for (const [key, value] of Object.entries(source)) {
202
+ if (mergedParameters[key] !== undefined) {
203
+ continue;
204
+ }
205
+ mergedParameters[key] = jsonClone(value);
206
+ }
207
+ };
208
+ mergeParameters(collectParameters(payload));
209
+ if (providerMetadata) {
210
+ mergedParameters.metadata = jsonClone(providerMetadata);
211
+ }
212
+ if (passthrough.passthrough) {
213
+ for (const [key, value] of Object.entries(passthrough.passthrough)) {
214
+ mergedParameters[key] = jsonClone(value);
215
+ }
216
+ }
217
+ if (Object.keys(mergedParameters).length) {
218
+ chatEnvelope.parameters = mergedParameters;
219
+ }
220
+ else {
221
+ delete chatEnvelope.parameters;
222
+ }
223
+ return chatEnvelope;
224
+ }
225
+ async fromChat(chat, ctx) {
226
+ const model = chat.parameters?.model;
227
+ if (typeof model !== 'string' || !model.trim()) {
228
+ throw new Error('ChatEnvelope.parameters.model is required for anthropic-messages outbound conversion');
229
+ }
230
+ const baseRequest = {
231
+ model,
232
+ messages: chat.messages,
233
+ tools: chat.tools
234
+ };
235
+ const semanticsNode = readAnthropicSemantics(chat);
236
+ const explicitEmptyTools = (chat.metadata?.toolsFieldPresent === true) || hasExplicitEmptyToolsSemantics(chat);
237
+ const trimmedParameters = chat.parameters && typeof chat.parameters === 'object' ? chat.parameters : undefined;
238
+ if (trimmedParameters) {
239
+ for (const [key, value] of Object.entries(trimmedParameters)) {
240
+ if (ANTHROPIC_TOP_LEVEL_FIELDS.has(key) || key === 'stop') {
241
+ if (key === 'messages' || key === 'tools') {
242
+ continue;
243
+ }
244
+ baseRequest[key] = value;
245
+ }
246
+ }
247
+ }
248
+ const passthroughMetadata = encodeMetadataPassthrough(chat.parameters, {
249
+ prefix: PASSTHROUGH_METADATA_PREFIX,
250
+ keys: PASSTHROUGH_PARAMETERS
251
+ });
252
+ if (passthroughMetadata) {
253
+ const rawMetadata = baseRequest.metadata;
254
+ const existingMetadata = isJsonObject(rawMetadata)
255
+ ? jsonClone(rawMetadata)
256
+ : {};
257
+ for (const [key, value] of Object.entries(passthroughMetadata)) {
258
+ existingMetadata[key] = value;
259
+ }
260
+ baseRequest.metadata = existingMetadata;
261
+ }
262
+ if (baseRequest.max_output_tokens && !baseRequest.max_tokens) {
263
+ baseRequest.max_tokens = baseRequest.max_output_tokens;
264
+ }
265
+ // 出站阶段不再直接透传其它协议的 providerMetadata,避免跨协议打洞;
266
+ // Anthropic 自身入口的 metadata 已在入站阶段通过 collectParameters/encodeMetadataPassthrough
267
+ // 按白名单收集,这里仅依赖这些显式映射结果。
268
+ if (explicitEmptyTools && (!Array.isArray(chat.tools) || chat.tools.length === 0)) {
269
+ baseRequest.tools = [];
270
+ }
271
+ const protocolState = getProtocolState(chat.metadata, 'anthropic');
272
+ if (protocolState?.systemBlocks !== undefined) {
273
+ baseRequest.system = jsonClone(protocolState.systemBlocks);
274
+ }
275
+ else if (semanticsNode?.systemBlocks !== undefined) {
276
+ baseRequest.system = jsonClone(semanticsNode.systemBlocks);
277
+ }
278
+ if (chat.metadata &&
279
+ typeof chat.metadata === 'object' &&
280
+ chat.metadata.extraFields &&
281
+ typeof chat.metadata.extraFields === 'object' &&
282
+ chat.metadata.extraFields.anthropicMirror) {
283
+ baseRequest.__anthropicMirror = jsonClone(chat.metadata.extraFields.anthropicMirror ?? {});
284
+ }
285
+ else if (semanticsNode?.mirror && isJsonObject(semanticsNode.mirror)) {
286
+ baseRequest.__anthropicMirror = jsonClone(semanticsNode.mirror);
287
+ }
288
+ const payloadSource = buildAnthropicRequestFromOpenAIChat(baseRequest);
289
+ const payload = sanitizeAnthropicPayload(JSON.parse(JSON.stringify(payloadSource)));
290
+ if (chat.metadata?.toolsFieldPresent && (!Array.isArray(chat.tools) || chat.tools.length === 0)) {
291
+ payload.tools = [];
292
+ }
293
+ sanitizeAnthropicPayload(payload);
294
+ return {
295
+ protocol: 'anthropic-messages',
296
+ direction: 'response',
297
+ payload,
298
+ meta: {
299
+ context: ctx
300
+ }
301
+ };
302
+ }
303
+ }
@@ -0,0 +1,8 @@
1
+ import type { SemanticMapper } from '../../format-adapters/index.js';
2
+ import type { AdapterContext, ChatEnvelope } from '../../types/chat-envelope.js';
3
+ import type { FormatEnvelope } from '../../types/format-envelope.js';
4
+ export declare function maybeAugmentApplyPatchErrorContent(content: string, toolName?: string): string;
5
+ export declare class ChatSemanticMapper implements SemanticMapper {
6
+ toChat(format: FormatEnvelope, ctx: AdapterContext): Promise<ChatEnvelope>;
7
+ fromChat(chat: ChatEnvelope, ctx: AdapterContext): Promise<FormatEnvelope>;
8
+ }