@jsonstudio/llms 0.6.3409 → 0.6.3541
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/codecs/anthropic-openai-codec.d.ts +12 -3
- package/dist/conversion/codecs/anthropic-openai-codec.js +32 -92
- package/dist/conversion/codecs/gemini-openai-codec.d.ts +6 -5
- package/dist/conversion/codecs/gemini-openai-codec.js +48 -685
- package/dist/conversion/codecs/openai-openai-codec.d.ts +1 -1
- package/dist/conversion/codecs/openai-openai-codec.js +34 -100
- package/dist/conversion/codecs/responses-openai-codec.d.ts +1 -1
- package/dist/conversion/codecs/responses-openai-codec.js +47 -159
- package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.d.ts +2 -6
- package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.js +29 -245
- package/dist/conversion/compat/actions/anthropic-claude-code-user-id.d.ts +3 -0
- package/dist/conversion/compat/actions/anthropic-claude-code-user-id.js +30 -0
- package/dist/conversion/compat/actions/antigravity-thought-signature-prepare.js +21 -232
- package/dist/conversion/compat/actions/deepseek-web-request.js +41 -276
- package/dist/conversion/compat/actions/deepseek-web-response.js +117 -855
- package/dist/conversion/compat/actions/gemini-cli-request.d.ts +1 -1
- package/dist/conversion/compat/actions/gemini-cli-request.js +20 -613
- package/dist/conversion/compat/actions/gemini-web-search.d.ts +1 -15
- package/dist/conversion/compat/actions/gemini-web-search.js +22 -69
- package/dist/conversion/compat/actions/glm-tool-extraction.d.ts +3 -2
- package/dist/conversion/compat/actions/glm-tool-extraction.js +28 -257
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +0 -8
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +24 -206
- package/dist/conversion/compat/actions/qwen-transform.d.ts +3 -2
- package/dist/conversion/compat/actions/qwen-transform.js +30 -271
- package/dist/conversion/compat/actions/tool-text-request-guidance.js +3 -173
- package/dist/conversion/compat/actions/universal-shape-filter.d.ts +6 -23
- package/dist/conversion/compat/actions/universal-shape-filter.js +4 -383
- package/dist/conversion/hub/pipeline/compat/native-adapter-context.js +1 -0
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.d.ts +1 -2
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +50 -104
- package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.js +12 -10
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.d.ts +0 -2
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.js +46 -67
- package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.js +15 -40
- package/dist/conversion/responses/responses-openai-bridge/response-payload.js +47 -348
- package/dist/conversion/responses/responses-openai-bridge.js +129 -611
- package/dist/conversion/shared/chat-output-normalizer.js +6 -0
- package/dist/conversion/shared/chat-request-filters.js +1 -1
- package/dist/conversion/shared/output-content-normalizer.js +10 -0
- package/dist/conversion/shared/responses-conversation-store.js +22 -135
- package/dist/conversion/shared/responses-output-builder.d.ts +0 -2
- package/dist/conversion/shared/responses-output-builder.js +28 -318
- package/dist/conversion/shared/responses-response-utils.js +35 -86
- package/dist/conversion/shared/streaming-text-extractor.d.ts +1 -2
- package/dist/conversion/shared/streaming-text-extractor.js +13 -14
- package/dist/native/router_hotpath_napi.node +0 -0
- package/dist/quota/quota-state.js +29 -7
- package/dist/quota/types.d.ts +1 -0
- package/dist/router/virtual-router/bootstrap/routing-config.js +11 -3
- package/dist/router/virtual-router/engine-legacy.d.ts +3 -3
- package/dist/router/virtual-router/engine-legacy.js +15 -7
- package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.d.ts +16 -0
- package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.js +434 -46
- package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.d.ts +83 -0
- package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.js +295 -0
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.d.ts +1 -0
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.d.ts +7 -0
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.js +8 -1
- package/dist/router/virtual-router/engine-selection/native-router-hotpath-loader.js +383 -298
- package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.d.ts +20 -0
- package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.js +201 -0
- package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.d.ts +1 -0
- package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.js +37 -0
- package/dist/router/virtual-router/engine.js +0 -38
- package/dist/router/virtual-router/features.js +44 -3
- package/dist/router/virtual-router/routing-instructions/parse.d.ts +0 -12
- package/dist/router/virtual-router/routing-instructions/parse.js +9 -389
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +3 -6
- package/dist/router/virtual-router/stop-message-state-sync.js +50 -21
- package/dist/servertool/handlers/followup-request-builder.js +12 -2
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -0
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +26 -0
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +12 -2
- package/package.json +1 -1
- package/dist/router/virtual-router/engine-legacy/route-finalize.d.ts +0 -9
- package/dist/router/virtual-router/engine-legacy/route-finalize.js +0 -84
- package/dist/router/virtual-router/engine-legacy/route-selection.d.ts +0 -17
- package/dist/router/virtual-router/engine-legacy/route-selection.js +0 -205
- package/dist/router/virtual-router/engine-legacy/route-state-allowlist.d.ts +0 -3
- package/dist/router/virtual-router/engine-legacy/route-state-allowlist.js +0 -36
- package/dist/router/virtual-router/engine-legacy/route-state.d.ts +0 -12
- package/dist/router/virtual-router/engine-legacy/route-state.js +0 -386
- package/dist/router/virtual-router/engine-legacy/routing.d.ts +0 -8
- package/dist/router/virtual-router/engine-legacy/routing.js +0 -8
|
@@ -1,393 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
4
|
-
}
|
|
5
|
-
function toRecord(value) {
|
|
6
|
-
return isRecord(value) ? value : {};
|
|
7
|
-
}
|
|
8
|
-
function toArray(value) {
|
|
9
|
-
return Array.isArray(value) ? value : [];
|
|
10
|
-
}
|
|
11
|
-
function hasArrayItems(value) {
|
|
12
|
-
return Array.isArray(value) && value.length > 0;
|
|
13
|
-
}
|
|
1
|
+
import { buildNativeReqOutboundCompatAdapterContext } from "../../hub/pipeline/compat/native-adapter-context.js";
|
|
2
|
+
import { applyUniversalShapeRequestFilterWithNative, applyUniversalShapeResponseFilterWithNative, } from "../../../router/virtual-router/engine-selection/native-compat-action-semantics.js";
|
|
14
3
|
export class UniversalShapeFilter {
|
|
15
4
|
cfg;
|
|
16
5
|
constructor(config) {
|
|
17
6
|
this.cfg = config;
|
|
18
7
|
}
|
|
19
8
|
applyRequestFilter(payload) {
|
|
20
|
-
|
|
21
|
-
const allow = new Set(cfg.request.allowTopLevel);
|
|
22
|
-
const src = toRecord(payload);
|
|
23
|
-
const out = {};
|
|
24
|
-
for (const key of Object.keys(src)) {
|
|
25
|
-
if (allow.has(key)) {
|
|
26
|
-
out[key] = src[key];
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
const normalizedMessages = this.normalizeRequestMessages(out.messages, cfg.request);
|
|
30
|
-
out.messages = normalizedMessages;
|
|
31
|
-
this.normalizeTools(out, cfg.request);
|
|
32
|
-
this.cleanupToolChoice(out);
|
|
33
|
-
return out;
|
|
9
|
+
return applyUniversalShapeRequestFilterWithNative(payload, this.cfg);
|
|
34
10
|
}
|
|
35
11
|
applyResponseFilter(payload, ctx) {
|
|
36
|
-
|
|
37
|
-
const envBypass = !(envFlag === '0' || envFlag === 'false' || envFlag === 'off');
|
|
38
|
-
const entryEndpoint = ctx?.entryEndpoint ?? ctx?.endpoint;
|
|
39
|
-
const entry = typeof entryEndpoint === 'string' ? entryEndpoint.toLowerCase() : '';
|
|
40
|
-
if (entry === '/v1/responses' || envBypass) {
|
|
41
|
-
return payload;
|
|
42
|
-
}
|
|
43
|
-
const cfg = this.cfg;
|
|
44
|
-
const src = toRecord(payload);
|
|
45
|
-
const out = this.shallowPick(src, cfg.response.allowTopLevel);
|
|
46
|
-
const choices = Array.isArray(src.choices) ? src.choices : [];
|
|
47
|
-
out.choices = choices.map((choice, idx) => this.normalizeResponseChoice(choice, idx, cfg.response));
|
|
48
|
-
if (src.usage && typeof src.usage === 'object') {
|
|
49
|
-
out.usage = this.shallowPick(src.usage, cfg.response.usage?.allow || []);
|
|
50
|
-
}
|
|
51
|
-
return out;
|
|
52
|
-
}
|
|
53
|
-
shallowPick(obj, allow) {
|
|
54
|
-
if (!isRecord(obj)) {
|
|
55
|
-
return {};
|
|
56
|
-
}
|
|
57
|
-
const out = {};
|
|
58
|
-
for (const key of allow) {
|
|
59
|
-
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
60
|
-
out[key] = obj[key];
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return out;
|
|
64
|
-
}
|
|
65
|
-
toObjectArgs(value) {
|
|
66
|
-
if (value === null || value === undefined) {
|
|
67
|
-
return {};
|
|
68
|
-
}
|
|
69
|
-
if (isRecord(value)) {
|
|
70
|
-
return value;
|
|
71
|
-
}
|
|
72
|
-
if (typeof value === 'string') {
|
|
73
|
-
try {
|
|
74
|
-
return JSON.parse(value);
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
return { raw: value };
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return {};
|
|
81
|
-
}
|
|
82
|
-
toStringArgs(value) {
|
|
83
|
-
if (typeof value === 'string') {
|
|
84
|
-
return value;
|
|
85
|
-
}
|
|
86
|
-
try {
|
|
87
|
-
return JSON.stringify(value ?? {});
|
|
88
|
-
}
|
|
89
|
-
catch {
|
|
90
|
-
return '{}';
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
normalizeToolContent(value) {
|
|
94
|
-
if (typeof value === 'string') {
|
|
95
|
-
return value.trim().length ? value : DEFAULT_TOOL_OUTPUT;
|
|
96
|
-
}
|
|
97
|
-
if (value === null || value === undefined) {
|
|
98
|
-
return DEFAULT_TOOL_OUTPUT;
|
|
99
|
-
}
|
|
100
|
-
try {
|
|
101
|
-
const text = JSON.stringify(value);
|
|
102
|
-
return text && text.length ? text : DEFAULT_TOOL_OUTPUT;
|
|
103
|
-
}
|
|
104
|
-
catch {
|
|
105
|
-
return DEFAULT_TOOL_OUTPUT;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
normalizeRequestMessages(messages, requestCfg) {
|
|
109
|
-
const entries = toArray(messages);
|
|
110
|
-
const normalized = entries.map(entry => this.normalizeSingleMessage(entry, requestCfg));
|
|
111
|
-
const withRules = this.applyMessageRules(normalized, requestCfg);
|
|
112
|
-
this.pairToolResults(withRules);
|
|
113
|
-
return withRules;
|
|
114
|
-
}
|
|
115
|
-
normalizeSingleMessage(message, requestCfg) {
|
|
116
|
-
const msg = toRecord(message);
|
|
117
|
-
const allowedRoles = requestCfg.messages.allowedRoles;
|
|
118
|
-
const requestedRole = typeof msg.role === 'string' ? msg.role : undefined;
|
|
119
|
-
const role = (requestedRole && allowedRoles.includes(requestedRole)) ? requestedRole : 'user';
|
|
120
|
-
const normalized = { role };
|
|
121
|
-
if (role === 'tool') {
|
|
122
|
-
normalized.content = this.normalizeToolContent(msg.content);
|
|
123
|
-
if (typeof msg.name === 'string') {
|
|
124
|
-
normalized.name = msg.name;
|
|
125
|
-
}
|
|
126
|
-
if (typeof msg.tool_call_id === 'string') {
|
|
127
|
-
normalized.tool_call_id = msg.tool_call_id;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
else if (Array.isArray(msg.content)) {
|
|
131
|
-
normalized.content = msg.content.map((part) => {
|
|
132
|
-
if (typeof part === 'string') {
|
|
133
|
-
return part;
|
|
134
|
-
}
|
|
135
|
-
if (isRecord(part)) {
|
|
136
|
-
return { ...part };
|
|
137
|
-
}
|
|
138
|
-
return part;
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
normalized.content = (msg.content !== null && msg.content !== undefined) ? String(msg.content) : '';
|
|
143
|
-
}
|
|
144
|
-
if (role === 'assistant' && hasArrayItems(msg.tool_calls)) {
|
|
145
|
-
normalized.tool_calls = this.normalizeAssistantToolCalls(msg.tool_calls, requestCfg);
|
|
146
|
-
if (requestCfg.messages.assistantWithToolCallsContentNull) {
|
|
147
|
-
normalized.content = null;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return normalized;
|
|
151
|
-
}
|
|
152
|
-
normalizeAssistantToolCalls(toolCalls, requestCfg) {
|
|
153
|
-
const entries = toArray(toolCalls);
|
|
154
|
-
return entries.map(call => {
|
|
155
|
-
const tc = toRecord(call);
|
|
156
|
-
const fn = toRecord(tc.function);
|
|
157
|
-
const name = typeof fn.name === 'string' ? fn.name : undefined;
|
|
158
|
-
const argsValue = requestCfg.assistantToolCalls?.functionArgumentsType === 'string'
|
|
159
|
-
? this.toStringArgs(fn.arguments)
|
|
160
|
-
: this.toObjectArgs(fn.arguments);
|
|
161
|
-
const normalized = {
|
|
162
|
-
type: typeof tc.type === 'string' ? tc.type : 'function',
|
|
163
|
-
function: { ...(name ? { name } : {}), arguments: argsValue }
|
|
164
|
-
};
|
|
165
|
-
if (typeof tc.id === 'string') {
|
|
166
|
-
normalized.id = tc.id;
|
|
167
|
-
}
|
|
168
|
-
return normalized;
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
applyMessageRules(messages, requestCfg) {
|
|
172
|
-
const rules = Array.isArray(requestCfg.messagesRules) ? requestCfg.messagesRules : [];
|
|
173
|
-
if (!rules.length) {
|
|
174
|
-
if (requestCfg.messages.suppressAssistantToolCalls) {
|
|
175
|
-
return messages.filter(msg => !(msg.role === 'assistant' && hasArrayItems(msg.tool_calls)));
|
|
176
|
-
}
|
|
177
|
-
return messages;
|
|
178
|
-
}
|
|
179
|
-
const result = [];
|
|
180
|
-
for (const message of messages) {
|
|
181
|
-
let dropped = false;
|
|
182
|
-
for (const rule of rules) {
|
|
183
|
-
const when = rule.when || {};
|
|
184
|
-
const matchRole = when.role ? message.role === when.role : true;
|
|
185
|
-
const hasTools = hasArrayItems(message.tool_calls);
|
|
186
|
-
const matchTools = typeof when.hasToolCalls === 'boolean' ? hasTools === when.hasToolCalls : true;
|
|
187
|
-
if (matchRole && matchTools) {
|
|
188
|
-
if (rule.action === 'drop') {
|
|
189
|
-
dropped = true;
|
|
190
|
-
break;
|
|
191
|
-
}
|
|
192
|
-
if (rule.action === 'set' && rule.set && typeof rule.set === 'object') {
|
|
193
|
-
Object.assign(message, rule.set);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
if (!dropped) {
|
|
198
|
-
result.push(message);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return result;
|
|
202
|
-
}
|
|
203
|
-
pairToolResults(messages) {
|
|
204
|
-
const nameById = new Map();
|
|
205
|
-
for (const message of messages) {
|
|
206
|
-
if (message.role === 'assistant' && hasArrayItems(message.tool_calls)) {
|
|
207
|
-
for (const call of message.tool_calls) {
|
|
208
|
-
const toolCall = toRecord(call);
|
|
209
|
-
if (typeof toolCall.id !== 'string') {
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
const fn = toRecord(toolCall.function);
|
|
213
|
-
if (typeof fn.name === 'string') {
|
|
214
|
-
nameById.set(toolCall.id, fn.name);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
for (const message of messages) {
|
|
220
|
-
if (message.role !== 'tool') {
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
const name = typeof message.name === 'string' ? message.name.trim() : '';
|
|
224
|
-
if (name) {
|
|
225
|
-
continue;
|
|
226
|
-
}
|
|
227
|
-
const toolCallId = typeof message.tool_call_id === 'string' ? message.tool_call_id : undefined;
|
|
228
|
-
if (toolCallId && nameById.has(toolCallId)) {
|
|
229
|
-
message.name = nameById.get(toolCallId);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
normalizeTools(container, requestCfg) {
|
|
234
|
-
if (!Array.isArray(container.tools)) {
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
if (!requestCfg.tools?.normalize) {
|
|
238
|
-
if (requestCfg.tools?.forceToolChoiceAuto) {
|
|
239
|
-
container.tool_choice = 'auto';
|
|
240
|
-
}
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
const normalized = [];
|
|
244
|
-
for (const toolEntry of container.tools) {
|
|
245
|
-
const normalizedTool = this.normalizeSingleTool(toolEntry);
|
|
246
|
-
normalized.push(normalizedTool);
|
|
247
|
-
}
|
|
248
|
-
container.tools = normalized;
|
|
249
|
-
if (requestCfg.tools?.forceToolChoiceAuto) {
|
|
250
|
-
container.tool_choice = 'auto';
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
normalizeSingleTool(toolEntry) {
|
|
254
|
-
const tool = toRecord(toolEntry);
|
|
255
|
-
const fnTop = {
|
|
256
|
-
name: typeof tool.name === 'string' ? tool.name : undefined,
|
|
257
|
-
description: typeof tool.description === 'string' ? tool.description : undefined,
|
|
258
|
-
parameters: tool.parameters
|
|
259
|
-
};
|
|
260
|
-
const fn = toRecord(tool.function);
|
|
261
|
-
const name = typeof fn.name === 'string' ? fn.name : fnTop.name;
|
|
262
|
-
const description = typeof fn.description === 'string' ? fn.description : fnTop.description;
|
|
263
|
-
let parameters = fn.parameters !== undefined ? fn.parameters : fnTop.parameters;
|
|
264
|
-
parameters = this.normalizeToolParameters(parameters, name);
|
|
265
|
-
const normalizedFn = {
|
|
266
|
-
...(name ? { name } : {}),
|
|
267
|
-
...(description ? { description } : {})
|
|
268
|
-
};
|
|
269
|
-
if (parameters !== undefined) {
|
|
270
|
-
normalizedFn.parameters = parameters;
|
|
271
|
-
}
|
|
272
|
-
return {
|
|
273
|
-
type: 'function',
|
|
274
|
-
function: normalizedFn
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
normalizeToolParameters(input, name) {
|
|
278
|
-
if (input === null || input === undefined) {
|
|
279
|
-
return undefined;
|
|
280
|
-
}
|
|
281
|
-
let params;
|
|
282
|
-
if (typeof input === 'string') {
|
|
283
|
-
try {
|
|
284
|
-
params = JSON.parse(input);
|
|
285
|
-
}
|
|
286
|
-
catch {
|
|
287
|
-
params = undefined;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
else if (isRecord(input)) {
|
|
291
|
-
params = input;
|
|
292
|
-
}
|
|
293
|
-
if (!params) {
|
|
294
|
-
return undefined;
|
|
295
|
-
}
|
|
296
|
-
if (name && name.trim().toLowerCase() === 'shell') {
|
|
297
|
-
return this.enforceShellSchema(params);
|
|
298
|
-
}
|
|
299
|
-
return params;
|
|
300
|
-
}
|
|
301
|
-
enforceShellSchema(schema) {
|
|
302
|
-
const next = { ...schema };
|
|
303
|
-
if (typeof next.type !== 'string') {
|
|
304
|
-
next.type = 'object';
|
|
305
|
-
}
|
|
306
|
-
if (!isRecord(next.properties)) {
|
|
307
|
-
next.properties = {};
|
|
308
|
-
}
|
|
309
|
-
const props = next.properties;
|
|
310
|
-
const command = toRecord(props.command);
|
|
311
|
-
const hasOneOf = Array.isArray(command.oneOf);
|
|
312
|
-
if (!hasOneOf) {
|
|
313
|
-
const descText = typeof command.description === 'string'
|
|
314
|
-
? command.description
|
|
315
|
-
: 'Shell command. Prefer a single string; an array of argv tokens is also accepted.';
|
|
316
|
-
props.command = {
|
|
317
|
-
description: descText,
|
|
318
|
-
oneOf: [
|
|
319
|
-
{ type: 'string' },
|
|
320
|
-
{ type: 'array', items: { type: 'string' } }
|
|
321
|
-
]
|
|
322
|
-
};
|
|
323
|
-
const requiredList = Array.isArray(next.required)
|
|
324
|
-
? next.required.filter((item) => typeof item === 'string')
|
|
325
|
-
: [];
|
|
326
|
-
if (!requiredList.includes('command')) {
|
|
327
|
-
requiredList.push('command');
|
|
328
|
-
}
|
|
329
|
-
next.required = requiredList;
|
|
330
|
-
if (typeof next.additionalProperties !== 'boolean') {
|
|
331
|
-
next.additionalProperties = false;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
return next;
|
|
335
|
-
}
|
|
336
|
-
cleanupToolChoice(container) {
|
|
337
|
-
const toolsArray = Array.isArray(container.tools) ? container.tools : [];
|
|
338
|
-
if (!toolsArray.length && Object.prototype.hasOwnProperty.call(container, 'tool_choice')) {
|
|
339
|
-
delete container.tool_choice;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
normalizeResponseChoice(choice, idx, responseCfg) {
|
|
343
|
-
const choiceRecord = toRecord(choice);
|
|
344
|
-
const normalized = {
|
|
345
|
-
index: typeof choiceRecord.index === 'number' ? choiceRecord.index : idx
|
|
346
|
-
};
|
|
347
|
-
const message = toRecord(choiceRecord.message);
|
|
348
|
-
normalized.message = this.normalizeResponseMessage(message, responseCfg);
|
|
349
|
-
const hasToolCalls = hasArrayItems(normalized.message.tool_calls);
|
|
350
|
-
normalized.finish_reason = choiceRecord.finish_reason ?? (hasToolCalls ? 'tool_calls' : null);
|
|
351
|
-
return normalized;
|
|
352
|
-
}
|
|
353
|
-
normalizeResponseMessage(message, responseCfg) {
|
|
354
|
-
const normalized = {};
|
|
355
|
-
normalized.role = typeof message.role === 'string'
|
|
356
|
-
? message.role
|
|
357
|
-
: (responseCfg.choices.message.roleDefault || 'assistant');
|
|
358
|
-
if (hasArrayItems(message.tool_calls)) {
|
|
359
|
-
normalized.tool_calls = this.normalizeResponseToolCalls(message.tool_calls, responseCfg);
|
|
360
|
-
normalized.content = responseCfg.choices.message.contentNullWhenToolCalls ? null : message.content ?? '';
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
normalized.content = message.content ?? '';
|
|
364
|
-
}
|
|
365
|
-
if (typeof message.reasoning_content === 'string') {
|
|
366
|
-
normalized.reasoning_content = message.reasoning_content;
|
|
367
|
-
}
|
|
368
|
-
if (message.audio) {
|
|
369
|
-
normalized.audio = message.audio;
|
|
370
|
-
}
|
|
371
|
-
return normalized;
|
|
372
|
-
}
|
|
373
|
-
normalizeResponseToolCalls(toolCalls, responseCfg) {
|
|
374
|
-
const entries = toArray(toolCalls);
|
|
375
|
-
return entries.map(call => {
|
|
376
|
-
const tc = toRecord(call);
|
|
377
|
-
const fn = toRecord(tc.function);
|
|
378
|
-
const name = typeof fn.name === 'string' ? fn.name : undefined;
|
|
379
|
-
const argsObject = this.toObjectArgs(fn.arguments);
|
|
380
|
-
const argsValue = responseCfg.choices.message.tool_calls?.function?.argumentsType === 'string'
|
|
381
|
-
? this.toStringArgs(argsObject)
|
|
382
|
-
: argsObject;
|
|
383
|
-
const normalized = {
|
|
384
|
-
type: typeof tc.type === 'string' ? tc.type : 'function',
|
|
385
|
-
function: { ...(name ? { name } : {}), arguments: argsValue }
|
|
386
|
-
};
|
|
387
|
-
if (typeof tc.id === 'string') {
|
|
388
|
-
normalized.id = tc.id;
|
|
389
|
-
}
|
|
390
|
-
return normalized;
|
|
391
|
-
});
|
|
12
|
+
return applyUniversalShapeResponseFilterWithNative(payload, this.cfg, buildNativeReqOutboundCompatAdapterContext(ctx));
|
|
392
13
|
}
|
|
393
14
|
}
|
|
@@ -31,6 +31,7 @@ export function buildNativeReqOutboundCompatAdapterContext(adapterContext) {
|
|
|
31
31
|
routeId: readString('routeId') ?? adapterContext?.routeId,
|
|
32
32
|
capturedChatRequest: readRecord('capturedChatRequest'),
|
|
33
33
|
deepseek: readRecord('deepseek'),
|
|
34
|
+
claudeCode: readRecord('claudeCode'),
|
|
34
35
|
estimatedInputTokens: readNumber('estimatedInputTokens'),
|
|
35
36
|
modelId: readString('modelId'),
|
|
36
37
|
clientModelId: readString('clientModelId'),
|
|
@@ -2,9 +2,8 @@ import type { ConversionCodec, ConversionContext, ConversionProfile } from '../.
|
|
|
2
2
|
import type { JsonObject } from '../../../hub/types/json.js';
|
|
3
3
|
export declare class AnthropicOpenAIPipelineCodec implements ConversionCodec {
|
|
4
4
|
readonly id = "anthropic-openai-v2";
|
|
5
|
-
private readonly
|
|
5
|
+
private readonly requestMetaStore;
|
|
6
6
|
private initialized;
|
|
7
|
-
constructor();
|
|
8
7
|
initialize(): Promise<void>;
|
|
9
8
|
private ensureInitialized;
|
|
10
9
|
convertRequest(payload: unknown, profile: ConversionProfile, context: ConversionContext): Promise<JsonObject>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { ProtocolConversionPipeline } from '../../index.js';
|
|
2
|
-
import { chatEnvelopeToStandardized } from '../../../hub/standardized-bridge.js';
|
|
3
|
-
import { AnthropicFormatAdapter } from '../../../hub/format-adapters/anthropic-format-adapter.js';
|
|
4
|
-
import { AnthropicSemanticMapper } from '../../../hub/semantic-mappers/anthropic-mapper.js';
|
|
5
|
-
import { buildAnthropicFromOpenAIChat } from '../../../codecs/anthropic-openai-codec.js';
|
|
6
1
|
import { buildAdapterContextFromPipeline } from '../../hooks/adapter-context.js';
|
|
7
2
|
import { runStandardChatRequestFilters } from '../../../index.js';
|
|
3
|
+
import { buildAnthropicFromOpenAIChat } from '../../../codecs/anthropic-openai-codec.js';
|
|
4
|
+
import { buildOpenAIChatFromAnthropicWithNative } from '../../../../router/virtual-router/engine-selection/native-compat-action-semantics.js';
|
|
5
|
+
import { chatEnvelopeToStandardizedWithNative } from '../../../../router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.js';
|
|
6
|
+
import { parseReqInboundFormatEnvelopeWithNative } from '../../../../router/virtual-router/engine-selection/native-hub-pipeline-edge-stage-semantics.js';
|
|
7
|
+
import { mapOpenaiChatToChatWithNative } from '../../../../router/virtual-router/engine-selection/native-hub-pipeline-semantic-mappers.js';
|
|
8
8
|
import { canonicalizeOpenAIChatResponse, convertStandardizedToOpenAIChat as convertCanonicalToOpenAIChat, OPENAI_PROTOCOL } from './shared/openai-chat-helpers.js';
|
|
9
9
|
const DEFAULT_ANTHROPIC_ENDPOINT = '/v1/messages';
|
|
10
10
|
const ANTHROPIC_PROTOCOL = 'anthropic-messages';
|
|
@@ -14,93 +14,22 @@ function assertJsonObject(value, stage) {
|
|
|
14
14
|
}
|
|
15
15
|
return value;
|
|
16
16
|
}
|
|
17
|
-
function
|
|
17
|
+
function normalizeAliasMap(candidate) {
|
|
18
18
|
if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
|
|
19
19
|
return undefined;
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
const result = {};
|
|
21
|
+
const out = {};
|
|
23
22
|
for (const [key, value] of Object.entries(candidate)) {
|
|
24
|
-
if (typeof key !== 'string' || typeof value !== 'string')
|
|
23
|
+
if (typeof key !== 'string' || typeof value !== 'string') {
|
|
25
24
|
continue;
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
}
|
|
26
|
+
const trimmed = key.trim();
|
|
27
|
+
if (!trimmed) {
|
|
28
28
|
continue;
|
|
29
|
-
result[trimmedKey] = value;
|
|
30
|
-
populated = true;
|
|
31
|
-
}
|
|
32
|
-
return populated ? result : undefined;
|
|
33
|
-
}
|
|
34
|
-
function readAliasMapFromSemantics(semantics) {
|
|
35
|
-
if (!semantics || typeof semantics !== 'object' || Array.isArray(semantics)) {
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
const toolsNode = semantics.tools;
|
|
39
|
-
if (!toolsNode || typeof toolsNode !== 'object' || Array.isArray(toolsNode)) {
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
const candidate = toolsNode.toolNameAliasMap ??
|
|
43
|
-
toolsNode.toolAliasMap;
|
|
44
|
-
return coerceAliasMap(candidate);
|
|
45
|
-
}
|
|
46
|
-
function ensureAliasMapInSemantics(chatEnvelope, aliasMap) {
|
|
47
|
-
if (!chatEnvelope || typeof chatEnvelope !== 'object') {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const semantics = chatEnvelope.semantics && typeof chatEnvelope.semantics === 'object' && !Array.isArray(chatEnvelope.semantics)
|
|
51
|
-
? chatEnvelope.semantics
|
|
52
|
-
: (() => {
|
|
53
|
-
chatEnvelope.semantics = {};
|
|
54
|
-
return chatEnvelope.semantics;
|
|
55
|
-
})();
|
|
56
|
-
const toolsNode = semantics.tools && typeof semantics.tools === 'object' && !Array.isArray(semantics.tools)
|
|
57
|
-
? semantics.tools
|
|
58
|
-
: (() => {
|
|
59
|
-
semantics.tools = {};
|
|
60
|
-
return semantics.tools;
|
|
61
|
-
})();
|
|
62
|
-
if (!toolsNode.toolNameAliasMap && !toolsNode.toolAliasMap) {
|
|
63
|
-
toolsNode.toolNameAliasMap = { ...aliasMap };
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
function createAnthropicHooks() {
|
|
67
|
-
const formatAdapter = new AnthropicFormatAdapter();
|
|
68
|
-
const semanticMapper = new AnthropicSemanticMapper();
|
|
69
|
-
return {
|
|
70
|
-
id: 'anthropic-openai-v2',
|
|
71
|
-
protocol: ANTHROPIC_PROTOCOL,
|
|
72
|
-
inbound: {
|
|
73
|
-
parse: async ({ wire, context }) => {
|
|
74
|
-
const adapterContext = buildAdapterContextFromPipeline(context, {
|
|
75
|
-
defaultEntryEndpoint: DEFAULT_ANTHROPIC_ENDPOINT,
|
|
76
|
-
overrideProtocol: ANTHROPIC_PROTOCOL
|
|
77
|
-
});
|
|
78
|
-
const formatEnvelope = await formatAdapter.parseRequest(wire, adapterContext);
|
|
79
|
-
const chatEnvelope = await semanticMapper.toChat(formatEnvelope, adapterContext);
|
|
80
|
-
const aliasMap = readAliasMapFromSemantics(chatEnvelope.semantics);
|
|
81
|
-
if (aliasMap) {
|
|
82
|
-
// A1: tool alias map is mappable semantics; keep it in chat.semantics (never metadata).
|
|
83
|
-
ensureAliasMapInSemantics(chatEnvelope, aliasMap);
|
|
84
|
-
}
|
|
85
|
-
const canonical = chatEnvelopeToStandardized(chatEnvelope, {
|
|
86
|
-
adapterContext,
|
|
87
|
-
endpoint: context.entryEndpoint ?? DEFAULT_ANTHROPIC_ENDPOINT,
|
|
88
|
-
requestId: context.requestId
|
|
89
|
-
});
|
|
90
|
-
return { canonical };
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
outbound: {
|
|
94
|
-
serialize: async ({ canonical, context }) => {
|
|
95
|
-
const aliasMap = readAliasMapFromSemantics(canonical.semantics);
|
|
96
|
-
const anthropicPayload = buildAnthropicFromOpenAIChat(canonical, {
|
|
97
|
-
toolNameMap: aliasMap,
|
|
98
|
-
requestId: context.requestId
|
|
99
|
-
});
|
|
100
|
-
return { payload: assertJsonObject(anthropicPayload, 'anthropic_outbound_serialize') };
|
|
101
|
-
}
|
|
102
29
|
}
|
|
103
|
-
|
|
30
|
+
out[trimmed] = value;
|
|
31
|
+
}
|
|
32
|
+
return Object.keys(out).length ? out : undefined;
|
|
104
33
|
}
|
|
105
34
|
function buildPipelineContext(profile, context) {
|
|
106
35
|
return {
|
|
@@ -115,11 +44,8 @@ function buildPipelineContext(profile, context) {
|
|
|
115
44
|
}
|
|
116
45
|
export class AnthropicOpenAIPipelineCodec {
|
|
117
46
|
id = 'anthropic-openai-v2';
|
|
118
|
-
|
|
47
|
+
requestMetaStore = new Map();
|
|
119
48
|
initialized = false;
|
|
120
|
-
constructor() {
|
|
121
|
-
this.pipeline = new ProtocolConversionPipeline(createAnthropicHooks());
|
|
122
|
-
}
|
|
123
49
|
async initialize() {
|
|
124
50
|
this.initialized = true;
|
|
125
51
|
}
|
|
@@ -131,28 +57,48 @@ export class AnthropicOpenAIPipelineCodec {
|
|
|
131
57
|
async convertRequest(payload, profile, context) {
|
|
132
58
|
this.ensureInitialized();
|
|
133
59
|
const inboundContext = buildPipelineContext(profile, context);
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
60
|
+
const requestId = context.requestId ?? inboundContext.requestId ?? `req_${Date.now()}`;
|
|
61
|
+
inboundContext.requestId = requestId;
|
|
62
|
+
const adapterContext = buildAdapterContextFromPipeline(inboundContext, {
|
|
63
|
+
defaultEntryEndpoint: DEFAULT_ANTHROPIC_ENDPOINT,
|
|
64
|
+
overrideProtocol: ANTHROPIC_PROTOCOL
|
|
65
|
+
});
|
|
66
|
+
const native = buildOpenAIChatFromAnthropicWithNative(assertJsonObject(payload, 'anthropic_inbound_request'), { includeToolCallIds: true });
|
|
67
|
+
const openaiRequest = assertJsonObject(native.request, 'anthropic_request_native');
|
|
68
|
+
const aliasMap = normalizeAliasMap(native.anthropicToolNameMap);
|
|
69
|
+
this.requestMetaStore.set(requestId, { aliasMap });
|
|
70
|
+
const formatEnvelope = parseReqInboundFormatEnvelopeWithNative({
|
|
71
|
+
rawRequest: openaiRequest,
|
|
72
|
+
protocol: OPENAI_PROTOCOL
|
|
73
|
+
});
|
|
74
|
+
const chatEnvelope = mapOpenaiChatToChatWithNative((formatEnvelope.payload ?? {}), adapterContext);
|
|
75
|
+
const canonical = chatEnvelopeToStandardizedWithNative({
|
|
76
|
+
chatEnvelope,
|
|
77
|
+
adapterContext: adapterContext,
|
|
78
|
+
endpoint: adapterContext.entryEndpoint ?? DEFAULT_ANTHROPIC_ENDPOINT,
|
|
79
|
+
requestId
|
|
80
|
+
});
|
|
81
|
+
const rebuilt = await convertCanonicalToOpenAIChat(canonical, inboundContext, { defaultEndpoint: DEFAULT_ANTHROPIC_ENDPOINT });
|
|
140
82
|
const filterContext = {
|
|
141
83
|
...context,
|
|
142
|
-
requestId
|
|
84
|
+
requestId,
|
|
143
85
|
entryEndpoint: context.entryEndpoint ?? context.endpoint ?? DEFAULT_ANTHROPIC_ENDPOINT,
|
|
144
86
|
endpoint: context.endpoint ?? context.entryEndpoint ?? DEFAULT_ANTHROPIC_ENDPOINT
|
|
145
87
|
};
|
|
146
|
-
return runStandardChatRequestFilters(
|
|
88
|
+
return runStandardChatRequestFilters(rebuilt, profile, filterContext);
|
|
147
89
|
}
|
|
148
90
|
async convertResponse(payload, profile, context) {
|
|
149
91
|
this.ensureInitialized();
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
return
|
|
92
|
+
const pipelineContext = buildPipelineContext(profile, context);
|
|
93
|
+
const requestId = context.requestId ?? pipelineContext.requestId ?? `req_${Date.now()}`;
|
|
94
|
+
pipelineContext.requestId = requestId;
|
|
95
|
+
const stored = this.requestMetaStore.get(requestId);
|
|
96
|
+
this.requestMetaStore.delete(requestId);
|
|
97
|
+
const sanitized = await canonicalizeOpenAIChatResponse(assertJsonObject(payload, 'openai_chat_response'), context, { defaultEndpoint: DEFAULT_ANTHROPIC_ENDPOINT, profile: ANTHROPIC_PROTOCOL });
|
|
98
|
+
return assertJsonObject(buildAnthropicFromOpenAIChat(sanitized, {
|
|
99
|
+
toolNameMap: stored?.aliasMap,
|
|
100
|
+
requestId,
|
|
101
|
+
entryEndpoint: pipelineContext.entryEndpoint ?? DEFAULT_ANTHROPIC_ENDPOINT
|
|
102
|
+
}), 'anthropic_outbound_serialize');
|
|
157
103
|
}
|
|
158
104
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ProtocolConversionPipeline } from '../../index.js';
|
|
2
2
|
import { buildAdapterContextFromPipeline } from '../../hooks/adapter-context.js';
|
|
3
|
-
import { chatEnvelopeToStandardized } from '../../../hub/standardized-bridge.js';
|
|
4
|
-
import { ChatFormatAdapter } from '../../../hub/format-adapters/chat-format-adapter.js';
|
|
5
|
-
import { ChatSemanticMapper } from '../../../hub/semantic-mappers/chat-mapper.js';
|
|
6
3
|
import { runStandardChatRequestFilters } from '../../../index.js';
|
|
4
|
+
import { chatEnvelopeToStandardizedWithNative } from '../../../../router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.js';
|
|
5
|
+
import { parseReqInboundFormatEnvelopeWithNative } from '../../../../router/virtual-router/engine-selection/native-hub-pipeline-edge-stage-semantics.js';
|
|
6
|
+
import { mapOpenaiChatToChatWithNative } from '../../../../router/virtual-router/engine-selection/native-hub-pipeline-semantic-mappers.js';
|
|
7
7
|
import { canonicalizeOpenAIChatResponse, convertStandardizedToOpenAIChat as convertCanonicalToOpenAIChat, DEFAULT_OPENAI_ENDPOINT, OPENAI_PROTOCOL } from './shared/openai-chat-helpers.js';
|
|
8
8
|
function assertJsonObject(value, stage) {
|
|
9
9
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
@@ -58,8 +58,6 @@ function restoreToolCallIndexes(targetMessages, sourceMessages) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
function createOpenAIHooks() {
|
|
61
|
-
const formatAdapter = new ChatFormatAdapter();
|
|
62
|
-
const semanticMapper = new ChatSemanticMapper();
|
|
63
61
|
return {
|
|
64
62
|
id: 'openai-openai-v2',
|
|
65
63
|
protocol: OPENAI_PROTOCOL,
|
|
@@ -69,14 +67,18 @@ function createOpenAIHooks() {
|
|
|
69
67
|
defaultEntryEndpoint: DEFAULT_OPENAI_ENDPOINT,
|
|
70
68
|
overrideProtocol: OPENAI_PROTOCOL
|
|
71
69
|
});
|
|
72
|
-
const formatEnvelope =
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
const formatEnvelope = parseReqInboundFormatEnvelopeWithNative({
|
|
71
|
+
rawRequest: wire,
|
|
72
|
+
protocol: OPENAI_PROTOCOL
|
|
73
|
+
});
|
|
74
|
+
const chatEnvelope = mapOpenaiChatToChatWithNative((formatEnvelope.payload ?? {}), adapterContext);
|
|
75
|
+
const canonical = chatEnvelopeToStandardizedWithNative({
|
|
76
|
+
chatEnvelope: chatEnvelope,
|
|
77
|
+
adapterContext: adapterContext,
|
|
76
78
|
endpoint: adapterContext.entryEndpoint ?? DEFAULT_OPENAI_ENDPOINT,
|
|
77
79
|
requestId: adapterContext.requestId
|
|
78
80
|
});
|
|
79
|
-
return { canonical };
|
|
81
|
+
return { canonical: canonical };
|
|
80
82
|
}
|
|
81
83
|
},
|
|
82
84
|
outbound: {
|
|
@@ -2,10 +2,8 @@ import type { ConversionCodec, ConversionContext, ConversionProfile } from '../.
|
|
|
2
2
|
import type { JsonObject } from '../../../hub/types/json.js';
|
|
3
3
|
export declare class ResponsesOpenAIPipelineCodec implements ConversionCodec {
|
|
4
4
|
readonly id = "responses-openai-v2";
|
|
5
|
-
private readonly pipeline;
|
|
6
5
|
private readonly requestMetaStore;
|
|
7
6
|
private initialized;
|
|
8
|
-
constructor();
|
|
9
7
|
initialize(): Promise<void>;
|
|
10
8
|
private ensureInitialized;
|
|
11
9
|
private stashMeta;
|