@jsonstudio/llms 0.6.3541 → 0.6.3685

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 (100) hide show
  1. package/dist/conversion/compat/actions/antigravity-thought-signature-cache.js +23 -114
  2. package/dist/conversion/compat/actions/auto-thinking.js +3 -2
  3. package/dist/conversion/compat/actions/deepseek-web-response.js +9 -50
  4. package/dist/conversion/compat/actions/field-mapping.js +2 -153
  5. package/dist/conversion/compat/actions/gemini-cli-request.d.ts +2 -0
  6. package/dist/conversion/compat/actions/gemini-cli-request.js +1 -1
  7. package/dist/conversion/compat/actions/glm-history-image-trim.js +3 -37
  8. package/dist/conversion/compat/actions/glm-image-content.js +3 -32
  9. package/dist/conversion/compat/actions/glm-native-compat.d.ts +6 -0
  10. package/dist/conversion/compat/actions/glm-native-compat.js +34 -0
  11. package/dist/conversion/compat/actions/glm-vision-prompt.js +3 -76
  12. package/dist/conversion/compat/actions/glm-web-search.js +10 -43
  13. package/dist/conversion/compat/actions/iflow-kimi-cli-defaults.js +4 -53
  14. package/dist/conversion/compat/actions/iflow-kimi-history-media-placeholder.js +5 -141
  15. package/dist/conversion/compat/actions/iflow-kimi-thinking-reasoning-fill.js +7 -28
  16. package/dist/conversion/compat/actions/iflow-native-compat.d.ts +6 -0
  17. package/dist/conversion/compat/actions/iflow-native-compat.js +36 -0
  18. package/dist/conversion/compat/actions/iflow-response-body-unwrap.js +4 -119
  19. package/dist/conversion/compat/actions/iflow-web-search.js +14 -55
  20. package/dist/conversion/compat/actions/lmstudio-responses-input-stringify.js +3 -104
  21. package/dist/conversion/hub/node-support.js +1 -1
  22. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +9 -1
  23. package/dist/conversion/hub/operation-table/semantic-mappers/archive/chat-mapper.archive.js +5 -0
  24. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +34 -14
  25. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +14 -14
  26. package/dist/conversion/hub/pipeline/hub-pipeline.js +838 -524
  27. package/dist/conversion/hub/pipeline/hub-stage-timing.d.ts +6 -0
  28. package/dist/conversion/hub/pipeline/hub-stage-timing.js +178 -0
  29. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.js +6 -4
  30. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +46 -0
  31. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/context-capture-orchestration.d.ts +3 -0
  32. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/context-capture-orchestration.js +2 -1
  33. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/context-factories.js +2 -0
  34. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +1 -0
  35. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/responses-context-snapshot.d.ts +3 -2
  36. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/responses-context-snapshot.js +18 -5
  37. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/context-merge.d.ts +1 -2
  38. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/context-merge.js +0 -16
  39. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.d.ts +1 -1
  40. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +30 -12
  41. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.d.ts +1 -0
  42. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +5 -2
  43. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.d.ts +1 -1
  44. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.js +9 -5
  45. package/dist/conversion/hub/process/chat-process-continue-execution.js +2 -4
  46. package/dist/conversion/hub/process/chat-process-governance-orchestration.js +3 -1
  47. package/dist/conversion/hub/process/chat-process-media.d.ts +1 -0
  48. package/dist/conversion/hub/process/chat-process-media.js +36 -0
  49. package/dist/conversion/hub/process/chat-process-session-usage.d.ts +25 -0
  50. package/dist/conversion/hub/process/chat-process-session-usage.js +246 -0
  51. package/dist/conversion/hub/response/provider-response.js +13 -0
  52. package/dist/conversion/hub/types/chat-envelope.d.ts +1 -0
  53. package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.js +0 -4
  54. package/dist/conversion/responses/responses-openai-bridge/response-payload.js +0 -12
  55. package/dist/conversion/responses/responses-openai-bridge/types.d.ts +1 -9
  56. package/dist/conversion/responses/responses-openai-bridge.d.ts +1 -0
  57. package/dist/conversion/responses/responses-openai-bridge.js +51 -24
  58. package/dist/conversion/shared/anthropic-message-utils.js +14 -1
  59. package/dist/conversion/shared/reasoning-normalizer.js +61 -0
  60. package/dist/conversion/shared/tool-governor.js +2 -4
  61. package/dist/native/router_hotpath_napi.node +0 -0
  62. package/dist/quota/quota-state.js +1 -6
  63. package/dist/router/virtual-router/bootstrap/profile-builder.js +1 -0
  64. package/dist/router/virtual-router/bootstrap/provider-normalization.d.ts +1 -0
  65. package/dist/router/virtual-router/bootstrap/provider-normalization.js +6 -0
  66. package/dist/router/virtual-router/bootstrap.js +1 -6
  67. package/dist/router/virtual-router/engine/routing-state/store.js +21 -2
  68. package/dist/router/virtual-router/engine-legacy.js +43 -0
  69. package/dist/router/virtual-router/engine-logging.d.ts +3 -0
  70. package/dist/router/virtual-router/engine-logging.js +29 -3
  71. package/dist/router/virtual-router/engine-selection/native-chat-process-governed-filter-semantics.d.ts +1 -0
  72. package/dist/router/virtual-router/engine-selection/native-chat-process-governed-filter-semantics.js +1 -0
  73. package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.d.ts +3 -0
  74. package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.js +72 -0
  75. package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.d.ts +1 -1
  76. package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.js +1 -1
  77. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-inbound-outbound-semantics.d.ts +0 -1
  78. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-inbound-outbound-semantics.js +0 -29
  79. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-req-process-semantics.d.ts +1 -0
  80. package/dist/router/virtual-router/engine-selection/native-router-hotpath-loader.js +6 -2
  81. package/dist/router/virtual-router/engine.js +28 -13
  82. package/dist/router/virtual-router/provider-registry.js +1 -0
  83. package/dist/router/virtual-router/routing-instructions/state.js +44 -2
  84. package/dist/router/virtual-router/routing-instructions/types.d.ts +6 -0
  85. package/dist/router/virtual-router/token-estimator.js +21 -0
  86. package/dist/router/virtual-router/types.d.ts +7 -0
  87. package/dist/servertool/engine.js +3 -34
  88. package/dist/servertool/handlers/followup-request-builder.js +0 -6
  89. package/dist/servertool/handlers/gemini-empty-reply-continue.js +3 -274
  90. package/dist/servertool/handlers/stop-message-auto/runtime-utils.d.ts +0 -3
  91. package/dist/servertool/handlers/stop-message-auto/runtime-utils.js +0 -29
  92. package/dist/servertool/handlers/stop-message-auto.js +11 -9
  93. package/dist/servertool/handlers/vision.js +4 -1
  94. package/dist/servertool/server-side-tools.d.ts +0 -1
  95. package/dist/servertool/server-side-tools.js +67 -5
  96. package/dist/tools/apply-patch/execution-capturer.d.ts +1 -1
  97. package/dist/tools/apply-patch/execution-capturer.js +1 -2
  98. package/dist/tools/apply-patch/regression-capturer.js +2 -1
  99. package/dist/tools/tool-registry.js +1 -2
  100. package/package.json +1 -1
