@jsonstudio/llms 0.6.3685 → 0.6.3686

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 (63) hide show
  1. package/dist/conversion/compat/actions/antigravity-thought-signature-cache.js +2 -22
  2. package/dist/conversion/compat/actions/deepseek-web-response.js +7 -0
  3. package/dist/conversion/compat/actions/field-mapping.js +153 -2
  4. package/dist/conversion/compat/actions/lmstudio-responses-input-stringify.js +104 -3
  5. package/dist/conversion/hub/node-support.js +1 -1
  6. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +1 -9
  7. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +28 -35
  8. package/dist/conversion/hub/pipeline/hub-pipeline.js +121 -197
  9. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.d.ts +4 -4
  10. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.js +37 -20
  11. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts +7 -6
  12. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +41 -69
  13. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/context-capture-orchestration.d.ts +0 -3
  14. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/context-capture-orchestration.js +1 -2
  15. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/context-factories.js +0 -2
  16. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +0 -1
  17. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/responses-context-snapshot.d.ts +2 -3
  18. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/responses-context-snapshot.js +5 -18
  19. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/context-merge.d.ts +2 -1
  20. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/context-merge.js +16 -0
  21. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.d.ts +1 -1
  22. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +52 -27
  23. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.d.ts +0 -1
  24. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +1 -1
  25. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.d.ts +1 -1
  26. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.js +5 -9
  27. package/dist/conversion/hub/process/chat-process-continue-execution.js +3 -0
  28. package/dist/conversion/hub/process/chat-process-media.d.ts +2 -1
  29. package/dist/conversion/hub/process/chat-process-media.js +63 -9
  30. package/dist/conversion/hub/process/chat-process-session-usage.d.ts +6 -24
  31. package/dist/conversion/hub/process/chat-process-session-usage.js +101 -200
  32. package/dist/conversion/hub/response/provider-response.js +13 -13
  33. package/dist/conversion/hub/types/chat-envelope.d.ts +0 -1
  34. package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.js +4 -0
  35. package/dist/conversion/responses/responses-openai-bridge.d.ts +0 -1
  36. package/dist/conversion/responses/responses-openai-bridge.js +34 -28
  37. package/dist/conversion/shared/anthropic-message-utils.js +1 -14
  38. package/dist/conversion/shared/reasoning-normalizer.js +22 -41
  39. package/dist/conversion/shared/responses-tool-utils.js +2 -3
  40. package/dist/conversion/shared/tool-governor.js +4 -2
  41. package/dist/native/router_hotpath_napi.node +0 -0
  42. package/dist/router/virtual-router/engine/routing-state/store.js +2 -21
  43. package/dist/router/virtual-router/engine-selection/native-chat-process-governed-filter-semantics.d.ts +0 -1
  44. package/dist/router/virtual-router/engine-selection/native-chat-process-governed-filter-semantics.js +0 -1
  45. package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.d.ts +0 -3
  46. package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.js +0 -72
  47. package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.d.ts +1 -1
  48. package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.js +1 -1
  49. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-edge-stage-semantics.d.ts +2 -2
  50. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-edge-stage-semantics.js +96 -80
  51. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-inbound-outbound-semantics.d.ts +1 -0
  52. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-inbound-outbound-semantics.js +29 -0
  53. package/dist/router/virtual-router/engine-selection/native-router-hotpath-loader.js +2 -6
  54. package/dist/router/virtual-router/engine.js +6 -9
  55. package/dist/router/virtual-router/routing-instructions/state.js +27 -37
  56. package/dist/router/virtual-router/routing-instructions/types.d.ts +4 -6
  57. package/dist/router/virtual-router/token-estimator.js +0 -21
  58. package/dist/servertool/handlers/stop-message-auto.js +1 -11
  59. package/dist/tools/apply-patch/execution-capturer.d.ts +1 -1
  60. package/dist/tools/apply-patch/execution-capturer.js +2 -1
  61. package/dist/tools/apply-patch/regression-capturer.js +1 -2
  62. package/dist/tools/tool-registry.js +2 -1
  63. package/package.json +1 -1
