@jsonstudio/llms 0.6.3275 → 0.6.3379

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 (48) hide show
  1. package/dist/conversion/bridge-message-utils.d.ts +4 -4
  2. package/dist/conversion/bridge-message-utils.js +28 -538
  3. package/dist/conversion/compat/profiles/responses-crs.json +15 -0
  4. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +16 -5
  5. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +1 -6
  6. package/dist/conversion/hub/response/response-runtime.js +14 -6
  7. package/dist/conversion/responses/responses-openai-bridge/response-payload.js +11 -11
  8. package/dist/conversion/shared/anthropic-message-utils.js +2 -12
  9. package/dist/conversion/shared/chat-request-filters.js +2 -61
  10. package/dist/conversion/shared/reasoning-mapping.js +3 -0
  11. package/dist/conversion/shared/reasoning-normalizer.d.ts +1 -0
  12. package/dist/conversion/shared/reasoning-normalizer.js +35 -388
  13. package/dist/conversion/shared/reasoning-tool-normalizer.js +8 -15
  14. package/dist/conversion/shared/reasoning-utils.js +13 -35
  15. package/dist/conversion/shared/responses-tool-utils.d.ts +1 -1
  16. package/dist/conversion/shared/responses-tool-utils.js +63 -65
  17. package/dist/conversion/shared/streaming-text-extractor.d.ts +0 -5
  18. package/dist/conversion/shared/streaming-text-extractor.js +18 -111
  19. package/dist/conversion/shared/text-markup-normalizer/normalize.d.ts +1 -1
  20. package/dist/conversion/shared/text-markup-normalizer/normalize.js +3 -91
  21. package/dist/conversion/shared/thought-signature-validator.js +19 -133
  22. package/dist/conversion/shared/tool-argument-repairer.js +16 -19
  23. package/dist/conversion/shared/tool-call-id-manager.d.ts +1 -5
  24. package/dist/conversion/shared/tool-call-id-manager.js +74 -98
  25. package/dist/conversion/shared/tool-harvester.js +19 -382
  26. package/dist/conversion/shared/tool-mapping.d.ts +2 -3
  27. package/dist/conversion/shared/tool-mapping.js +28 -184
  28. package/dist/conversion/shared/tooling.js +20 -151
  29. package/dist/filters/special/response-tool-arguments-stringify.js +9 -1
  30. package/dist/guidance/index.js +2 -2
  31. package/dist/native/router_hotpath_napi.node +0 -0
  32. package/dist/router/virtual-router/engine-legacy/helpers.js +1 -1
  33. package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.d.ts +39 -0
  34. package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.js +196 -0
  35. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.d.ts +1 -0
  36. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.js +27 -0
  37. package/dist/router/virtual-router/engine-selection/native-router-hotpath-loader.js +34 -0
  38. package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.d.ts +65 -1
  39. package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.js +836 -35
  40. package/dist/router/virtual-router/engine.js +3 -2
  41. package/dist/router/virtual-router/routing-instructions/parse.js +30 -3
  42. package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +28 -3
  43. package/dist/sse/types/anthropic-types.d.ts +3 -1
  44. package/dist/tools/apply-patch/args-normalizer/extract-patch.js +2 -2
  45. package/dist/tools/apply-patch/patch-text/looks-like-patch.js +3 -6
  46. package/dist/tools/apply-patch/patch-text/normalize.js +14 -3
  47. package/dist/tools/tool-registry.js +12 -0
  48. package/package.json +6 -1
@@ -223,8 +223,7 @@ export function buildOpenAIChatFromAnthropicMessage(payload, options) {
223
223
  };
224
224
  const includeToolCallIds = options?.includeToolCallIds === true;
225
225
  const canonicalToolCalls = toolCalls.map((tc) => ({
226
- id: tc.id,
227
- ...(includeToolCallIds ? { call_id: tc.id, tool_call_id: tc.id } : {}),
226
+ ...(includeToolCallIds ? { id: tc.id, call_id: tc.id, tool_call_id: tc.id } : {}),
228
227
  type: 'function',
229
228
  function: { name: tc.name, arguments: tc.args }
230
229
  }));