@@ -10,6 +10,42 @@ export function stripHistoricalImageAttachments(messages) {
10
10
  }
11
11
  return stripped.messages;
12
12
  }
13
+ const INLINE_MEDIA_DATA_RE = /data:(image|video)\/[a-z0-9.+-]+;base64,[a-z0-9+/=\s]+/i;
14
+ function isVisualToolMessage(message) {
15
+ if (!message || typeof message !== 'object') {
16
+ return false;
17
+ }
18
+ if (message.role !== 'tool') {
19
+ return false;
20
+ }
21
+ const name = typeof message.name === 'string' ? message.name.trim().toLowerCase() : '';
22
+ if (name === 'view_image') {
23
+ return true;
24
+ }
25
+ const content = typeof message.content === 'string' ? message.content : '';
26
+ return INLINE_MEDIA_DATA_RE.test(content);
27
+ }
28
+ export function stripHistoricalVisualToolOutputs(messages) {
29
+ if (!Array.isArray(messages) || messages.length === 0) {
30
+ return messages;
31
+ }
32
+ let changed = false;
33
+ const next = messages.map((message) => {
34
+ if (!isVisualToolMessage(message)) {
35
+ return message;
36
+ }
37
+ const content = typeof message.content === 'string' ? message.content : '';
38
+ if (!INLINE_MEDIA_DATA_RE.test(content)) {
39
+ return message;
40
+ }
41
+ changed = true;
42
+ return {
43
+ ...message,
44
+ content: '[Image omitted]'
45
+ };
46
+ });
47
+ return changed ? next : messages;
48
+ }
13
49
  export function containsImageAttachment(messages) {
14
50
  if (!Array.isArray(messages) || !messages.length) {
15
51
  return false;
@@ -0,0 +1,25 @@
1
+ import type { ProcessedRequest, StandardizedRequest } from '../types/standardized.js';
2
+ type SessionScopeMetadata = {
3
+ sessionId?: string;
4
+ conversationId?: string;
5
+ };
6
+ type ChatProcessSessionRequestSnapshot = Pick<StandardizedRequest | ProcessedRequest, 'messages' | 'tools' | 'parameters'>;
7
+ export interface ChatProcessSessionInputEstimateResult {
8
+ tokens?: number;
9
+ scope?: string;
10
+ mode: 'session_reuse' | 'session_delta' | 'unavailable';
11
+ reason?: 'missing_scope' | 'missing_state' | 'missing_previous_usage' | 'tools_signature_changed' | 'parameters_signature_changed' | 'message_count_regressed' | 'boundary_mismatch';
12
+ previousMessageCount?: number;
13
+ appendedMessageCount?: number;
14
+ hasPreviousTokens?: boolean;
15
+ hasPreviousMessageCount?: boolean;
16
+ hasToolsSignature?: boolean;
17
+ hasParametersSignature?: boolean;
18
+ previousParametersSignatureDigest?: string;
19
+ currentParametersSignatureDigest?: string;
20
+ }
21
+ export declare function estimateChatProcessSessionInputTokens(metadata: SessionScopeMetadata, request: StandardizedRequest | ProcessedRequest): number | undefined;
22
+ export declare function estimateChatProcessSessionInputTokensDetailed(metadata: SessionScopeMetadata, request: StandardizedRequest | ProcessedRequest): ChatProcessSessionInputEstimateResult;
23
+ export declare function saveChatProcessSessionInputEstimate(metadata: SessionScopeMetadata, request: StandardizedRequest | ProcessedRequest, estimatedInputTokens: number): void;
24
+ export declare function saveChatProcessSessionActualUsage(metadata: SessionScopeMetadata, usageRaw: unknown, requestSnapshot?: ChatProcessSessionRequestSnapshot): void;
25
+ export {};
@@ -0,0 +1,246 @@
1
+ import { loadRoutingInstructionStateSync, saveRoutingInstructionStateSync } from '../../../router/virtual-router/sticky-session-store.js';
2
+ import { countRequestTokens } from '../../../router/virtual-router/token-counter.js';
3
+ function buildEmptyRoutingInstructionState() {
4
+ return {
5
+ forcedTarget: undefined,
6
+ stickyTarget: undefined,
7
+ preferTarget: undefined,
8
+ allowedProviders: new Set(),
9
+ disabledProviders: new Set(),
10
+ disabledKeys: new Map(),
11
+ disabledModels: new Map(),
12
+ stopMessageSource: undefined,
13
+ stopMessageText: undefined,
14
+ stopMessageMaxRepeats: undefined,
15
+ stopMessageUsed: undefined,
16
+ stopMessageUpdatedAt: undefined,
17
+ stopMessageLastUsedAt: undefined,
18
+ stopMessageStageMode: undefined,
19
+ stopMessageAiMode: undefined,
20
+ stopMessageAiSeedPrompt: undefined,
21
+ stopMessageAiHistory: undefined,
22
+ preCommandSource: undefined,
23
+ preCommandScriptPath: undefined,
24
+ preCommandUpdatedAt: undefined,
25
+ chatProcessInputTokens: undefined,
26
+ chatProcessMessageCount: undefined,
27
+ chatProcessToolsSignature: undefined,
28
+ chatProcessParametersSignature: undefined,
29
+ chatProcessBoundarySignature: undefined,
30
+ chatProcessUpdatedAt: undefined
31
+ };
32
+ }
33
+ function readScope(metadata) {
34
+ const sessionId = typeof metadata.sessionId === 'string' ? metadata.sessionId.trim() : '';
35
+ if (sessionId) {
36
+ return `session:${sessionId}`;
37
+ }
38
+ const conversationId = typeof metadata.conversationId === 'string' ? metadata.conversationId.trim() : '';
39
+ if (conversationId) {
40
+ return `conversation:${conversationId}`;
41
+ }
42
+ return undefined;
43
+ }
44
+ function stableSignature(value) {
45
+ try {
46
+ return JSON.stringify(value ?? null) || 'null';
47
+ }
48
+ catch {
49
+ return 'unserializable';
50
+ }
51
+ }
52
+ function messageBoundarySignature(message) {
53
+ if (!message) {
54
+ return undefined;
55
+ }
56
+ return stableSignature({
57
+ role: message.role,
58
+ content: message.content ?? null,
59
+ tool_calls: Array.isArray(message.tool_calls) ? message.tool_calls : [],
60
+ tool_call_id: message.tool_call_id ?? null,
61
+ name: message.name ?? null
62
+ });
63
+ }
64
+ function coercePositiveInteger(value) {
65
+ return typeof value === 'number' && Number.isFinite(value) && value > 0
66
+ ? Math.max(1, Math.round(value))
67
+ : undefined;
68
+ }
69
+ function digestSignature(value) {
70
+ if (!value) {
71
+ return undefined;
72
+ }
73
+ let hash = 2166136261;
74
+ for (let i = 0; i < value.length; i += 1) {
75
+ hash ^= value.charCodeAt(i);
76
+ hash = Math.imul(hash, 16777619);
77
+ }
78
+ return `${value.length}:${(hash >>> 0).toString(16)}`;
79
+ }
80
+ function hasReusableSessionUsageShape(state) {
81
+ if (!state) {
82
+ return false;
83
+ }
84
+ const previousTokens = coercePositiveInteger(state.chatProcessInputTokens);
85
+ const previousMessageCount = typeof state.chatProcessMessageCount === 'number' &&
86
+ Number.isFinite(state.chatProcessMessageCount) &&
87
+ state.chatProcessMessageCount >= 0;
88
+ const toolsSignature = typeof state.chatProcessToolsSignature === 'string' && state.chatProcessToolsSignature.trim().length > 0;
89
+ const parametersSignature = typeof state.chatProcessParametersSignature === 'string' &&
90
+ state.chatProcessParametersSignature.trim().length > 0;
91
+ return previousTokens !== undefined && previousMessageCount && toolsSignature && parametersSignature;
92
+ }
93
+ function applyRequestSnapshotToState(state, request) {
94
+ if (!request || typeof request !== 'object') {
95
+ return false;
96
+ }
97
+ const messages = Array.isArray(request.messages) ? request.messages : null;
98
+ if (!messages) {
99
+ return false;
100
+ }
101
+ state.chatProcessMessageCount = messages.length;
102
+ state.chatProcessToolsSignature = stableSignature(request.tools ?? null);
103
+ state.chatProcessParametersSignature = stableSignature(request.parameters ?? null);
104
+ state.chatProcessBoundarySignature = messageBoundarySignature(messages[messages.length - 1]);
105
+ return true;
106
+ }
107
+ export function estimateChatProcessSessionInputTokens(metadata, request) {
108
+ return estimateChatProcessSessionInputTokensDetailed(metadata, request).tokens;
109
+ }
110
+ export function estimateChatProcessSessionInputTokensDetailed(metadata, request) {
111
+ const scope = readScope(metadata);
112
+ if (!scope) {
113
+ return {
114
+ scope,
115
+ mode: 'unavailable',
116
+ reason: 'missing_scope'
117
+ };
118
+ }
119
+ const state = loadRoutingInstructionStateSync(scope);
120
+ if (!state) {
121
+ return {
122
+ scope,
123
+ mode: 'unavailable',
124
+ reason: 'missing_state'
125
+ };
126
+ }
127
+ const previousTokens = coercePositiveInteger(state.chatProcessInputTokens);
128
+ const previousMessageCount = typeof state.chatProcessMessageCount === 'number' &&
129
+ Number.isFinite(state.chatProcessMessageCount) &&
130
+ state.chatProcessMessageCount >= 0
131
+ ? Math.max(0, Math.floor(state.chatProcessMessageCount))
132
+ : undefined;
133
+ if (previousTokens === undefined || previousMessageCount === undefined) {
134
+ return {
135
+ scope,
136
+ mode: 'unavailable',
137
+ reason: 'missing_previous_usage',
138
+ hasPreviousTokens: previousTokens !== undefined,
139
+ hasPreviousMessageCount: previousMessageCount !== undefined,
140
+ hasToolsSignature: Boolean(state.chatProcessToolsSignature),
141
+ hasParametersSignature: Boolean(state.chatProcessParametersSignature)
142
+ };
143
+ }
144
+ const currentToolsSignature = stableSignature(request.tools ?? null);
145
+ const currentParametersSignature = stableSignature(request.parameters ?? null);
146
+ if (state.chatProcessToolsSignature !== currentToolsSignature) {
147
+ return {
148
+ scope,
149
+ mode: 'unavailable',
150
+ reason: 'tools_signature_changed',
151
+ previousMessageCount,
152
+ hasPreviousTokens: true,
153
+ hasPreviousMessageCount: true
154
+ };
155
+ }
156
+ if (state.chatProcessParametersSignature !== currentParametersSignature) {
157
+ return {
158
+ scope,
159
+ mode: 'unavailable',
160
+ reason: 'parameters_signature_changed',
161
+ previousMessageCount,
162
+ hasPreviousTokens: true,
163
+ hasPreviousMessageCount: true,
164
+ previousParametersSignatureDigest: digestSignature(state.chatProcessParametersSignature),
165
+ currentParametersSignatureDigest: digestSignature(currentParametersSignature)
166
+ };
167
+ }
168
+ const messages = Array.isArray(request.messages) ? request.messages : [];
169
+ if (previousMessageCount > messages.length) {
170
+ return {
171
+ scope,
172
+ mode: 'unavailable',
173
+ reason: 'message_count_regressed',
174
+ previousMessageCount
175
+ };
176
+ }
177
+ if (previousMessageCount > 0) {
178
+ const boundary = messageBoundarySignature(messages[previousMessageCount - 1]);
179
+ if ((state.chatProcessBoundarySignature || undefined) !== boundary) {
180
+ return {
181
+ scope,
182
+ mode: 'unavailable',
183
+ reason: 'boundary_mismatch',
184
+ previousMessageCount
185
+ };
186
+ }
187
+ }
188
+ const appendedMessages = messages.slice(previousMessageCount);
189
+ if (appendedMessages.length === 0) {
190
+ return {
191
+ tokens: previousTokens,
192
+ scope,
193
+ mode: 'session_reuse',
194
+ previousMessageCount,
195
+ appendedMessageCount: 0
196
+ };
197
+ }
198
+ const delta = countRequestTokens({
199
+ model: request.model,
200
+ messages: appendedMessages,
201
+ tools: undefined,
202
+ parameters: {},
203
+ metadata: request.metadata ?? { originalEndpoint: '' }
204
+ });
205
+ return {
206
+ tokens: previousTokens + delta,
207
+ scope,
208
+ mode: 'session_delta',
209
+ previousMessageCount,
210
+ appendedMessageCount: appendedMessages.length
211
+ };
212
+ }
213
+ export function saveChatProcessSessionInputEstimate(metadata, request, estimatedInputTokens) {
214
+ const scope = readScope(metadata);
215
+ const normalized = coercePositiveInteger(estimatedInputTokens);
216
+ if (!scope || normalized === undefined) {
217
+ return;
218
+ }
219
+ const state = loadRoutingInstructionStateSync(scope) ?? buildEmptyRoutingInstructionState();
220
+ const messages = Array.isArray(request.messages) ? request.messages : [];
221
+ state.chatProcessInputTokens = normalized;
222
+ state.chatProcessMessageCount = messages.length;
223
+ state.chatProcessToolsSignature = stableSignature(request.tools ?? null);
224
+ state.chatProcessParametersSignature = stableSignature(request.parameters ?? null);
225
+ state.chatProcessBoundarySignature = messageBoundarySignature(messages[messages.length - 1]);
226
+ state.chatProcessUpdatedAt = Date.now();
227
+ saveRoutingInstructionStateSync(scope, state);
228
+ }
229
+ export function saveChatProcessSessionActualUsage(metadata, usageRaw, requestSnapshot) {
230
+ const scope = readScope(metadata);
231
+ if (!scope || !usageRaw || typeof usageRaw !== 'object' || Array.isArray(usageRaw)) {
232
+ return;
233
+ }
234
+ const usage = usageRaw;
235
+ const actualInputTokens = coercePositiveInteger(usage.prompt_tokens ?? usage.input_tokens ?? usage.total_input_tokens);
236
+ if (actualInputTokens === undefined) {
237
+ return;
238
+ }
239
+ const state = loadRoutingInstructionStateSync(scope) ?? buildEmptyRoutingInstructionState();
240
+ if (!hasReusableSessionUsageShape(state) && !applyRequestSnapshotToState(state, requestSnapshot)) {
241
+ return;
242
+ }
243
+ state.chatProcessInputTokens = actualInputTokens;
244
+ state.chatProcessUpdatedAt = Date.now();
245
+ saveRoutingInstructionStateSync(scope, state);
246
+ }
@@ -15,6 +15,7 @@ import { runRespProcessStage3ServerToolOrchestration } from '../pipeline/stages/
15
15
  import { runRespOutboundStage1ClientRemap } from '../pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js';
16
16
  import { runRespOutboundStage2SseStream } from '../pipeline/stages/resp_outbound/resp_outbound_stage2_sse_stream/index.js';
17
17
  import { recordResponsesResponse } from '../../shared/responses-conversation-store.js';
18
+ import { saveChatProcessSessionActualUsage } from '../process/chat-process-session-usage.js';
18
19
  import { ProviderProtocolError } from '../../provider-protocol-error.js';
19
20
  import { readRuntimeMetadata } from '../../runtime-metadata.js';
20
21
  import { commitClockReservation, resolveClockConfig } from '../../../servertool/clock/task-store.js';
@@ -397,6 +398,18 @@ export async function convertProviderResponse(options) {
397
398
  });
398
399
  applyModelOverride(clientPayload, displayModel);
399
400
  stripInternalPolicyDebugFields(clientPayload);
401
+ try {
402
+ const usage = clientPayload && typeof clientPayload === 'object' && !Array.isArray(clientPayload)
403
+ ? clientPayload.usage
404
+ : undefined;
405
+ saveChatProcessSessionActualUsage({
406
+ sessionId: typeof options.context.sessionId === 'string' ? options.context.sessionId : undefined,
407
+ conversationId: typeof options.context.conversationId === 'string' ? options.context.conversationId : undefined
408
+ }, usage, isJsonRecord(options.context.capturedChatRequest) ? options.context.capturedChatRequest : undefined);
409
+ }
410
+ catch {
411
+ // best-effort: usage backfill must not break response conversion
412
+ }
400
413
  if (clientProtocol === 'openai-responses') {
401
414
  try {
402
415
  recordResponsesResponse({
@@ -53,6 +53,7 @@ export interface AdapterContext {
53
53
  originalModelId?: string;
54
54
  clientModelId?: string;
55
55
  toolCallIdStyle?: 'fc' | 'preserve';
56
+ applyPatchToolMode?: 'schema' | 'freeform';
56
57
  responsesResume?: JsonObject;
57
58
  [key: string]: JsonValue;
58
59
  }
@@ -142,10 +142,6 @@ export class OpenAIOpenAIPipelineCodec {
142
142
  if (!Array.isArray(openaiPayload.tools) && Array.isArray(inboundPayload.tools)) {
143
143
  openaiPayload.tools = inboundPayload.tools;
144
144
  }
145
- if (openaiPayload.tool_choice === undefined &&
146
- inboundPayload.tool_choice !== undefined) {
147
- openaiPayload.tool_choice = inboundPayload.tool_choice;
148
- }
149
145
  const filterContext = {
150
146
  ...context,
151
147
  requestId,
@@ -55,10 +55,6 @@ function collectRetentionContext(context) {
55
55
  const stripHostManagedFields = shouldStripHostManagedFields(context);
56
56
  return {
57
57
  metadata: context?.metadata,
58
- parallelToolCalls: context?.parallel_tool_calls,
59
- toolChoice: context?.tool_choice,
60
- include: context?.include,
61
- store: context?.store,
62
58
  stripHostManagedFields
63
59
  };
64
60
  }
@@ -103,10 +99,6 @@ export function buildResponsesPayloadFromChat(payload, context) {
103
99
  requestId: context?.requestId,
104
100
  toolsRaw: Array.isArray(context?.toolsRaw) ? context?.toolsRaw : [],
105
101
  metadata: retentionContext.metadata,
106
- parallelToolCalls: retentionContext.parallelToolCalls,
107
- toolChoice: retentionContext.toolChoice,
108
- include: retentionContext.include,
109
- store: retentionContext.store,
110
102
  stripHostManagedFields: retentionContext.stripHostManagedFields,
111
103
  sourceForRetention: sourceForRetention
112
104
  });
@@ -176,10 +168,6 @@ export function buildResponsesPayloadFromChat(payload, context) {
176
168
  requestId: context?.requestId,
177
169
  toolsRaw: Array.isArray(context?.toolsRaw) ? context?.toolsRaw : [],
178
170
  metadata: retentionContext.metadata,
179
- parallelToolCalls: retentionContext.parallelToolCalls,
180
- toolChoice: retentionContext.toolChoice,
181
- include: retentionContext.include,
182
- store: retentionContext.store,
183
171
  stripHostManagedFields: retentionContext.stripHostManagedFields,
184
172
  sourceForRetention: sourceForRetention
185
173
  });
@@ -1,6 +1,6 @@
1
1
  import type { BridgeInputItem, BridgeToolDefinition } from '../../types/bridge-message-types.js';
2
2
  import type { ChatToolDefinition } from '../../hub/types/chat-envelope.js';
3
- import type { JsonObject, JsonValue } from '../../hub/types/json.js';
3
+ import type { JsonObject } from '../../hub/types/json.js';
4
4
  import type { ToolCallIdStyle } from '../../shared/responses-tool-utils.js';
5
5
  export type Unknown = Record<string, unknown>;
6
6
  export interface ResponsesRequestContext extends Unknown {
@@ -8,15 +8,7 @@ export interface ResponsesRequestContext extends Unknown {
8
8
  targetProtocol?: string;
9
9
  originalSystemMessages?: string[];
10
10
  input?: BridgeInputItem[];
11
- include?: unknown;
12
- store?: unknown;
13
- serviceTier?: unknown;
14
- truncation?: unknown;
15
- toolChoice?: unknown;
16
- parallelToolCalls?: boolean;
17
11
  metadata?: JsonObject;
18
- responseFormat?: JsonValue;
19
- stream?: boolean;
20
12
  isChatPayload?: boolean;
21
13
  isResponsesPayload?: boolean;
22
14
  historyMessages?: Array<{
@@ -10,6 +10,7 @@ export declare function buildChatRequestFromResponses(payload: Record<string, un
10
10
  export declare function buildResponsesRequestFromChat(payload: Record<string, unknown>, ctx?: ResponsesRequestContext, extras?: {
11
11
  bridgeHistory?: BridgeInputBuildResult;
12
12
  systemInstruction?: string;
13
+ routeToolCallIdStyle?: 'fc' | 'preserve';
13
14
  }): BuildResponsesRequestResult;
14
15
  export { buildResponsesPayloadFromChat, extractRequestIdFromResponse } from './responses-openai-bridge/response-payload.js';
15
16
  export { buildChatResponseFromResponses } from '../shared/responses-response-utils.js';
@@ -4,6 +4,7 @@ import { convertBridgeInputToChatMessages } from '../bridge-message-utils.js';
4
4
  import { createToolCallIdTransformer, enforceToolCallIdStyle, sanitizeResponsesFunctionName } from '../shared/responses-tool-utils.js';
5
5
  import { mapChatToolsToBridge } from '../shared/tool-mapping.js';
6
6
  import { ProviderProtocolError } from '../provider-protocol-error.js';
7
+ import { isJsonObject, jsonClone } from '../hub/types/json.js';
7
8
  import { captureReqInboundResponsesContextSnapshotWithNative, mapReqInboundBridgeToolsToChatWithNative } from '../../router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.js';
8
9
  import { appendLocalImageBlockOnLatestUserInputWithNative, buildBridgeHistoryWithNative, filterBridgeInputForUpstreamWithNative, normalizeBridgeHistorySeedWithNative, prepareResponsesRequestEnvelopeWithNative, resolveResponsesRequestBridgeDecisionsWithNative, resolveResponsesBridgeToolsWithNative, runBridgeActionPipelineWithNative } from '../../router/virtual-router/engine-selection/native-hub-bridge-action-semantics.js';
9
10
  // --- Utilities (ported strictly) ---
@@ -50,7 +51,7 @@ export function captureResponsesContext(payload, dto) {
50
51
  const captured = captureReqInboundResponsesContextSnapshotWithNative({
51
52
  rawRequest: requestForCapture,
52
53
  requestId: dto?.route?.requestId,
53
- toolCallIdStyle: payload?.toolCallIdStyle ?? payload?.metadata?.toolCallIdStyle
54
+ toolCallIdStyle: payload?.toolCallIdStyle
54
55
  });
55
56
  const instructionReasoning = payload?.__rcc_reasoning_instructions;
56
57
  if (instructionReasoning !== undefined && instructionReasoning !== null) {
@@ -68,6 +69,12 @@ export function captureResponsesContext(payload, dto) {
68
69
  if (!captured.systemInstruction && typeof payload.instructions === 'string' && payload.instructions.trim().length) {
69
70
  captured.systemInstruction = payload.instructions;
70
71
  }
72
+ if (captured.metadata && isJsonObject(captured.metadata)) {
73
+ const cloned = jsonClone(captured.metadata);
74
+ delete cloned.toolCallIdStyle;
75
+ delete cloned.extraFields;
76
+ captured.metadata = cloned;
77
+ }
71
78
  return captured;
72
79
  }
73
80
  export function buildChatRequestFromResponses(payload, context) {
@@ -165,9 +172,6 @@ export function buildResponsesRequestFromChat(payload, ctx, extras) {
165
172
  const chat = unwrapData(payload);
166
173
  const out = {};
167
174
  const envelopeMetadata = ctx?.metadata && typeof ctx.metadata === 'object' ? ctx.metadata : undefined;
168
- const requestMetadata = chat && typeof chat === 'object' && chat.metadata && typeof chat.metadata === 'object'
169
- ? chat.metadata
170
- : undefined;
171
175
  // 基本字段
172
176
  out.model = chat.model;
173
177
  let messages = Array.isArray(chat.messages) ? chat.messages : [];
@@ -197,7 +201,7 @@ export function buildResponsesRequestFromChat(payload, ctx, extras) {
197
201
  const metadataExtraFields = extractMetadataExtraFields(envelopeMetadata);
198
202
  const bridgeDecisions = resolveResponsesRequestBridgeDecisionsWithNative({
199
203
  context: ctx && typeof ctx === 'object' ? ctx : undefined,
200
- requestMetadata,
204
+ routeToolCallIdStyle: extras?.routeToolCallIdStyle,
201
205
  envelopeMetadata,
202
206
  bridgeMetadata,
203
207
  extraBridgeHistory: extras?.bridgeHistory
@@ -270,29 +274,27 @@ export function buildResponsesRequestFromChat(payload, ctx, extras) {
270
274
  metadataSystemInstruction: envelopeMetadata?.systemInstruction,
271
275
  combinedSystemInstruction,
272
276
  reasoningInstructionSegments: ctx?.__rcc_reasoning_instructions_segments,
273
- contextParameters: ctx?.parameters,
277
+ contextParameters: undefined,
274
278
  chatParameters: chat.parameters,
275
- metadataParameters: metadataExtraFields?.parameters,
276
- contextStream: ctx?.stream,
277
- metadataStream: metadataExtraFields?.stream,
279
+ metadataParameters: stripToolControlFieldsFromParameterObject(metadataExtraFields?.parameters),
280
+ contextStream: undefined,
281
+ metadataStream: undefined,
278
282
  chatStream: streamFromChat,
279
283
  chatParametersStream: streamFromParameters,
280
- contextInclude: ctx?.include,
281
- metadataInclude: metadataExtraFields?.include,
282
- contextStore: ctx?.store,
283
- metadataStore: metadataExtraFields?.store,
284
+ contextInclude: undefined,
285
+ metadataInclude: undefined,
286
+ contextStore: undefined,
287
+ metadataStore: undefined,
284
288
  stripHostFields,
285
- contextToolChoice: ctx?.toolChoice,
286
- metadataToolChoice: metadataExtraFields?.tool_choice,
287
- contextParallelToolCalls: ctx?.parallelToolCalls,
288
- metadataParallelToolCalls: metadataExtraFields?.parallel_tool_calls,
289
- contextResponseFormat: ctx?.responseFormat,
290
- metadataResponseFormat: metadataExtraFields?.response_format,
291
- contextServiceTier: ctx?.serviceTier,
292
- metadataServiceTier: metadataExtraFields?.service_tier,
293
- contextTruncation: ctx?.truncation,
294
- metadataTruncation: metadataExtraFields?.truncation,
295
- contextMetadata: ctx?.metadata,
289
+ contextToolChoice: undefined,
290
+ contextParallelToolCalls: undefined,
291
+ contextResponseFormat: undefined,
292
+ metadataResponseFormat: undefined,
293
+ contextServiceTier: undefined,
294
+ metadataServiceTier: undefined,
295
+ contextTruncation: undefined,
296
+ metadataTruncation: undefined,
297
+ contextMetadata: stripToolControlFieldsFromContextMetadata(ctx?.metadata),
296
298
  metadataMetadata: metadataExtraFields?.metadata
297
299
  });
298
300
  Object.assign(out, preparedEnvelope.request);
@@ -326,6 +328,31 @@ function extractMetadataExtraFields(metadata) {
326
328
  }
327
329
  return undefined;
328
330
  }
331
+ function stripToolControlFieldsFromContextMetadata(metadata) {
332
+ if (!metadata) {
333
+ return undefined;
334
+ }
335
+ const cloned = jsonClone(metadata);
336
+ const extras = cloned.extraFields;
337
+ if (!extras || !isPlainObject(extras)) {
338
+ return cloned;
339
+ }
340
+ delete extras.tool_choice;
341
+ delete extras.parallel_tool_calls;
342
+ if (Object.keys(extras).length === 0) {
343
+ delete cloned.extraFields;
344
+ }
345
+ return cloned;
346
+ }
347
+ function stripToolControlFieldsFromParameterObject(value) {
348
+ if (!value) {
349
+ return undefined;
350
+ }
351
+ const cloned = jsonClone(value);
352
+ delete cloned.tool_choice;
353
+ delete cloned.parallel_tool_calls;
354
+ return Object.keys(cloned).length ? cloned : undefined;
355
+ }
329
356
  function isPlainObject(value) {
330
357
  return Boolean(value && typeof value === 'object' && !Array.isArray(value));
331
358
  }
@@ -16,6 +16,9 @@ function safeJson(v) {
16
16
  return '{}';
17
17
  }
18
18
  }
19
+ function pickBoolean(value) {
20
+ return typeof value === 'boolean' ? value : undefined;
21
+ }
19
22
  function stripOpenAIChatToolAliasFields(messages) {
20
23
  // No-op: preserve tool_call_id/call_id for downstream consumers and regression parity.
21
24
  void messages;
@@ -202,7 +205,8 @@ const ANTHROPIC_TOP_LEVEL_FIELDS = new Set([
202
205
  'metadata',
203
206
  'stream',
204
207
  'tool_choice',
205
- 'thinking'
208
+ 'thinking',
209
+ 'disable_parallel_tool_use'
206
210
  ]);
207
211
  const ANTHROPIC_STABLE_TOOL_SCHEMA_NAMES = new Set([
208
212
  'exec_command',
@@ -526,6 +530,10 @@ export function buildOpenAIChatFromAnthropic(payload, options) {
526
530
  }
527
531
  if ('tool_choice' in body)
528
532
  request.tool_choice = body.tool_choice;
533
+ const disableParallelToolUse = pickBoolean(body.disable_parallel_tool_use ?? body.disableParallelToolUse);
534
+ if (disableParallelToolUse !== undefined) {
535
+ request.parallel_tool_calls = !disableParallelToolUse;
536
+ }
529
537
  const normalizedTools = mapAnthropicToolsToChat(body.tools);
530
538
  if (normalizedTools !== undefined) {
531
539
  request.tools = normalizedTools;
@@ -1077,6 +1085,11 @@ export function buildAnthropicRequestFromOpenAIChat(chatReq) {
1077
1085
  if (normalizedToolChoice !== undefined) {
1078
1086
  out.tool_choice = normalizedToolChoice;
1079
1087
  }
1088
+ const parallelToolCalls = pickBoolean(requestBody.parallel_tool_calls
1089
+ ?? requestBody.parallelToolCalls);
1090
+ if (parallelToolCalls !== undefined) {
1091
+ out.disable_parallel_tool_use = !parallelToolCalls;
1092
+ }
1080
1093
  if (requestBody.thinking !== undefined) {
1081
1094
  try {
1082
1095
  out.thinking = JSON.parse(JSON.stringify(requestBody.thinking));
@@ -1,5 +1,63 @@
1
1
  import { normalizeReasoningInAnthropicPayloadWithNative, normalizeReasoningInChatPayloadWithNative, normalizeReasoningInGeminiPayloadWithNative, normalizeReasoningInOpenAIPayloadWithNative, normalizeReasoningInResponsesPayloadWithNative } from '../../router/virtual-router/engine-selection/native-shared-conversion-semantics.js';
2
2
  export const RESPONSES_INSTRUCTIONS_REASONING_FIELD = '__rcc_reasoning_instructions';
3
+ function textMayContainReasoningMarkup(text) {
4
+ if (!text || (text.indexOf('<') === -1 && text.indexOf('`') === -1)) {
5
+ return false;
6
+ }
7
+ const lower = text.toLowerCase();
8
+ return (lower.includes('<think') ||
9
+ lower.includes('<reflection') ||
10
+ lower.includes('</think>') ||
11
+ lower.includes('</reflection>') ||
12
+ lower.includes('```think') ||
13
+ lower.includes('```reflection') ||
14
+ lower.includes('``` think') ||
15
+ lower.includes('``` reflection'));
16
+ }
17
+ function valueMayContainReasoningMarkup(value) {
18
+ if (typeof value === 'string') {
19
+ return textMayContainReasoningMarkup(value);
20
+ }
21
+ if (Array.isArray(value)) {
22
+ for (const entry of value) {
23
+ if (valueMayContainReasoningMarkup(entry)) {
24
+ return true;
25
+ }
26
+ }
27
+ return false;
28
+ }
29
+ if (!value || typeof value !== 'object') {
30
+ return false;
31
+ }
32
+ for (const entry of Object.values(value)) {
33
+ if (valueMayContainReasoningMarkup(entry)) {
34
+ return true;
35
+ }
36
+ }
37
+ return false;
38
+ }
39
+ function responsesPayloadMayNeedReasoningNormalization(payload, options) {
40
+ if (!payload || typeof payload !== 'object') {
41
+ return false;
42
+ }
43
+ const includeOutput = options.includeOutput ?? true;
44
+ const includeInput = options.includeInput ?? false;
45
+ const includeRequiredAction = options.includeRequiredAction ?? false;
46
+ const includeInstructions = options.includeInstructions ?? false;
47
+ if (includeOutput && valueMayContainReasoningMarkup(payload.output)) {
48
+ return true;
49
+ }
50
+ if (includeInput && valueMayContainReasoningMarkup(payload.input)) {
51
+ return true;
52
+ }
53
+ if (includeInstructions && valueMayContainReasoningMarkup(payload.instructions)) {
54
+ return true;
55
+ }
56
+ if (includeRequiredAction && valueMayContainReasoningMarkup(payload.required_action)) {
57
+ return true;
58
+ }
59
+ return false;
60
+ }
3
61
  function assertReasoningNormalizerNativeAvailable() {
4
62
  if (typeof normalizeReasoningInChatPayloadWithNative !== 'function' ||
5
63
  typeof normalizeReasoningInResponsesPayloadWithNative !== 'function' ||
@@ -22,6 +80,9 @@ export function normalizeReasoningInResponsesPayload(payload, options = { includ
22
80
  assertReasoningNormalizerNativeAvailable();
23
81
  if (!payload)
24
82
  return;
83
+ if (!responsesPayloadMayNeedReasoningNormalization(payload, options)) {
84
+ return;
85
+ }
25
86
  const normalized = normalizeReasoningInResponsesPayloadWithNative(payload, options);
26
87
  if (normalized && typeof normalized === 'object') {
27
88
  Object.assign(payload, normalized);