@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.
Files changed (85) hide show
  1. package/dist/conversion/codecs/anthropic-openai-codec.d.ts +12 -3
  2. package/dist/conversion/codecs/anthropic-openai-codec.js +32 -92
  3. package/dist/conversion/codecs/gemini-openai-codec.d.ts +6 -5
  4. package/dist/conversion/codecs/gemini-openai-codec.js +48 -685
  5. package/dist/conversion/codecs/openai-openai-codec.d.ts +1 -1
  6. package/dist/conversion/codecs/openai-openai-codec.js +34 -100
  7. package/dist/conversion/codecs/responses-openai-codec.d.ts +1 -1
  8. package/dist/conversion/codecs/responses-openai-codec.js +47 -159
  9. package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.d.ts +2 -6
  10. package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.js +29 -245
  11. package/dist/conversion/compat/actions/anthropic-claude-code-user-id.d.ts +3 -0
  12. package/dist/conversion/compat/actions/anthropic-claude-code-user-id.js +30 -0
  13. package/dist/conversion/compat/actions/antigravity-thought-signature-prepare.js +21 -232
  14. package/dist/conversion/compat/actions/deepseek-web-request.js +41 -276
  15. package/dist/conversion/compat/actions/deepseek-web-response.js +117 -855
  16. package/dist/conversion/compat/actions/gemini-cli-request.d.ts +1 -1
  17. package/dist/conversion/compat/actions/gemini-cli-request.js +20 -613
  18. package/dist/conversion/compat/actions/gemini-web-search.d.ts +1 -15
  19. package/dist/conversion/compat/actions/gemini-web-search.js +22 -69
  20. package/dist/conversion/compat/actions/glm-tool-extraction.d.ts +3 -2
  21. package/dist/conversion/compat/actions/glm-tool-extraction.js +28 -257
  22. package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +0 -8
  23. package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +24 -206
  24. package/dist/conversion/compat/actions/qwen-transform.d.ts +3 -2
  25. package/dist/conversion/compat/actions/qwen-transform.js +30 -271
  26. package/dist/conversion/compat/actions/tool-text-request-guidance.js +3 -173
  27. package/dist/conversion/compat/actions/universal-shape-filter.d.ts +6 -23
  28. package/dist/conversion/compat/actions/universal-shape-filter.js +4 -383
  29. package/dist/conversion/hub/pipeline/compat/native-adapter-context.js +1 -0
  30. package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.d.ts +1 -2
  31. package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +50 -104
  32. package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.js +12 -10
  33. package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.d.ts +0 -2
  34. package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.js +46 -67
  35. package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.js +15 -40
  36. package/dist/conversion/responses/responses-openai-bridge/response-payload.js +47 -348
  37. package/dist/conversion/responses/responses-openai-bridge.js +129 -611
  38. package/dist/conversion/shared/chat-output-normalizer.js +6 -0
  39. package/dist/conversion/shared/chat-request-filters.js +1 -1
  40. package/dist/conversion/shared/output-content-normalizer.js +10 -0
  41. package/dist/conversion/shared/responses-conversation-store.js +22 -135
  42. package/dist/conversion/shared/responses-output-builder.d.ts +0 -2
  43. package/dist/conversion/shared/responses-output-builder.js +28 -318
  44. package/dist/conversion/shared/responses-response-utils.js +35 -86
  45. package/dist/conversion/shared/streaming-text-extractor.d.ts +1 -2
  46. package/dist/conversion/shared/streaming-text-extractor.js +13 -14
  47. package/dist/native/router_hotpath_napi.node +0 -0
  48. package/dist/quota/quota-state.js +29 -7
  49. package/dist/quota/types.d.ts +1 -0
  50. package/dist/router/virtual-router/bootstrap/routing-config.js +11 -3
  51. package/dist/router/virtual-router/engine-legacy.d.ts +3 -3
  52. package/dist/router/virtual-router/engine-legacy.js +15 -7
  53. package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.d.ts +16 -0
  54. package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.js +434 -46
  55. package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.d.ts +83 -0
  56. package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.js +295 -0
  57. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.d.ts +1 -0
  58. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.d.ts +7 -0
  59. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.js +8 -1
  60. package/dist/router/virtual-router/engine-selection/native-router-hotpath-loader.js +383 -298
  61. package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.d.ts +20 -0
  62. package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.js +201 -0
  63. package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.d.ts +1 -0
  64. package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.js +37 -0
  65. package/dist/router/virtual-router/engine.js +0 -38
  66. package/dist/router/virtual-router/features.js +44 -3
  67. package/dist/router/virtual-router/routing-instructions/parse.d.ts +0 -12
  68. package/dist/router/virtual-router/routing-instructions/parse.js +9 -389
  69. package/dist/router/virtual-router/stop-message-state-sync.d.ts +3 -6
  70. package/dist/router/virtual-router/stop-message-state-sync.js +50 -21
  71. package/dist/servertool/handlers/followup-request-builder.js +12 -2
  72. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -0
  73. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +26 -0
  74. package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +12 -2
  75. package/package.json +1 -1
  76. package/dist/router/virtual-router/engine-legacy/route-finalize.d.ts +0 -9
  77. package/dist/router/virtual-router/engine-legacy/route-finalize.js +0 -84
  78. package/dist/router/virtual-router/engine-legacy/route-selection.d.ts +0 -17
  79. package/dist/router/virtual-router/engine-legacy/route-selection.js +0 -205
  80. package/dist/router/virtual-router/engine-legacy/route-state-allowlist.d.ts +0 -3
  81. package/dist/router/virtual-router/engine-legacy/route-state-allowlist.js +0 -36
  82. package/dist/router/virtual-router/engine-legacy/route-state.d.ts +0 -12
  83. package/dist/router/virtual-router/engine-legacy/route-state.js +0 -386
  84. package/dist/router/virtual-router/engine-legacy/routing.d.ts +0 -8
  85. package/dist/router/virtual-router/engine-legacy/routing.js +0 -8