@@ -246,6 +245,14 @@ export function buildOpenAIChatFromAnthropicMessage(payload, options) {
246
245
  if (!("tool_call_id" in inferred))
247
246
  inferred.tool_call_id = inferredId;
248
247
  }
248
+ else if (!includeToolCallIds) {
249
+ if ("id" in inferred)
250
+ delete inferred.id;
251
+ if ("call_id" in inferred)
252
+ delete inferred.call_id;
253
+ if ("tool_call_id" in inferred)
254
+ delete inferred.tool_call_id;
255
+ }
249
256
  canonicalToolCalls.push(inferred);
250
257
  if (key)
251
258
  seen.add(key);
@@ -259,9 +266,13 @@ export function buildOpenAIChatFromAnthropicMessage(payload, options) {
259
266
  call.call_id = cid;
260
267
  if (!("tool_call_id" in call))
261
268
  call.tool_call_id = cid;
269
+ if (!("id" in call))
270
+ call.id = cid;
262
271
  }
263
272
  }
264
273
  else {
274
+ if ("id" in call)
275
+ delete call.id;
265
276
  if ("call_id" in call)
266
277
  delete call.call_id;
267
278
  if ("tool_call_id" in call)
@@ -443,10 +454,7 @@ function sanitizeAnthropicMessage(message) {
443
454
  .filter((block) => block !== null);
444
455
  const usage = message.usage;
445
456
  if (usage && typeof usage === 'object') {
446
- sanitized.usage = {
447
- input_tokens: usage.input_tokens ?? 0,
448
- output_tokens: usage.output_tokens ?? 0
449
- };
457
+ sanitized.usage = JSON.parse(JSON.stringify(usage));
450
458
  }
451
459
  return sanitized;
452
460
  }
@@ -238,12 +238,6 @@ function buildClientToolIndex(toolsRaw) {
238
238
  }
239
239
  return index;
240
240
  }
241
- const CLIENT_TOOL_NAME_ALIASES = new Map([
242
- ['shell_command', 'exec_command'],
243
- ['shell', 'exec_command'],
244
- ['bash', 'exec_command'],
245
- ['terminal', 'exec_command']
246
- ]);
247
241
  function resolveClientToolName(toolIndex, rawToolName) {
248
242
  const trimmed = rawToolName.trim();
249
243
  if (!trimmed.length)
@@ -256,12 +250,18 @@ function resolveClientToolName(toolIndex, rawToolName) {
256
250
  return key;
257
251
  }
258
252
  }
259
- const aliased = CLIENT_TOOL_NAME_ALIASES.get(lower);
260
- if (!aliased)
253
+ const hasBash = Array.from(toolIndex.keys()).some((key) => key.toLowerCase() === 'bash');
254
+ if (hasBash)
261
255
  return undefined;
