@jsonstudio/llms 0.4.4 → 0.4.6
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/conversion/codec-registry.js +11 -1
- package/dist/conversion/codecs/anthropic-openai-codec.d.ts +13 -0
- package/dist/conversion/codecs/anthropic-openai-codec.js +18 -473
- package/dist/conversion/codecs/gemini-openai-codec.js +91 -48
- package/dist/conversion/codecs/responses-openai-codec.js +9 -2
- package/dist/conversion/hub/format-adapters/anthropic-format-adapter.js +3 -0
- package/dist/conversion/hub/format-adapters/chat-format-adapter.js +3 -0
- package/dist/conversion/hub/format-adapters/gemini-format-adapter.js +3 -0
- package/dist/conversion/hub/format-adapters/responses-format-adapter.d.ts +19 -0
- package/dist/conversion/hub/format-adapters/responses-format-adapter.js +9 -0
- package/dist/conversion/hub/node-support.js +3 -1
- package/dist/conversion/hub/pipeline/hub-pipeline.js +37 -32
- package/dist/conversion/hub/response/provider-response.js +1 -1
- package/dist/conversion/hub/response/response-mappers.js +1 -1
- package/dist/conversion/hub/response/response-runtime.js +109 -10
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +70 -156
- package/dist/conversion/hub/semantic-mappers/chat-mapper.js +63 -52
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +76 -143
- package/dist/conversion/hub/semantic-mappers/responses-mapper.js +40 -160
- package/dist/conversion/hub/standardized-bridge.js +3 -0
- package/dist/conversion/hub/tool-governance/rules.js +2 -2
- package/dist/conversion/index.d.ts +5 -0
- package/dist/conversion/index.js +5 -0
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.d.ts +12 -0
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +100 -0
- package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.d.ts +15 -0
- package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.js +174 -0
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.d.ts +14 -0
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.js +166 -0
- package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.d.ts +13 -0
- package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.js +66 -0
- package/dist/conversion/pipeline/hooks/adapter-context.d.ts +7 -0
- package/dist/conversion/pipeline/hooks/adapter-context.js +18 -0
- package/dist/conversion/pipeline/hooks/protocol-hooks.d.ts +67 -0
- package/dist/conversion/pipeline/hooks/protocol-hooks.js +1 -0
- package/dist/conversion/pipeline/index.d.ts +35 -0
- package/dist/conversion/pipeline/index.js +103 -0
- package/dist/conversion/pipeline/meta/meta-bag.d.ts +20 -0
- package/dist/conversion/pipeline/meta/meta-bag.js +81 -0
- package/dist/conversion/pipeline/schema/canonical-chat.d.ts +18 -0
- package/dist/conversion/pipeline/schema/canonical-chat.js +1 -0
- package/dist/conversion/pipeline/schema/index.d.ts +1 -0
- package/dist/conversion/pipeline/schema/index.js +1 -0
- package/dist/conversion/responses/responses-openai-bridge.d.ts +48 -0
- package/dist/conversion/responses/responses-openai-bridge.js +157 -1146
- package/dist/conversion/shared/anthropic-message-utils.d.ts +12 -0
- package/dist/conversion/shared/anthropic-message-utils.js +587 -0
- package/dist/conversion/shared/bridge-actions.d.ts +39 -0
- package/dist/conversion/shared/bridge-actions.js +709 -0
- package/dist/conversion/shared/bridge-conversation-store.d.ts +41 -0
- package/dist/conversion/shared/bridge-conversation-store.js +279 -0
- package/dist/conversion/shared/bridge-id-utils.d.ts +7 -0
- package/dist/conversion/shared/bridge-id-utils.js +42 -0
- package/dist/conversion/shared/bridge-instructions.d.ts +1 -0
- package/dist/conversion/shared/bridge-instructions.js +113 -0
- package/dist/conversion/shared/bridge-message-types.d.ts +39 -0
- package/dist/conversion/shared/bridge-message-types.js +1 -0
- package/dist/conversion/shared/bridge-message-utils.d.ts +22 -0
- package/dist/conversion/shared/bridge-message-utils.js +473 -0
- package/dist/conversion/shared/bridge-metadata.d.ts +1 -0
- package/dist/conversion/shared/bridge-metadata.js +1 -0
- package/dist/conversion/shared/bridge-policies.d.ts +18 -0
- package/dist/conversion/shared/bridge-policies.js +276 -0
- package/dist/conversion/shared/bridge-request-adapter.d.ts +28 -0
- package/dist/conversion/shared/bridge-request-adapter.js +430 -0
- package/dist/conversion/shared/chat-output-normalizer.d.ts +4 -0
- package/dist/conversion/shared/chat-output-normalizer.js +56 -0
- package/dist/conversion/shared/chat-request-filters.js +24 -1
- package/dist/conversion/shared/gemini-tool-utils.d.ts +5 -0
- package/dist/conversion/shared/gemini-tool-utils.js +130 -0
- package/dist/conversion/shared/metadata-passthrough.d.ts +11 -0
- package/dist/conversion/shared/metadata-passthrough.js +57 -0
- package/dist/conversion/shared/output-content-normalizer.d.ts +12 -0
- package/dist/conversion/shared/output-content-normalizer.js +119 -0
- package/dist/conversion/shared/reasoning-normalizer.d.ts +21 -0
- package/dist/conversion/shared/reasoning-normalizer.js +368 -0
- package/dist/conversion/shared/reasoning-tool-normalizer.d.ts +12 -0
- package/dist/conversion/shared/reasoning-tool-normalizer.js +132 -0
- package/dist/conversion/shared/reasoning-tool-parser.d.ts +10 -0
- package/dist/conversion/shared/reasoning-tool-parser.js +95 -0
- package/dist/conversion/shared/reasoning-utils.d.ts +2 -0
- package/dist/conversion/shared/reasoning-utils.js +42 -0
- package/dist/conversion/shared/responses-conversation-store.js +5 -11
- package/dist/conversion/shared/responses-message-utils.d.ts +15 -0
- package/dist/conversion/shared/responses-message-utils.js +206 -0
- package/dist/conversion/shared/responses-output-builder.d.ts +15 -0
- package/dist/conversion/shared/responses-output-builder.js +179 -0
- package/dist/conversion/shared/responses-output-utils.d.ts +7 -0
- package/dist/conversion/shared/responses-output-utils.js +108 -0
- package/dist/conversion/shared/responses-request-adapter.d.ts +28 -0
- package/dist/conversion/shared/responses-request-adapter.js +9 -40
- package/dist/conversion/shared/responses-response-utils.d.ts +3 -0
- package/dist/conversion/shared/responses-response-utils.js +209 -0
- package/dist/conversion/shared/responses-tool-utils.d.ts +12 -0
- package/dist/conversion/shared/responses-tool-utils.js +90 -0
- package/dist/conversion/shared/responses-types.d.ts +33 -0
- package/dist/conversion/shared/responses-types.js +1 -0
- package/dist/conversion/shared/tool-call-utils.d.ts +11 -0
- package/dist/conversion/shared/tool-call-utils.js +56 -0
- package/dist/conversion/shared/tool-governor.js +5 -0
- package/dist/conversion/shared/tool-mapping.d.ts +19 -0
- package/dist/conversion/shared/tool-mapping.js +124 -0
- package/dist/conversion/shared/tool-normalizers.d.ts +4 -0
- package/dist/conversion/shared/tool-normalizers.js +84 -0
- package/dist/router/virtual-router/bootstrap.js +18 -3
- package/dist/router/virtual-router/provider-registry.js +4 -2
- package/dist/router/virtual-router/types.d.ts +212 -0
- package/dist/sse/index.d.ts +38 -2
- package/dist/sse/index.js +27 -0
- package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +14 -0
- package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.js +106 -73
- package/dist/sse/json-to-sse/chat-json-to-sse-converter.js +6 -2
- package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +14 -0
- package/dist/sse/json-to-sse/gemini-json-to-sse-converter.js +99 -0
- package/dist/sse/json-to-sse/index.d.ts +7 -0
- package/dist/sse/json-to-sse/index.js +2 -0
- package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +13 -0
- package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.js +150 -0
- package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +39 -0
- package/dist/sse/json-to-sse/sequencers/chat-sequencer.js +49 -3
- package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +10 -0
- package/dist/sse/json-to-sse/sequencers/gemini-sequencer.js +95 -0
- package/dist/sse/json-to-sse/sequencers/responses-sequencer.js +31 -5
- package/dist/sse/registry/sse-codec-registry.d.ts +32 -0
- package/dist/sse/registry/sse-codec-registry.js +30 -1
- package/dist/sse/shared/reasoning-dispatcher.d.ts +10 -0
- package/dist/sse/shared/reasoning-dispatcher.js +25 -0
- package/dist/sse/shared/responses-output-normalizer.d.ts +12 -0
- package/dist/sse/shared/responses-output-normalizer.js +45 -0
- package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +2 -0
- package/dist/sse/shared/serializers/anthropic-event-serializer.js +9 -0
- package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +2 -0
- package/dist/sse/shared/serializers/gemini-event-serializer.js +5 -0
- package/dist/sse/shared/serializers/index.d.ts +41 -0
- package/dist/sse/shared/serializers/index.js +2 -0
- package/dist/sse/shared/writer.d.ts +127 -0
- package/dist/sse/shared/writer.js +37 -1
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +11 -0
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +92 -127
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +16 -0
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +151 -0
- package/dist/sse/sse-to-json/builders/response-builder.d.ts +165 -0
- package/dist/sse/sse-to-json/builders/response-builder.js +27 -6
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +114 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +79 -3
- package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +13 -0
- package/dist/sse/sse-to-json/gemini-sse-to-json-converter.js +160 -0
- package/dist/sse/sse-to-json/index.d.ts +7 -0
- package/dist/sse/sse-to-json/index.js +2 -0
- package/dist/sse/sse-to-json/parsers/sse-parser.js +53 -1
- package/dist/sse/types/anthropic-types.d.ts +170 -0
- package/dist/sse/types/anthropic-types.js +8 -5
- package/dist/sse/types/chat-types.d.ts +10 -0
- package/dist/sse/types/chat-types.js +2 -1
- package/dist/sse/types/core-interfaces.d.ts +1 -1
- package/dist/sse/types/gemini-types.d.ts +116 -0
- package/dist/sse/types/gemini-types.js +5 -0
- package/dist/sse/types/index.d.ts +5 -2
- package/dist/sse/types/index.js +2 -0
- package/package.json +1 -1
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { isJsonObject, jsonClone } from '../types/json.js';
|
|
2
2
|
import { buildOpenAIChatFromAnthropic, buildAnthropicRequestFromOpenAIChat } from '../../codecs/anthropic-openai-codec.js';
|
|
3
|
+
import { createBridgeActionState, runBridgeActionPipeline } from '../../shared/bridge-actions.js';
|
|
4
|
+
import { resolveBridgePolicy, resolvePolicyActions } from '../../shared/bridge-policies.js';
|
|
5
|
+
import { encodeMetadataPassthrough, extractMetadataPassthrough } from '../../shared/metadata-passthrough.js';
|
|
6
|
+
import { mapAnthropicToolsToChat } from '../../shared/anthropic-message-utils.js';
|
|
3
7
|
const ANTHROPIC_PARAMETER_KEYS = [
|
|
4
8
|
'model',
|
|
5
9
|
'temperature',
|
|
@@ -28,55 +32,6 @@ const ANTHROPIC_TOP_LEVEL_FIELDS = new Set([
|
|
|
28
32
|
]);
|
|
29
33
|
const PASSTHROUGH_METADATA_PREFIX = 'rcc_passthrough_';
|
|
30
34
|
const PASSTHROUGH_PARAMETERS = ['tool_choice'];
|
|
31
|
-
function encodePassthroughParameters(chat) {
|
|
32
|
-
if (!chat.parameters)
|
|
33
|
-
return undefined;
|
|
34
|
-
const encoded = {};
|
|
35
|
-
for (const key of PASSTHROUGH_PARAMETERS) {
|
|
36
|
-
const value = chat.parameters[key];
|
|
37
|
-
if (value === undefined)
|
|
38
|
-
continue;
|
|
39
|
-
try {
|
|
40
|
-
encoded[`${PASSTHROUGH_METADATA_PREFIX}${key}`] = JSON.stringify(value);
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return Object.keys(encoded).length ? encoded : undefined;
|
|
47
|
-
}
|
|
48
|
-
function extractPassthroughParameters(metadataField) {
|
|
49
|
-
if (!isJsonObject(metadataField))
|
|
50
|
-
return {};
|
|
51
|
-
const cloned = jsonClone(metadataField);
|
|
52
|
-
const passthrough = {};
|
|
53
|
-
let metadataMutated = false;
|
|
54
|
-
let hasPassthrough = false;
|
|
55
|
-
for (const key of Object.keys(cloned)) {
|
|
56
|
-
if (!key.startsWith(PASSTHROUGH_METADATA_PREFIX))
|
|
57
|
-
continue;
|
|
58
|
-
const paramKey = key.slice(PASSTHROUGH_METADATA_PREFIX.length);
|
|
59
|
-
if (!PASSTHROUGH_PARAMETERS.includes(paramKey))
|
|
60
|
-
continue;
|
|
61
|
-
const rawValue = cloned[key];
|
|
62
|
-
if (typeof rawValue !== 'string')
|
|
63
|
-
continue;
|
|
64
|
-
try {
|
|
65
|
-
const parsed = rawValue ? JSON.parse(rawValue) : undefined;
|
|
66
|
-
passthrough[paramKey] = parsed;
|
|
67
|
-
delete cloned[key];
|
|
68
|
-
metadataMutated = true;
|
|
69
|
-
hasPassthrough = true;
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return {
|
|
76
|
-
metadata: metadataMutated ? (Object.keys(cloned).length ? cloned : undefined) : cloned,
|
|
77
|
-
passthrough: hasPassthrough ? passthrough : undefined
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
35
|
function collectParameters(payload) {
|
|
81
36
|
const params = {};
|
|
82
37
|
for (const key of ANTHROPIC_PARAMETER_KEYS) {
|
|
@@ -89,34 +44,6 @@ function collectParameters(payload) {
|
|
|
89
44
|
}
|
|
90
45
|
return Object.keys(params).length ? params : undefined;
|
|
91
46
|
}
|
|
92
|
-
function normalizeAnthropicTools(rawTools, missing) {
|
|
93
|
-
if (!Array.isArray(rawTools) || rawTools.length === 0)
|
|
94
|
-
return undefined;
|
|
95
|
-
const tools = [];
|
|
96
|
-
rawTools.forEach((entry, index) => {
|
|
97
|
-
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
98
|
-
missing.push({ path: `tools[${index}]`, reason: 'invalid_entry', originalValue: jsonClone(entry) });
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
const nameValue = entry.name;
|
|
102
|
-
if (typeof nameValue !== 'string' || !nameValue.trim())
|
|
103
|
-
return;
|
|
104
|
-
const desc = entry.description;
|
|
105
|
-
const schema = entry.input_schema;
|
|
106
|
-
const parameters = schema && typeof schema === 'object' && !Array.isArray(schema)
|
|
107
|
-
? schema
|
|
108
|
-
: { type: 'object', properties: {} };
|
|
109
|
-
tools.push({
|
|
110
|
-
type: 'function',
|
|
111
|
-
function: {
|
|
112
|
-
name: nameValue,
|
|
113
|
-
description: typeof desc === 'string' ? desc : undefined,
|
|
114
|
-
parameters
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
return tools.length ? tools : undefined;
|
|
119
|
-
}
|
|
120
47
|
function normalizeToolContent(content) {
|
|
121
48
|
if (typeof content === 'string')
|
|
122
49
|
return content;
|
|
@@ -149,43 +76,6 @@ function collectToolOutputsFromMessages(messages, missing) {
|
|
|
149
76
|
});
|
|
150
77
|
return outputs.length ? outputs : undefined;
|
|
151
78
|
}
|
|
152
|
-
function collectExtraFields(payload) {
|
|
153
|
-
const extras = {};
|
|
154
|
-
for (const key of Object.keys(payload)) {
|
|
155
|
-
if (ANTHROPIC_TOP_LEVEL_FIELDS.has(key))
|
|
156
|
-
continue;
|
|
157
|
-
extras[key] = payload[key];
|
|
158
|
-
}
|
|
159
|
-
return Object.keys(extras).length ? extras : undefined;
|
|
160
|
-
}
|
|
161
|
-
function collectSystemSegments(systemField) {
|
|
162
|
-
if (!systemField)
|
|
163
|
-
return [];
|
|
164
|
-
const flatten = (value) => {
|
|
165
|
-
if (typeof value === 'string')
|
|
166
|
-
return value;
|
|
167
|
-
if (Array.isArray(value)) {
|
|
168
|
-
return value.map(v => flatten(v)).filter(Boolean).join('\n');
|
|
169
|
-
}
|
|
170
|
-
if (value && typeof value === 'object') {
|
|
171
|
-
const obj = value;
|
|
172
|
-
if (typeof obj.text === 'string')
|
|
173
|
-
return obj.text;
|
|
174
|
-
if (typeof obj.content === 'string')
|
|
175
|
-
return obj.content;
|
|
176
|
-
if (Array.isArray(obj.content))
|
|
177
|
-
return obj.content.map(v => flatten(v)).join('\n');
|
|
178
|
-
}
|
|
179
|
-
return '';
|
|
180
|
-
};
|
|
181
|
-
const text = flatten(systemField).trim();
|
|
182
|
-
return text ? [text] : [];
|
|
183
|
-
}
|
|
184
|
-
function systemSegmentsToAnthropicBlocks(segments) {
|
|
185
|
-
if (!segments || !segments.length)
|
|
186
|
-
return undefined;
|
|
187
|
-
return segments.map(seg => ({ type: 'text', text: seg }));
|
|
188
|
-
}
|
|
189
79
|
export class AnthropicSemanticMapper {
|
|
190
80
|
async toChat(format, ctx) {
|
|
191
81
|
const payload = (format.payload ?? {});
|
|
@@ -194,38 +84,55 @@ export class AnthropicSemanticMapper {
|
|
|
194
84
|
missing.push({ path: 'messages', reason: 'absent' });
|
|
195
85
|
if (typeof payload.model !== 'string')
|
|
196
86
|
missing.push({ path: 'model', reason: 'absent' });
|
|
197
|
-
const
|
|
87
|
+
const openaiPayload = buildOpenAIChatFromAnthropic(payload);
|
|
88
|
+
const messages = Array.isArray(openaiPayload.messages)
|
|
89
|
+
? openaiPayload.messages
|
|
90
|
+
: [];
|
|
198
91
|
const toolOutputs = collectToolOutputsFromMessages(messages, missing);
|
|
199
|
-
const tools =
|
|
92
|
+
const tools = mapAnthropicToolsToChat(payload.tools, missing);
|
|
200
93
|
let parameters = collectParameters(payload);
|
|
201
|
-
const passthrough =
|
|
94
|
+
const passthrough = extractMetadataPassthrough(payload.metadata, {
|
|
95
|
+
prefix: PASSTHROUGH_METADATA_PREFIX,
|
|
96
|
+
keys: PASSTHROUGH_PARAMETERS
|
|
97
|
+
});
|
|
202
98
|
if (passthrough.passthrough) {
|
|
203
99
|
parameters = { ...(parameters || {}), ...passthrough.passthrough };
|
|
204
100
|
}
|
|
205
|
-
const systemSegments = collectSystemSegments(payload.system);
|
|
206
101
|
const metadata = { context: ctx };
|
|
207
|
-
if (systemSegments.length) {
|
|
208
|
-
metadata.systemInstructions = systemSegments;
|
|
209
|
-
}
|
|
210
|
-
if (payload.system !== undefined) {
|
|
211
|
-
metadata.rawSystem = jsonClone(payload.system);
|
|
212
|
-
}
|
|
213
102
|
if (payload.tools && Array.isArray(payload.tools) && payload.tools.length === 0) {
|
|
214
103
|
metadata.toolsFieldPresent = true;
|
|
215
104
|
}
|
|
216
105
|
if (missing.length) {
|
|
217
106
|
metadata.missingFields = missing;
|
|
218
107
|
}
|
|
219
|
-
const extraFields = collectExtraFields(payload);
|
|
220
|
-
if (extraFields) {
|
|
221
|
-
metadata.extraFields = extraFields;
|
|
222
|
-
}
|
|
223
108
|
if (passthrough.metadata) {
|
|
224
109
|
metadata.providerMetadata = passthrough.metadata;
|
|
225
110
|
}
|
|
226
111
|
else if (payload.metadata && isJsonObject(payload.metadata)) {
|
|
227
112
|
metadata.providerMetadata = jsonClone(payload.metadata);
|
|
228
113
|
}
|
|
114
|
+
try {
|
|
115
|
+
const bridgePolicy = resolveBridgePolicy({ protocol: 'anthropic-messages' });
|
|
116
|
+
const actions = resolvePolicyActions(bridgePolicy, 'request_inbound');
|
|
117
|
+
if (actions?.length) {
|
|
118
|
+
const actionState = createBridgeActionState({
|
|
119
|
+
messages: messages,
|
|
120
|
+
rawRequest: payload,
|
|
121
|
+
metadata: metadata
|
|
122
|
+
});
|
|
123
|
+
runBridgeActionPipeline({
|
|
124
|
+
stage: 'request_inbound',
|
|
125
|
+
actions,
|
|
126
|
+
protocol: bridgePolicy?.protocol ?? 'anthropic-messages',
|
|
127
|
+
moduleType: bridgePolicy?.moduleType ?? 'anthropic-messages',
|
|
128
|
+
requestId: ctx.requestId,
|
|
129
|
+
state: actionState
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// best-effort metadata extraction
|
|
135
|
+
}
|
|
229
136
|
return {
|
|
230
137
|
messages,
|
|
231
138
|
tools,
|
|
@@ -242,10 +149,13 @@ export class AnthropicSemanticMapper {
|
|
|
242
149
|
const baseRequest = {
|
|
243
150
|
...(chat.parameters || {}),
|
|
244
151
|
model,
|
|
245
|
-
messages: chat.
|
|
152
|
+
messages: chat.messages,
|
|
246
153
|
tools: chat.tools
|
|
247
154
|
};
|
|
248
|
-
const passthroughMetadata =
|
|
155
|
+
const passthroughMetadata = encodeMetadataPassthrough(chat.parameters, {
|
|
156
|
+
prefix: PASSTHROUGH_METADATA_PREFIX,
|
|
157
|
+
keys: PASSTHROUGH_PARAMETERS
|
|
158
|
+
});
|
|
249
159
|
if (passthroughMetadata) {
|
|
250
160
|
const rawMetadata = baseRequest.metadata;
|
|
251
161
|
const existingMetadata = isJsonObject(rawMetadata)
|
|
@@ -259,43 +169,47 @@ export class AnthropicSemanticMapper {
|
|
|
259
169
|
if (baseRequest.max_output_tokens && !baseRequest.max_tokens) {
|
|
260
170
|
baseRequest.max_tokens = baseRequest.max_output_tokens;
|
|
261
171
|
}
|
|
262
|
-
const systemSegmentsRaw = Array.isArray(chat.metadata?.systemInstructions)
|
|
263
|
-
? chat.metadata?.systemInstructions
|
|
264
|
-
: undefined;
|
|
265
|
-
const systemSegments = systemSegmentsRaw?.filter((seg) => typeof seg === 'string' && seg.trim().length > 0);
|
|
266
|
-
if (chat.metadata?.rawSystem !== undefined) {
|
|
267
|
-
baseRequest.system = chat.metadata.rawSystem;
|
|
268
|
-
}
|
|
269
|
-
else if (systemSegments && systemSegments.length > 0) {
|
|
270
|
-
baseRequest.system = systemSegmentsToAnthropicBlocks(systemSegments);
|
|
271
|
-
}
|
|
272
172
|
if (isJsonObject(chat.metadata?.providerMetadata)) {
|
|
273
173
|
baseRequest.metadata = jsonClone(chat.metadata?.providerMetadata);
|
|
274
174
|
}
|
|
275
175
|
if (chat.metadata?.toolsFieldPresent && (!Array.isArray(chat.tools) || chat.tools.length === 0)) {
|
|
276
176
|
baseRequest.tools = [];
|
|
277
177
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (baseRequest[key] === undefined) {
|
|
281
|
-
baseRequest[key] = value;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
const payload = buildAnthropicRequestFromOpenAIChat(baseRequest);
|
|
286
|
-
if (chat.metadata?.rawSystem !== undefined) {
|
|
287
|
-
payload.system = chat.metadata.rawSystem;
|
|
288
|
-
}
|
|
178
|
+
const payloadSource = buildAnthropicRequestFromOpenAIChat(baseRequest);
|
|
179
|
+
const payload = JSON.parse(JSON.stringify(payloadSource));
|
|
289
180
|
if (chat.metadata?.toolsFieldPresent && (!Array.isArray(chat.tools) || chat.tools.length === 0)) {
|
|
290
181
|
payload.tools = [];
|
|
291
182
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
183
|
+
try {
|
|
184
|
+
const bridgePolicy = resolveBridgePolicy({ protocol: 'anthropic-messages' });
|
|
185
|
+
const actions = resolvePolicyActions(bridgePolicy, 'request_outbound');
|
|
186
|
+
if (actions?.length) {
|
|
187
|
+
const capturedToolResults = Array.isArray(chat.toolOutputs)
|
|
188
|
+
? chat.toolOutputs.map((entry) => ({
|
|
189
|
+
tool_call_id: entry.tool_call_id,
|
|
190
|
+
output: entry.content,
|
|
191
|
+
name: entry.name
|
|
192
|
+
}))
|
|
193
|
+
: undefined;
|
|
194
|
+
const actionState = createBridgeActionState({
|
|
195
|
+
messages: Array.isArray(payload.messages) ? payload.messages : undefined,
|
|
196
|
+
rawRequest: payload,
|
|
197
|
+
metadata: chat.metadata,
|
|
198
|
+
capturedToolResults
|
|
199
|
+
});
|
|
200
|
+
runBridgeActionPipeline({
|
|
201
|
+
stage: 'request_outbound',
|
|
202
|
+
actions,
|
|
203
|
+
protocol: bridgePolicy?.protocol ?? 'anthropic-messages',
|
|
204
|
+
moduleType: bridgePolicy?.moduleType ?? 'anthropic-messages',
|
|
205
|
+
requestId: ctx.requestId,
|
|
206
|
+
state: actionState
|
|
207
|
+
});
|
|
297
208
|
}
|
|
298
209
|
}
|
|
210
|
+
catch {
|
|
211
|
+
// ignore metadata propagation failures
|
|
212
|
+
}
|
|
299
213
|
return {
|
|
300
214
|
protocol: 'anthropic-messages',
|
|
301
215
|
direction: 'response',
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { isJsonObject, jsonClone } from '../types/json.js';
|
|
2
|
+
import { createBridgeActionState, runBridgeActionPipeline } from '../../shared/bridge-actions.js';
|
|
3
|
+
import { resolveBridgePolicy, resolvePolicyActions } from '../../shared/bridge-policies.js';
|
|
4
|
+
import { normalizeChatMessageContent } from '../../shared/chat-output-normalizer.js';
|
|
2
5
|
const CHAT_PARAMETER_KEYS = [
|
|
3
6
|
'model',
|
|
4
7
|
'temperature',
|
|
@@ -115,6 +118,15 @@ function normalizeChatMessages(raw) {
|
|
|
115
118
|
return;
|
|
116
119
|
}
|
|
117
120
|
const chatMessage = value;
|
|
121
|
+
if (roleValue !== 'system' && roleValue !== 'tool') {
|
|
122
|
+
const normalizedContent = normalizeChatMessageContent(chatMessage.content);
|
|
123
|
+
if (normalizedContent.contentText !== undefined) {
|
|
124
|
+
chatMessage.content = normalizedContent.contentText;
|
|
125
|
+
}
|
|
126
|
+
if (typeof normalizedContent.reasoningText === 'string' && normalizedContent.reasoningText.trim().length) {
|
|
127
|
+
chatMessage.reasoning_content = normalizedContent.reasoningText.trim();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
118
130
|
norm.messages.push(chatMessage);
|
|
119
131
|
if (roleValue === 'system') {
|
|
120
132
|
const segment = flattenSystemContent(chatMessage.content);
|
|
@@ -185,27 +197,10 @@ function extractParameters(body) {
|
|
|
185
197
|
}
|
|
186
198
|
return Object.keys(params).length ? params : undefined;
|
|
187
199
|
}
|
|
188
|
-
function collectExtraFields(payload) {
|
|
189
|
-
const extras = {};
|
|
190
|
-
for (const key of Object.keys(payload)) {
|
|
191
|
-
if (KNOWN_TOP_LEVEL_FIELDS.has(key))
|
|
192
|
-
continue;
|
|
193
|
-
extras[key] = payload[key];
|
|
194
|
-
}
|
|
195
|
-
return Object.keys(extras).length ? extras : undefined;
|
|
196
|
-
}
|
|
197
200
|
export class ChatSemanticMapper {
|
|
198
201
|
async toChat(format, ctx) {
|
|
199
202
|
const payload = (format.payload ?? {});
|
|
200
203
|
const normalized = normalizeChatMessages(payload.messages);
|
|
201
|
-
const rawSystemSentinel = payload.__rcc_raw_system;
|
|
202
|
-
const providerMetadataSentinel = payload.__rcc_provider_metadata;
|
|
203
|
-
if (rawSystemSentinel !== undefined) {
|
|
204
|
-
delete payload.__rcc_raw_system;
|
|
205
|
-
}
|
|
206
|
-
if (providerMetadataSentinel !== undefined) {
|
|
207
|
-
delete payload.__rcc_provider_metadata;
|
|
208
|
-
}
|
|
209
204
|
const topLevelOutputs = normalizeStandaloneToolOutputs(payload.tool_outputs, normalized.missingFields);
|
|
210
205
|
const toolOutputs = [...normalized.toolOutputs];
|
|
211
206
|
for (const entry of topLevelOutputs) {
|
|
@@ -214,22 +209,6 @@ export class ChatSemanticMapper {
|
|
|
214
209
|
}
|
|
215
210
|
}
|
|
216
211
|
const metadata = { context: ctx };
|
|
217
|
-
if (typeof rawSystemSentinel === 'string') {
|
|
218
|
-
try {
|
|
219
|
-
metadata.rawSystem = JSON.parse(rawSystemSentinel);
|
|
220
|
-
}
|
|
221
|
-
catch {
|
|
222
|
-
// ignore parse errors
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
if (typeof providerMetadataSentinel === 'string') {
|
|
226
|
-
try {
|
|
227
|
-
metadata.providerMetadata = JSON.parse(providerMetadataSentinel);
|
|
228
|
-
}
|
|
229
|
-
catch {
|
|
230
|
-
// ignore parse errors
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
212
|
if (normalized.systemSegments.length) {
|
|
234
213
|
metadata.systemInstructions = normalized.systemSegments;
|
|
235
214
|
}
|
|
@@ -240,9 +219,27 @@ export class ChatSemanticMapper {
|
|
|
240
219
|
if (normalized.missingFields.length) {
|
|
241
220
|
metadata.missingFields = normalized.missingFields;
|
|
242
221
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
222
|
+
try {
|
|
223
|
+
const bridgePolicy = resolveBridgePolicy({ protocol: 'openai-chat' });
|
|
224
|
+
const actions = resolvePolicyActions(bridgePolicy, 'request_inbound');
|
|
225
|
+
if (actions?.length) {
|
|
226
|
+
const actionState = createBridgeActionState({
|
|
227
|
+
messages: normalized.messages,
|
|
228
|
+
rawRequest: payload,
|
|
229
|
+
metadata: metadata
|
|
230
|
+
});
|
|
231
|
+
runBridgeActionPipeline({
|
|
232
|
+
stage: 'request_inbound',
|
|
233
|
+
actions,
|
|
234
|
+
protocol: bridgePolicy?.protocol ?? 'openai-chat',
|
|
235
|
+
moduleType: bridgePolicy?.moduleType ?? 'openai-chat',
|
|
236
|
+
requestId: ctx.requestId,
|
|
237
|
+
state: actionState
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// noop: best-effort policy application
|
|
246
243
|
}
|
|
247
244
|
if (Array.isArray(payload.tools) && payload.tools.length === 0) {
|
|
248
245
|
metadata.toolsFieldPresent = true;
|
|
@@ -261,17 +258,39 @@ export class ChatSemanticMapper {
|
|
|
261
258
|
tools: chat.tools ?? (chat.metadata?.toolsFieldPresent ? [] : undefined),
|
|
262
259
|
...(chat.parameters || {})
|
|
263
260
|
};
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
261
|
+
try {
|
|
262
|
+
const bridgePolicy = resolveBridgePolicy({ protocol: 'openai-chat' });
|
|
263
|
+
const actions = resolvePolicyActions(bridgePolicy, 'request_outbound');
|
|
264
|
+
if (actions?.length && Array.isArray(payload.messages)) {
|
|
265
|
+
const capturedToolResults = Array.isArray(chat.toolOutputs)
|
|
266
|
+
? chat.toolOutputs.map((entry) => ({
|
|
267
|
+
tool_call_id: entry.tool_call_id,
|
|
268
|
+
output: entry.content,
|
|
269
|
+
name: entry.name
|
|
270
|
+
}))
|
|
271
|
+
: undefined;
|
|
272
|
+
const actionState = createBridgeActionState({
|
|
273
|
+
messages: payload.messages,
|
|
274
|
+
rawRequest: payload,
|
|
275
|
+
metadata: chat.metadata,
|
|
276
|
+
capturedToolResults
|
|
277
|
+
});
|
|
278
|
+
runBridgeActionPipeline({
|
|
279
|
+
stage: 'request_outbound',
|
|
280
|
+
actions,
|
|
281
|
+
protocol: bridgePolicy?.protocol ?? 'openai-chat',
|
|
282
|
+
moduleType: bridgePolicy?.moduleType ?? 'openai-chat',
|
|
283
|
+
requestId: ctx.requestId,
|
|
284
|
+
state: actionState
|
|
285
|
+
});
|
|
270
286
|
}
|
|
271
287
|
}
|
|
272
|
-
|
|
288
|
+
catch {
|
|
289
|
+
// ignore policy failures
|
|
290
|
+
}
|
|
291
|
+
if (chat.metadata?.rawSystem !== undefined) {
|
|
273
292
|
try {
|
|
274
|
-
payload.
|
|
293
|
+
payload.__rcc_raw_system = JSON.stringify(chat.metadata.rawSystem);
|
|
275
294
|
}
|
|
276
295
|
catch {
|
|
277
296
|
// ignore serialization errors
|
|
@@ -284,14 +303,6 @@ export class ChatSemanticMapper {
|
|
|
284
303
|
payload.max_tokens = payload.max_output_tokens;
|
|
285
304
|
delete payload.max_output_tokens;
|
|
286
305
|
}
|
|
287
|
-
const extraFields = chat.metadata?.extraFields;
|
|
288
|
-
if (isJsonObject(extraFields)) {
|
|
289
|
-
for (const [key, value] of Object.entries(extraFields)) {
|
|
290
|
-
if (payload[key] === undefined) {
|
|
291
|
-
payload[key] = value;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
306
|
return {
|
|
296
307
|
protocol: 'openai-chat',
|
|
297
308
|
direction: 'response',
|