@@ -1,393 +1,14 @@
1
- const DEFAULT_TOOL_OUTPUT = 'Command succeeded (no output).';
2
- function isRecord(value) {
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
- const cfg = this.cfg;
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
- const envFlag = String(process.env.RCC_COMPAT_FILTER_OFF_RESPONSES || '1').toLowerCase();
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 pipeline;
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 coerceAliasMap(candidate) {
17
+ function normalizeAliasMap(candidate) {
18
18
  if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
19
19
  return undefined;
20
20
  }
21
- let populated = false;
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
- const trimmedKey = key.trim();
27
- if (!trimmedKey.length)
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
- pipeline;
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 inboundOptions = {
135
- payload: assertJsonObject(payload, 'anthropic_inbound_request'),
136
- context: inboundContext
137
- };
138
- const inbound = await this.pipeline.convertInbound(inboundOptions);
139
- const openaiPayload = await convertCanonicalToOpenAIChat(inbound.canonical, inbound.context);
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: context.requestId ?? inbound.context.requestId ?? `req_${Date.now()}`,
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(openaiPayload, profile, filterContext);
88
+ return runStandardChatRequestFilters(rebuilt, profile, filterContext);
147
89
  }
148
90
  async convertResponse(payload, profile, context) {
149
91
  this.ensureInitialized();
150
- const sanitized = await canonicalizeOpenAIChatResponse(assertJsonObject(payload, 'openai_chat_response'), context);
151
- const outboundOptions = {
152
- canonical: sanitized,
153
- context: buildPipelineContext(profile, context)
154
- };
155
- const outbound = await this.pipeline.convertOutbound(outboundOptions);
156
- return outbound.payload;
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 = await formatAdapter.parseRequest(wire, adapterContext);
73
- const chatEnvelope = await semanticMapper.toChat(formatEnvelope, adapterContext);
74
- const canonical = chatEnvelopeToStandardized(chatEnvelope, {
75
- adapterContext,
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;