262
- for (const key of toolIndex.keys()) {
263
- if (key.toLowerCase() === aliased) {
264
- return key;
256
+ if (!['shell_command', 'exec_command', 'shell', 'terminal', 'bash'].includes(lower)) {
257
+ return undefined;
258
+ }
259
+ const preferred = ['shell_command', 'exec_command', 'shell', 'terminal'];
260
+ for (const candidate of preferred) {
261
+ for (const key of toolIndex.keys()) {
262
+ if (key.toLowerCase() === candidate) {
263
+ return key;
264
+ }
265
265
  }
266
266
  }
267
267
  return undefined;
@@ -186,12 +186,7 @@ function requireSystemText(block, context) {
186
186
  }
187
187
  return text;
188
188
  }
189
- const ANTHROPIC_TOOL_NAME_ALIASES = new Map([
190
- ['bash', 'shell_command'],
191
- ['shell', 'shell_command'],
192
- ['terminal', 'shell_command'],
193
- ['exec_command', 'shell_command']
194
- ]);
189
+ const ANTHROPIC_TOOL_NAME_ALIASES = new Map();
195
190
  const CANONICAL_TO_ANTHROPIC_TOOL_NAMES = new Map([['shell_command', 'Bash']]);
196
191
  const ANTHROPIC_TOP_LEVEL_FIELDS = new Set([
197
192
  'model',
@@ -1398,12 +1393,7 @@ export function buildAnthropicToolAliasMap(rawTools) {
1398
1393
  continue;
1399
1394
  }
1400
1395
  aliasMap.set(canonicalKey, rawName);
1401
- // Anthropic shell-like tools (e.g. Bash) and exec_command are treated as one family.
1402
- // Keep an explicit exec_command key so reverse mapping can preserve
1403
- // canonical exec_command when upstream already uses that name.
1404
- if (canonicalKey === 'shell_command' && !aliasMap.has('exec_command')) {
1405
- aliasMap.set('exec_command', rawName);
1406
- }
1396
+ const lowered = canonicalKey.toLowerCase();
1407
1397
  const lowerKey = canonicalKey.toLowerCase();
1408
1398
  if (lowerKey !== canonicalKey && !aliasMap.has(lowerKey)) {
1409
1399
  aliasMap.set(lowerKey, rawName);
@@ -1,6 +1,7 @@
1
1
  import { normalizeChatRequest } from '../index.js';
2
2
  import { createSnapshotWriter } from '../snapshot-utils.js';
3
3
  import { buildGovernedFilterPayloadWithNativeFallback } from '../../router/virtual-router/engine-selection/native-chat-request-filter-semantics.js';
4
+ import { pruneChatRequestPayloadWithNative } from '../../router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.js';
4
5
  /**
5
6
  * Native-primary Chat request filters.
6
7
  *
@@ -56,67 +57,7 @@ export async function runStandardChatRequestFilters(chatRequest, profile, contex
56
57
  normalized.__rcc_disable_mcp_tools = true;
57
58
  }
58
59
  const preserveStreamField = profile.incomingProtocol === 'openai-chat' && profile.outgoingProtocol === 'openai-chat';
59
- const pruned = pruneChatRequestPayload(normalized, { preserveStreamField });
60
+ const pruned = pruneChatRequestPayloadWithNative(normalized, preserveStreamField);
60
61
  snapshotStage('req_process_filters_output', pruned);
61
62
  return pruned;
62
63
  }
63
- function pruneChatRequestPayload(chatRequest, options) {
64
- if (!chatRequest || typeof chatRequest !== 'object')
65
- return chatRequest;
66
- const stripped = { ...chatRequest };
67
- stripSentinelKeys(stripped);
68
- if ('originalStream' in stripped) {
69
- delete stripped.originalStream;
70
- }
71
- if ('_originalStreamOptions' in stripped) {
72
- delete stripped._originalStreamOptions;
73
- }
74
- if ('metadata' in stripped) {
75
- delete stripped.metadata;
76
- }
77
- if (!options?.preserveStreamField && 'stream' in stripped && stripped.stream !== true) {
78
- delete stripped.stream;
79
- }
80
- if (Array.isArray(stripped.messages)) {
81
- stripped.messages = stripped.messages.map((message) => sanitizeMessageEntry(message));
82
- }
83
- return stripped;
84
- }
85
- function sanitizeMessageEntry(message) {
86
- if (!message || typeof message !== 'object')
87
- return message;
88
- const clone = { ...message };
89
- if (Array.isArray(clone.tool_calls) && clone.tool_calls.length) {
90
- clone.tool_calls = clone.tool_calls.map((call) => sanitizeToolCallEntry(call));
91
- }
92
- if (clone.role === 'tool') {
93
- if (typeof clone.tool_call_id !== 'string' && typeof clone.call_id === 'string') {
94
- clone.tool_call_id = clone.call_id;
95
- }
96
- if ('id' in clone) {
97
- delete clone.id;
98
- }
99
- }
100
- if ('call_id' in clone) {
101
- delete clone.call_id;
102
- }
103
- return clone;
104
- }
105
- function sanitizeToolCallEntry(call) {
106
- if (!call || typeof call !== 'object')
107
- return call;
108
- const clone = { ...call };
109
- delete clone.call_id;
110
- delete clone.tool_call_id;
111
- if (clone.function && typeof clone.function === 'object') {
112
- clone.function = { ...clone.function };
113
- }
114
- return clone;
115
- }
116
- function stripSentinelKeys(record) {
117
- Object.keys(record).forEach((key) => {
118
- if (key.startsWith('__rcc_')) {
119
- delete record[key];
120
- }
121
- });
122
- }
@@ -1,4 +1,7 @@
1
1
  import { mapReasoningContentToResponsesOutputWithNative } from '../../router/virtual-router/engine-selection/native-shared-conversion-semantics.js';
2
2
  export function mapReasoningContentToResponsesOutput(reasoningContent) {
3
+ if (typeof mapReasoningContentToResponsesOutputWithNative !== 'function') {
4
+ throw new Error('[reasoning-mapping] native bindings unavailable');
5
+ }
3
6
  return mapReasoningContentToResponsesOutputWithNative(reasoningContent);
4
7
  }
@@ -19,4 +19,5 @@ export declare function normalizeReasoningInResponsesPayload(payload: {
19
19
  } | null | undefined, options?: ResponsesReasoningNormalizeOptions): void;
20
20
  export declare function normalizeReasoningInGeminiPayload(payload: JsonObject | null | undefined): void;
21
21
  export declare function normalizeReasoningInAnthropicPayload(payload: JsonObject | null | undefined): void;
22
+ export declare function normalizeReasoningInOpenAIPayload(payload: JsonObject | null | undefined): void;
22
23
  export {};
@@ -1,409 +1,56 @@
1
- import { normalizeChatMessageContent } from './chat-output-normalizer.js';
2
- import { extractReasoningSegments } from './reasoning-utils.js';
3
- import { expandResponsesMessageItem } from '../../sse/shared/responses-output-normalizer.js';
4
- import { sanitizeReasoningTaggedTextWithNative } from '../../router/virtual-router/engine-selection/native-shared-conversion-semantics.js';
1
+ import { normalizeReasoningInAnthropicPayloadWithNative, normalizeReasoningInChatPayloadWithNative, normalizeReasoningInGeminiPayloadWithNative, normalizeReasoningInOpenAIPayloadWithNative, normalizeReasoningInResponsesPayloadWithNative } from '../../router/virtual-router/engine-selection/native-shared-conversion-semantics.js';
5
2
  export const RESPONSES_INSTRUCTIONS_REASONING_FIELD = '__rcc_reasoning_instructions';
6
- function isRecord(value) {
7
- return Boolean(value && typeof value === 'object' && !Array.isArray(value));
8
- }
9
- export function normalizeReasoningInChatPayload(payload) {
10
- if (!payload) {
11
- return;
12
- }
13
- if (Array.isArray(payload.messages)) {
14
- payload.messages = payload.messages.map(entry => normalizeChatMessageEntry(entry));
15
- }
16
- if (Array.isArray(payload.choices)) {
17
- payload.choices.forEach(choice => {
18
- normalizeChatChoice(choice);
19
- });
3
+ function assertReasoningNormalizerNativeAvailable() {
4
+ if (typeof normalizeReasoningInChatPayloadWithNative !== 'function' ||
5
+ typeof normalizeReasoningInResponsesPayloadWithNative !== 'function' ||
6
+ typeof normalizeReasoningInGeminiPayloadWithNative !== 'function' ||
7
+ typeof normalizeReasoningInAnthropicPayloadWithNative !== 'function' ||
8
+ typeof normalizeReasoningInOpenAIPayloadWithNative !== 'function') {
9
+ throw new Error('[reasoning-normalizer] native bindings unavailable');
20
10
  }
21
11
  }
22
- function normalizeChatMessageEntry(entry) {
23
- if (!entry || typeof entry !== 'object') {
24
- return entry;
25
- }
26
- const msg = entry;
27
- applyNormalizedChatContent(msg);
28
- return msg;
29
- }
30
- function normalizeChatChoice(choice) {
31
- if (!choice || typeof choice !== 'object') {
12
+ export function normalizeReasoningInChatPayload(payload) {
13
+ assertReasoningNormalizerNativeAvailable();
14
+ if (!payload)
32
15
  return;
33
- }
34
- const record = choice;
35
- const containers = [];
36
- if (record.message && typeof record.message === 'object') {
37
- containers.push(record.message);
38
- }
39
- if (record.delta && typeof record.delta === 'object') {
40
- containers.push(record.delta);
41
- }
42
- containers.forEach(container => applyNormalizedChatContent(container));
43
- }
44
- function applyNormalizedChatContent(container) {
45
- const existingReasoning = typeof container.reasoning_content === 'string' && container.reasoning_content.trim().length
46
- ? container.reasoning_content.trim()
47
- : undefined;
48
- const normalized = normalizeChatMessageContent(container.content);
49
- const role = typeof container.role === 'string' ? container.role : undefined;
50
- if (normalized.contentText !== undefined && normalized.contentText.trim().length) {
51
- container.content = normalized.contentText;
52
- }
53
- else if (typeof container.reasoning_content === 'string' && container.reasoning_content.trim().length) {
54
- container.content = container.reasoning_content.trim();
55
- }
56
- else if (role && role !== 'system' && role !== 'tool') {
57
- container.content = '';
58
- }
59
- if (normalized.reasoningText && normalized.reasoningText.trim().length) {
60
- container.reasoning_content = normalized.reasoningText.trim();
61
- }
62
- else if (existingReasoning) {
63
- container.reasoning_content = existingReasoning;
64
- }
65
- else if ('reasoning_content' in container) {
66
- delete container.reasoning_content;
16
+ const normalized = normalizeReasoningInChatPayloadWithNative(payload);
17
+ if (normalized && typeof normalized === 'object') {
18
+ Object.assign(payload, normalized);
67
19
  }
68
20
  }
69
21
  export function normalizeReasoningInResponsesPayload(payload, options = { includeOutput: true }) {
70
- if (!payload) {
71
- return;
72
- }
73
- const includeOutput = options.includeOutput !== false;
74
- if (includeOutput) {
75
- normalizeResponsesOutput(payload);
76
- }
77
- if (options.includeInput) {
78
- normalizeResponsesInput(payload);
79
- }
80
- if (options.includeInstructions) {
81
- normalizeResponsesInstructions(payload);
82
- }
83
- if (options.includeRequiredAction) {
84
- normalizeResponsesRequiredAction(payload);
85
- }
86
- }
87
- function normalizeResponsesOutput(payload) {
88
- if (!Array.isArray(payload.output)) {
89
- return;
90
- }
91
- const normalized = [];
92
- payload.output.forEach((entry, index) => {
93
- if (isResponsesMessageItem(entry)) {
94
- const expanded = expandResponsesMessageItem(entry, {
95
- requestId: typeof payload.id === 'string' ? payload.id : 'responses',
96
- outputIndex: index
97
- });
98
- expanded.forEach((item) => {
99
- normalized.push(item);
100
- });
101
- }
102
- else {
103
- normalized.push(entry);
104
- }
105
- });
106
- payload.output = normalized;
107
- }
108
- function isResponsesMessageItem(entry) {
109
- if (!isRecord(entry)) {
110
- return false;
111
- }
112
- const type = typeof entry.type === 'string' ? entry.type : undefined;
113
- if (type !== 'message') {
114
- return false;
115
- }
116
- if (!Array.isArray(entry.content)) {
117
- return false;
118
- }
119
- const status = typeof entry.status === 'string' ? entry.status : undefined;
120
- const role = typeof entry.role === 'string' ? entry.role : undefined;
121
- return Boolean(status && role);
122
- }
123
- function normalizeResponsesInput(payload) {
124
- if (!Array.isArray(payload.input)) {
22
+ assertReasoningNormalizerNativeAvailable();
23
+ if (!payload)
125
24
  return;
126
- }
127
- payload.input.forEach((entry) => {
128
- if (!isRecord(entry))
129
- return;
130
- const reasoningSegments = [];
131
- if (typeof entry.content === 'string') {
132
- const { cleaned, segments } = stripReasoningFromString(entry.content);
133
- entry.content = cleaned;
134
- reasoningSegments.push(...segments);
135
- }
136
- else if (Array.isArray(entry.content)) {
137
- entry.content = entry.content.map((block) => normalizeResponsesContentBlock(block, reasoningSegments));
138
- }
139
- if (isRecord(entry.message)) {
140
- normalizeResponsesMessageBlock(entry.message, reasoningSegments);
141
- }
142
- if (typeof entry.text === 'string') {
143
- const { cleaned, segments } = stripReasoningFromString(entry.text);
144
- entry.text = cleaned;
145
- reasoningSegments.push(...segments);
146
- }
147
- if (reasoningSegments.length) {
148
- entry.reasoning_content = mergeReasoningText(entry.reasoning_content, reasoningSegments);
149
- }
150
- else if ('reasoning_content' in entry) {
151
- delete entry.reasoning_content;
152
- }
153
- });
154
- }
155
- function normalizeResponsesMessageBlock(message, collector) {
156
- const localSegments = [];
157
- if (Array.isArray(message.content)) {
158
- message.content = message.content.map((block) => normalizeResponsesContentBlock(block, localSegments));
159
- }
160
- else if (typeof message.content === 'string') {
161
- const { cleaned, segments } = stripReasoningFromString(message.content);
162
- message.content = cleaned;
163
- localSegments.push(...segments);
164
- }
165
- if (typeof message.text === 'string') {
166
- const { cleaned, segments } = stripReasoningFromString(message.text);
167
- message.text = cleaned;
168
- localSegments.push(...segments);
169
- }
170
- if (localSegments.length) {
171
- collector.push(...localSegments);
172
- message.reasoning_content = mergeReasoningText(message.reasoning_content, localSegments);
173
- }
174
- else if ('reasoning_content' in message) {
175
- delete message.reasoning_content;
25
+ const normalized = normalizeReasoningInResponsesPayloadWithNative(payload, options);
26
+ if (normalized && typeof normalized === 'object') {
27
+ Object.assign(payload, normalized);
176
28
  }
177
29
  }
178
- function normalizeResponsesContentBlock(block, collector) {
179
- if (typeof block === 'string') {
180
- const { cleaned, segments } = stripReasoningFromString(block);
181
- collector.push(...segments);
182
- return cleaned;
183
- }
184
- if (!isRecord(block)) {
185
- return block;
186
- }
187
- const type = typeof block.type === 'string' ? block.type.toLowerCase() : '';
188
- if ((type === 'input_text' || type === 'output_text' || type === 'text' || type === 'commentary') && typeof block.text === 'string') {
189
- const { cleaned, segments } = stripReasoningFromString(block.text);
190
- block.text = cleaned;
191
- if (segments.length) {
192
- collector.push(...segments);
193
- block.reasoning_content = mergeReasoningText(block.reasoning_content, segments);
194
- }
195
- else if ('reasoning_content' in block) {
196
- delete block.reasoning_content;
197
- }
198
- }
199
- if (Array.isArray(block.content)) {
200
- block.content = block.content.map((nested) => normalizeResponsesContentBlock(nested, collector));
201
- }
202
- else if (typeof block.content === 'string') {
203
- const { cleaned, segments } = stripReasoningFromString(block.content);
204
- block.content = cleaned;
205
- if (segments.length) {
206
- collector.push(...segments);
207
- block.reasoning_content = mergeReasoningText(block.reasoning_content, segments);
208
- }
209
- else if ('reasoning_content' in block) {
210
- delete block.reasoning_content;
211
- }
212
- }
213
- return block;
214
- }
215
- function normalizeResponsesInstructions(payload) {
216
- if (typeof payload.instructions !== 'string') {
217
- if (RESPONSES_INSTRUCTIONS_REASONING_FIELD in payload) {
218
- delete payload[RESPONSES_INSTRUCTIONS_REASONING_FIELD];
219
- }
220
- return;
221
- }
222
- const { cleaned, segments } = stripReasoningFromString(payload.instructions);
223
- payload.instructions = cleaned;
224
- if (segments.length) {
225
- payload[RESPONSES_INSTRUCTIONS_REASONING_FIELD] = segments.join('\n');
226
- }
227
- else if (RESPONSES_INSTRUCTIONS_REASONING_FIELD in payload) {
228
- delete payload[RESPONSES_INSTRUCTIONS_REASONING_FIELD];
229
- }
230
- }
231
- function normalizeResponsesRequiredAction(payload) {
232
- if (!isRecord(payload.required_action)) {
233
- return;
234
- }
235
- const submit = payload.required_action.submit_tool_outputs;
236
- if (!submit || typeof submit !== 'object') {
237
- return;
238
- }
239
- const toolCalls = Array.isArray(submit.tool_calls)
240
- ? submit.tool_calls
241
- : [];
242
- toolCalls.forEach((call) => {
243
- if (!isRecord(call))
244
- return;
245
- if (typeof call.instructions === 'string') {
246
- const { cleaned, segments } = stripReasoningFromString(call.instructions);
247
- call.instructions = cleaned;
248
- if (segments.length) {
249
- call.reasoning_content = mergeReasoningText(call.reasoning_content, segments);
250
- }
251
- else if ('reasoning_content' in call) {
252
- delete call.reasoning_content;
253
- }
254
- }
255
- });
256
- }
257
- function stripReasoningFromString(value) {
258
- if (typeof value !== 'string') {
259
- return { cleaned: typeof value === 'string' ? value : '', segments: [] };
260
- }
261
- const segments = [];
262
- const cleaned = extractReasoningSegments(value, segments);
263
- const cleanedNative = sanitizeReasoningTaggedTextWithNative(value);
264
- const stableCleaned = typeof cleanedNative === 'string' ? cleanedNative : cleaned;
265
- return { cleaned: stableCleaned, segments };
266
- }
267
- function mergeReasoningText(existing, segments) {
268
- const combined = [];
269
- if (typeof existing === 'string' && existing.trim().length) {
270
- combined.push(existing.trim());
271
- }
272
- for (const segment of segments) {
273
- if (typeof segment === 'string' && segment.trim().length) {
274
- combined.push(segment.trim());
275
- }
276
- }
277
- return combined.join('\n');
278
- }
279
30
  export function normalizeReasoningInGeminiPayload(payload) {
280
- if (!payload) {
31
+ assertReasoningNormalizerNativeAvailable();
32
+ if (!payload)
281
33
  return;
34
+ const normalized = normalizeReasoningInGeminiPayloadWithNative(payload);
35
+ if (normalized && typeof normalized === 'object') {
36
+ Object.assign(payload, normalized);
282
37
  }
283
- const contents = Array.isArray(payload.contents) ? payload.contents : [];
284
- contents.forEach((content) => {
285
- if (!isRecord(content))
286
- return;
287
- const parts = Array.isArray(content.parts) ? content.parts : [];
288
- parts.forEach((part) => {
289
- if (!isRecord(part) || typeof part.text !== 'string') {
290
- return;
291
- }
292
- const reasoningSegments = [];
293
- const cleaned = extractReasoningSegments(part.text, reasoningSegments);
294
- part.text = cleaned;
295
- if (reasoningSegments.length) {
296
- part.reasoning = reasoningSegments.join('\n');
297
- }
298
- else if ('reasoning' in part) {
299
- delete part.reasoning;
300
- }
301
- });
302
- });
303
38
  }
304
39
  export function normalizeReasoningInAnthropicPayload(payload) {
305
- if (!payload) {
40
+ assertReasoningNormalizerNativeAvailable();
41
+ if (!payload)
306
42
  return;
307
- }
308
- const responseContent = Array.isArray(payload.content) ? payload.content : undefined;
309
- if (responseContent) {
310
- const responseReasoning = [];
311
- responseContent.forEach((block) => normalizeAnthropicBlock(block, responseReasoning));
312
- if (responseReasoning.length) {
313
- payload.reasoning_content = mergeReasoningText(payload.reasoning_content, responseReasoning);
314
- }
315
- else if ('reasoning_content' in payload) {
316
- delete payload.reasoning_content;
317
- }
318
- }
319
- const messages = Array.isArray(payload.messages) ? payload.messages : [];
320
- messages.forEach((message) => {
321
- if (!isRecord(message))
322
- return;
323
- normalizeAnthropicMessage(message);
324
- });
325
- const systemField = payload.system;
326
- if (typeof systemField === 'string') {
327
- const { cleaned } = stripReasoningFromString(systemField);
328
- payload.system = cleaned;
329
- }
330
- else if (Array.isArray(systemField)) {
331
- systemField.forEach((entry) => normalizeAnthropicBlock(entry, []));
332
- }
333
- else if (isRecord(systemField) && Array.isArray(systemField.content)) {
334
- const sysBlocks = systemField.content;
335
- systemField.content = sysBlocks.map((block) => {
336
- normalizeAnthropicBlock(block, []);
337
- return block;
338
- });
339
- }
340
- }
341
- function normalizeAnthropicMessage(message) {
342
- const reasoningSegments = [];
343
- if (Array.isArray(message.content)) {
344
- message.content.forEach((block) => normalizeAnthropicBlock(block, reasoningSegments));
345
- }
346
- else if (typeof message.content === 'string') {
347
- const { cleaned, segments } = stripReasoningFromString(message.content);
348
- message.content = cleaned;
349
- reasoningSegments.push(...segments);
350
- }
351
- if (reasoningSegments.length) {
352
- message.reasoning_content = mergeReasoningText(message.reasoning_content, reasoningSegments);
353
- }
354
- else if ('reasoning_content' in message) {
355
- delete message.reasoning_content;
43
+ const normalized = normalizeReasoningInAnthropicPayloadWithNative(payload);
44
+ if (normalized && typeof normalized === 'object') {
45
+ Object.assign(payload, normalized);
356
46
  }
357
47
  }
358
- function normalizeAnthropicBlock(block, collector) {
359
- if (typeof block === 'string') {
360
- const { cleaned, segments } = stripReasoningFromString(block);
361
- collector.push(...segments);
48
+ export function normalizeReasoningInOpenAIPayload(payload) {
49
+ assertReasoningNormalizerNativeAvailable();
50
+ if (!payload)
362
51
  return;
52
+ const normalized = normalizeReasoningInOpenAIPayloadWithNative(payload);
53
+ if (normalized && typeof normalized === 'object') {
54
+ Object.assign(payload, normalized);
363
55
  }
364
- if (!isRecord(block)) {
365
- return;
366
- }
367
- const type = typeof block.type === 'string' ? block.type.toLowerCase() : '';
368
- if (type === 'text' && typeof block.text === 'string') {
369
- const { cleaned, segments } = stripReasoningFromString(block.text);
370
- block.text = cleaned;
371
- collector.push(...segments);
372
- return;
373
- }
374
- if (type === 'thinking' || type === 'reasoning') {
375
- const flattened = flattenAnthropicText(block);
376
- if (flattened.trim().length) {
377
- collector.push(flattened.trim());
378
- }
379
- return;
380
- }
381
- if (Array.isArray(block.content)) {
382
- block.content.forEach((nested) => normalizeAnthropicBlock(nested, collector));
383
- }
384
- else if (typeof block.content === 'string') {
385
- const { cleaned, segments } = stripReasoningFromString(block.content);
386
- block.content = cleaned;
387
- collector.push(...segments);
388
- }
389
- }
390
- function flattenAnthropicText(source) {
391
- if (typeof source === 'string') {
392
- return source;
393
- }
394
- if (Array.isArray(source)) {
395
- return source.map((entry) => flattenAnthropicText(entry)).filter(Boolean).join('');
396
- }
397
- if (isRecord(source)) {
398
- if (typeof source.text === 'string') {
399
- return source.text;
400
- }
401
- if (typeof source.content === 'string') {
402
- return source.content;
403
- }
404
- if (Array.isArray(source.content)) {
405
- return source.content.map((entry) => flattenAnthropicText(entry)).filter(Boolean).join('');
406
- }
407
- }
408
- return '';
409
56
  }