@@ -1,9 +1,9 @@
1
- import { analyzeChatProcessMedia, stripChatProcessHistoricalImages } from '../../../router/virtual-router/engine-selection/native-router-hotpath.js';
1
+ import { analyzeChatProcessMedia, stripChatProcessHistoricalImages, } from "../../../router/virtual-router/engine-selection/native-router-hotpath.js";
2
2
  export function stripHistoricalImageAttachments(messages) {
3
3
  if (!Array.isArray(messages) || !messages.length) {
4
4
  return messages;
5
5
  }
6
- const placeholderText = '[Image omitted]';
6
+ const placeholderText = "[Image omitted]";
7
7
  const stripped = stripChatProcessHistoricalImages(messages, placeholderText);
8
8
  if (stripped.changed !== true || !Array.isArray(stripped.messages)) {
9
9
  return messages;
@@ -12,17 +12,17 @@ export function stripHistoricalImageAttachments(messages) {
12
12
  }
13
13
  const INLINE_MEDIA_DATA_RE = /data:(image|video)\/[a-z0-9.+-]+;base64,[a-z0-9+/=\s]+/i;
14
14
  function isVisualToolMessage(message) {
15
- if (!message || typeof message !== 'object') {
15
+ if (!message || typeof message !== "object") {
16
16
  return false;
17
17
  }
18
- if (message.role !== 'tool') {
18
+ if (message.role !== "tool") {
19
19
  return false;
20
20
  }
21
- const name = typeof message.name === 'string' ? message.name.trim().toLowerCase() : '';
22
- if (name === 'view_image') {
21
+ const name = typeof message.name === "string" ? message.name.trim().toLowerCase() : "";
22
+ if (name === "view_image") {
23
23
  return true;
24
24
  }
25
- const content = typeof message.content === 'string' ? message.content : '';
25
+ const content = typeof message.content === "string" ? message.content : "";
26
26
  return INLINE_MEDIA_DATA_RE.test(content);
27
27
  }
28
28
  export function stripHistoricalVisualToolOutputs(messages) {
@@ -34,14 +34,14 @@ export function stripHistoricalVisualToolOutputs(messages) {
34
34
  if (!isVisualToolMessage(message)) {
35
35
  return message;
36
36
  }
37
- const content = typeof message.content === 'string' ? message.content : '';
37
+ const content = typeof message.content === "string" ? message.content : "";
38
38
  if (!INLINE_MEDIA_DATA_RE.test(content)) {
39
39
  return message;
40
40
  }
41
41
  changed = true;
42
42
  return {
43
43
  ...message,
44
- content: '[Image omitted]'
44
+ content: "[Image omitted]",
45
45
  };
46
46
  });
47
47
  return changed ? next : messages;
@@ -52,3 +52,57 @@ export function containsImageAttachment(messages) {
52
52
  }
53
53
  return analyzeChatProcessMedia(messages).containsCurrentTurnImage === true;
54
54
  }
55
+ export function repairIncompleteToolCalls(messages) {
56
+ if (!Array.isArray(messages) || messages.length === 0) {
57
+ return messages;
58
+ }
59
+ const toolCallIdsWithResponse = new Set();
60
+ for (let i = messages.length - 1; i >= 0; i--) {
61
+ const msg = messages[i];
62
+ if (msg?.role === "tool" && typeof msg.tool_call_id === "string") {
63
+ toolCallIdsWithResponse.add(msg.tool_call_id);
64
+ }
65
+ }
66
+ const result = [];
67
+ let changed = false;
68
+ for (const msg of messages) {
69
+ if (msg?.role === "assistant" && Array.isArray(msg.tool_calls)) {
70
+ const completeToolCalls = [];
71
+ const missingToolCallIds = [];
72
+ for (const tc of msg.tool_calls) {
73
+ const tcId = typeof tc?.id === "string" ? tc.id : "";
74
+ if (tcId && toolCallIdsWithResponse.has(tcId)) {
75
+ completeToolCalls.push(tc);
76
+ }
77
+ else if (tcId) {
78
+ missingToolCallIds.push(tcId);
79
+ }
80
+ }
81
+ if (missingToolCallIds.length > 0) {
82
+ changed = true;
83
+ const repaired = {
84
+ ...msg,
85
+ tool_calls: completeToolCalls.length > 0 ? completeToolCalls : undefined,
86
+ };
87
+ if (!repaired.tool_calls) {
88
+ delete repaired.tool_calls;
89
+ }
90
+ result.push(repaired);
91
+ for (const missingId of missingToolCallIds) {
92
+ result.push({
93
+ role: "tool",
94
+ tool_call_id: missingId,
95
+ content: '{"status":"tool_call_repaired_orphaned_tool_call"}',
96
+ });
97
+ }
98
+ }
99
+ else {
100
+ result.push(msg);
101
+ }
102
+ }
103
+ else {
104
+ result.push(msg);
105
+ }
106
+ }
107
+ return changed ? result : messages;
108
+ }
@@ -1,25 +1,7 @@
1
+ import type { AdapterContext } from '../types/chat-envelope.js';
1
2
  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 {};
3
+ export declare function estimateSessionBoundTokens(request: StandardizedRequest | ProcessedRequest, metadata: Record<string, unknown> | undefined): number | undefined;
4
+ export declare function saveChatProcessSessionActualUsage(options: {
5
+ context: AdapterContext;
6
+ usage: Record<string, unknown> | undefined;
7
+ }): void;
@@ -1,246 +1,147 @@
1
1
  import { loadRoutingInstructionStateSync, saveRoutingInstructionStateSync } from '../../../router/virtual-router/sticky-session-store.js';
2
2
  import { countRequestTokens } from '../../../router/virtual-router/token-counter.js';
3
- function buildEmptyRoutingInstructionState() {
3
+ function createEmptyRoutingInstructionState() {
4
4
  return {
5
- forcedTarget: undefined,
6
- stickyTarget: undefined,
7
- preferTarget: undefined,
8
5
  allowedProviders: new Set(),
9
6
  disabledProviders: new Set(),
10
7
  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
8
+ disabledModels: new Map()
31
9
  };
32
10
  }
33
- function readScope(metadata) {
34
- const sessionId = typeof metadata.sessionId === 'string' ? metadata.sessionId.trim() : '';
11
+ function resolveSessionUsageScope(record) {
12
+ const sessionId = typeof record?.sessionId === 'string' ? record.sessionId.trim() : '';
35
13
  if (sessionId) {
36
14
  return `session:${sessionId}`;
37
15
  }
38
- const conversationId = typeof metadata.conversationId === 'string' ? metadata.conversationId.trim() : '';
16
+ const conversationId = typeof record?.conversationId === 'string' ? record.conversationId.trim() : '';
39
17
  if (conversationId) {
40
18
  return `conversation:${conversationId}`;
41
19
  }
42
20
  return undefined;
43
21
  }
44
- function stableSignature(value) {
22
+ function loadState(scope) {
45
23
  try {
46
- return JSON.stringify(value ?? null) || 'null';
24
+ return loadRoutingInstructionStateSync(scope);
47
25
  }
48
26
  catch {
49
- return 'unserializable';
27
+ return null;
50
28
  }
51
29
  }
52
- function messageBoundarySignature(message) {
53
- if (!message) {
30
+ function readRoundedToken(value) {
31
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
54
32
  return undefined;
55
33
  }
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;
34
+ const rounded = Math.round(value);
35
+ return rounded > 0 ? rounded : undefined;
68
36
  }
69
- function digestSignature(value) {
70
- if (!value) {
71
- return undefined;
37
+ function buildSnapshot(scope, state) {
38
+ if (!state) {
39
+ return null;
72
40
  }
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);
41
+ const totalTokens = readRoundedToken(state.chatProcessLastTotalTokens);
42
+ const inputTokens = readRoundedToken(state.chatProcessLastInputTokens);
43
+ const messageCount = readRoundedToken(state.chatProcessLastMessageCount);
44
+ const updatedAtMs = readRoundedToken(state.chatProcessLastUpdatedAt);
45
+ if (totalTokens === undefined && inputTokens === undefined) {
46
+ return null;
77
47
  }
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;
48
+ return {
49
+ scope,
50
+ ...(totalTokens !== undefined ? { totalTokens } : {}),
51
+ ...(inputTokens !== undefined ? { inputTokens } : {}),
52
+ ...(messageCount !== undefined ? { messageCount } : {}),
53
+ ...(updatedAtMs !== undefined ? { updatedAtMs } : {})
54
+ };
109
55
  }
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
- };
56
+ function normalizeUsage(usage) {
57
+ if (!usage || typeof usage !== 'object') {
58
+ return null;
59
+ }
60
+ const inputTokens = readRoundedToken(usage.input_tokens ??
61
+ usage.prompt_tokens ??
62
+ usage.inputTokens ??
63
+ usage.promptTokens ??
64
+ usage.request_tokens ??
65
+ usage.requestTokens);
66
+ const outputTokens = readRoundedToken(usage.output_tokens ??
67
+ usage.completion_tokens ??
68
+ usage.outputTokens ??
69
+ usage.completionTokens ??
70
+ usage.response_tokens ??
71
+ usage.responseTokens);
72
+ const totalTokens = readRoundedToken(usage.total_tokens ??
73
+ usage.totalTokens ??
74
+ ((inputTokens ?? 0) + (outputTokens ?? 0) > 0
75
+ ? (inputTokens ?? 0) + (outputTokens ?? 0)
76
+ : undefined));
77
+ if (totalTokens === undefined && inputTokens === undefined) {
78
+ return null;
167
79
  }
80
+ return {
81
+ ...(inputTokens !== undefined ? { inputTokens } : {}),
82
+ ...(outputTokens !== undefined ? { outputTokens } : {}),
83
+ ...(totalTokens !== undefined ? { totalTokens } : {})
84
+ };
85
+ }
86
+ function estimateDeltaTokens(request, previousMessageCount) {
168
87
  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
- }
88
+ if (previousMessageCount < 0 || previousMessageCount > messages.length) {
89
+ return undefined;
187
90
  }
188
91
  const appendedMessages = messages.slice(previousMessageCount);
189
92
  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({
93
+ return 0;
94
+ }
95
+ return countRequestTokens({
199
96
  model: request.model,
200
97
  messages: appendedMessages,
201
- tools: undefined,
202
98
  parameters: {},
203
- metadata: request.metadata ?? { originalEndpoint: '' }
99
+ metadata: { originalEndpoint: request.metadata?.originalEndpoint ?? '/v1/chat/completions' }
204
100
  });
205
- return {
206
- tokens: previousTokens + delta,
207
- scope,
208
- mode: 'session_delta',
209
- previousMessageCount,
210
- appendedMessageCount: appendedMessages.length
211
- };
212
101
  }
213
- export function saveChatProcessSessionInputEstimate(metadata, request, estimatedInputTokens) {
214
- const scope = readScope(metadata);
215
- const normalized = coercePositiveInteger(estimatedInputTokens);
216
- if (!scope || normalized === undefined) {
217
- return;
102
+ export function estimateSessionBoundTokens(request, metadata) {
103
+ const scope = resolveSessionUsageScope(metadata);
104
+ if (!scope) {
105
+ return undefined;
218
106
  }
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);
107
+ const snapshot = buildSnapshot(scope, loadState(scope));
108
+ if (!snapshot) {
109
+ return undefined;
110
+ }
111
+ const previousTotal = snapshot.totalTokens ?? snapshot.inputTokens;
112
+ const previousMessageCount = snapshot.messageCount;
113
+ if (previousTotal === undefined || previousMessageCount === undefined) {
114
+ return undefined;
115
+ }
116
+ const deltaTokens = estimateDeltaTokens(request, previousMessageCount);
117
+ if (deltaTokens === undefined) {
118
+ return undefined;
119
+ }
120
+ return Math.max(1, Math.round(previousTotal + deltaTokens));
228
121
  }
229
- export function saveChatProcessSessionActualUsage(metadata, usageRaw, requestSnapshot) {
230
- const scope = readScope(metadata);
231
- if (!scope || !usageRaw || typeof usageRaw !== 'object' || Array.isArray(usageRaw)) {
122
+ export function saveChatProcessSessionActualUsage(options) {
123
+ const scope = resolveSessionUsageScope(options.context);
124
+ if (!scope) {
232
125
  return;
233
126
  }
234
- const usage = usageRaw;
235
- const actualInputTokens = coercePositiveInteger(usage.prompt_tokens ?? usage.input_tokens ?? usage.total_input_tokens);
236
- if (actualInputTokens === undefined) {
127
+ const normalizedUsage = normalizeUsage(options.usage);
128
+ if (!normalizedUsage) {
237
129
  return;
238
130
  }
239
- const state = loadRoutingInstructionStateSync(scope) ?? buildEmptyRoutingInstructionState();
240
- if (!hasReusableSessionUsageShape(state) && !applyRequestSnapshotToState(state, requestSnapshot)) {
241
- return;
131
+ const capturedChatRequest = options.context.capturedChatRequest;
132
+ const messageCount = Array.isArray(capturedChatRequest?.messages)
133
+ ? (capturedChatRequest.messages ?? []).length
134
+ : undefined;
135
+ const state = loadState(scope) ?? createEmptyRoutingInstructionState();
136
+ if (normalizedUsage.totalTokens !== undefined) {
137
+ state.chatProcessLastTotalTokens = normalizedUsage.totalTokens;
138
+ }
139
+ if (normalizedUsage.inputTokens !== undefined) {
140
+ state.chatProcessLastInputTokens = normalizedUsage.inputTokens;
141
+ }
142
+ if (typeof messageCount === 'number' && Number.isFinite(messageCount)) {
143
+ state.chatProcessLastMessageCount = Math.max(0, Math.round(messageCount));
242
144
  }
243
- state.chatProcessInputTokens = actualInputTokens;
244
- state.chatProcessUpdatedAt = Date.now();
145
+ state.chatProcessLastUpdatedAt = Date.now();
245
146
  saveRoutingInstructionStateSync(scope, state);
246
147
  }
@@ -15,11 +15,11 @@ 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';
19
18
  import { ProviderProtocolError } from '../../provider-protocol-error.js';
20
19
  import { readRuntimeMetadata } from '../../runtime-metadata.js';
21
20
  import { commitClockReservation, resolveClockConfig } from '../../../servertool/clock/task-store.js';
22
21
  import { detectProviderResponseShapeWithNative } from '../../../router/virtual-router/engine-selection/native-chat-process-servertool-orchestration-semantics.js';
22
+ import { saveChatProcessSessionActualUsage } from '../process/chat-process-session-usage.js';
23
23
  const PROVIDER_RESPONSE_REGISTRY = {
24
24
  'openai-chat': {
25
25
  createFormatAdapter: () => new ChatFormatAdapter(),
@@ -398,18 +398,6 @@ export async function convertProviderResponse(options) {
398
398
  });
399
399
  applyModelOverride(clientPayload, displayModel);
400
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
- }
413
401
  if (clientProtocol === 'openai-responses') {
414
402
  try {
415
403
  recordResponsesResponse({
@@ -464,6 +452,18 @@ export async function convertProviderResponse(options) {
464
452
  });
465
453
  // Commit scheduled-task delivery only after a successful client payload/stream is prepared.
466
454
  await maybeCommitClockReservationFromContext(options.context);
455
+ try {
456
+ const usage = clientPayload && typeof clientPayload === 'object' && !Array.isArray(clientPayload)
457
+ ? clientPayload.usage
458
+ : undefined;
459
+ saveChatProcessSessionActualUsage({
460
+ context: options.context,
461
+ usage
462
+ });
463
+ }
464
+ catch {
465
+ // best-effort: usage persistence must not break response delivery
466
+ }
467
467
  if (outbound.stream) {
468
468
  const usage = clientPayload && typeof clientPayload === 'object' && !Array.isArray(clientPayload)
469
469
  ? clientPayload.usage
@@ -53,7 +53,6 @@ export interface AdapterContext {
53
53
  originalModelId?: string;
54
54
  clientModelId?: string;
55
55
  toolCallIdStyle?: 'fc' | 'preserve';
56
- applyPatchToolMode?: 'schema' | 'freeform';
57
56
  responsesResume?: JsonObject;
58
57
  [key: string]: JsonValue;
59
58
  }
@@ -142,6 +142,10 @@ 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
+ }
145
149
  const filterContext = {
146
150
  ...context,
147
151
  requestId,
@@ -10,7 +10,6 @@ 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';
14
13
  }): BuildResponsesRequestResult;
15
14
  export { buildResponsesPayloadFromChat, extractRequestIdFromResponse } from './responses-openai-bridge/response-payload.js';
16
15
  export { buildChatResponseFromResponses } from '../shared/responses-response-utils.js';