@jsonstudio/llms 0.6.954 → 0.6.1172
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/conversion/hub/operation-table/operation-table-runner.d.ts +18 -0
- package/dist/conversion/hub/operation-table/operation-table-runner.js +158 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.d.ts +8 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +303 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.d.ts +8 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +413 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.d.ts +7 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +841 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.d.ts +21 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +535 -0
- package/dist/conversion/hub/ops/operations.d.ts +19 -0
- package/dist/conversion/hub/ops/operations.js +126 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +9 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +489 -19
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +6 -0
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +11 -0
- package/dist/conversion/hub/policy/policy-engine.js +41 -9
- package/dist/conversion/hub/policy/protocol-spec.d.ts +25 -0
- package/dist/conversion/hub/policy/protocol-spec.js +73 -23
- package/dist/conversion/hub/process/chat-process.js +252 -41
- package/dist/conversion/hub/response/provider-response.js +175 -2
- package/dist/conversion/hub/response/response-runtime.js +1 -1
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.d.ts +1 -8
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +1 -365
- package/dist/conversion/hub/semantic-mappers/chat-mapper.d.ts +1 -8
- package/dist/conversion/hub/semantic-mappers/chat-mapper.js +1 -467
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +1 -7
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +1 -903
- package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +1 -21
- package/dist/conversion/hub/semantic-mappers/responses-mapper.js +1 -593
- package/dist/conversion/hub/tool-surface/tool-surface-engine.d.ts +18 -0
- package/dist/conversion/hub/tool-surface/tool-surface-engine.js +571 -0
- package/dist/conversion/responses/responses-openai-bridge.js +14 -2
- package/dist/conversion/shared/bridge-message-utils.js +2 -8
- package/dist/conversion/shared/bridge-policies.js +5 -105
- package/dist/conversion/shared/gemini-tool-utils.js +89 -15
- package/dist/conversion/shared/protocol-field-allowlists.d.ts +7 -0
- package/dist/conversion/shared/protocol-field-allowlists.js +145 -0
- package/dist/conversion/shared/reasoning-tool-normalizer.js +4 -2
- package/dist/conversion/shared/snapshot-hooks.js +166 -3
- package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -0
- package/dist/conversion/shared/text-markup-normalizer.js +345 -9
- package/dist/conversion/shared/thought-signature-validator.d.ts +52 -0
- package/dist/conversion/shared/thought-signature-validator.js +170 -0
- package/dist/conversion/shared/tool-argument-repairer.d.ts +39 -0
- package/dist/conversion/shared/tool-argument-repairer.js +56 -0
- package/dist/conversion/shared/tool-call-id-manager.d.ts +113 -0
- package/dist/conversion/shared/tool-call-id-manager.js +231 -0
- package/dist/conversion/shared/tool-canonicalizer.js +2 -11
- package/dist/router/virtual-router/bootstrap.js +70 -5
- package/dist/router/virtual-router/context-advisor.d.ts +4 -0
- package/dist/router/virtual-router/context-advisor.js +3 -0
- package/dist/router/virtual-router/context-weighted.d.ts +31 -0
- package/dist/router/virtual-router/context-weighted.js +54 -0
- package/dist/router/virtual-router/engine-selection.js +284 -47
- package/dist/router/virtual-router/engine.d.ts +3 -0
- package/dist/router/virtual-router/engine.js +142 -33
- package/dist/router/virtual-router/health-weighted.d.ts +25 -0
- package/dist/router/virtual-router/health-weighted.js +63 -0
- package/dist/router/virtual-router/load-balancer.d.ts +2 -0
- package/dist/router/virtual-router/load-balancer.js +45 -16
- package/dist/router/virtual-router/routing-instructions.js +17 -1
- package/dist/router/virtual-router/sticky-session-store.js +136 -24
- package/dist/router/virtual-router/stop-message-file-resolver.d.ts +1 -0
- package/dist/router/virtual-router/stop-message-file-resolver.js +74 -0
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +15 -0
- package/dist/router/virtual-router/stop-message-state-sync.js +57 -0
- package/dist/router/virtual-router/types.d.ts +98 -0
- package/dist/servertool/clock/config.d.ts +7 -0
- package/dist/servertool/clock/config.js +27 -0
- package/dist/servertool/clock/daemon.d.ts +3 -0
- package/dist/servertool/clock/daemon.js +79 -0
- package/dist/servertool/clock/io.d.ts +2 -0
- package/dist/servertool/clock/io.js +13 -0
- package/dist/servertool/clock/paths.d.ts +4 -0
- package/dist/servertool/clock/paths.js +25 -0
- package/dist/servertool/clock/session-store.d.ts +3 -0
- package/dist/servertool/clock/session-store.js +56 -0
- package/dist/servertool/clock/state.d.ts +5 -0
- package/dist/servertool/clock/state.js +62 -0
- package/dist/servertool/clock/task-store.d.ts +5 -0
- package/dist/servertool/clock/task-store.js +4 -0
- package/dist/servertool/clock/tasks.d.ts +17 -0
- package/dist/servertool/clock/tasks.js +221 -0
- package/dist/servertool/clock/types.d.ts +36 -0
- package/dist/servertool/clock/types.js +1 -0
- package/dist/servertool/engine.d.ts +2 -0
- package/dist/servertool/engine.js +161 -7
- package/dist/servertool/followup-shadow.d.ts +16 -0
- package/dist/servertool/followup-shadow.js +145 -0
- package/dist/servertool/handlers/apply-patch-guard.js +1 -265
- package/dist/servertool/handlers/clock-auto.d.ts +1 -0
- package/dist/servertool/handlers/clock-auto.js +160 -0
- package/dist/servertool/handlers/clock.d.ts +1 -0
- package/dist/servertool/handlers/clock.js +197 -0
- package/dist/servertool/handlers/exec-command-guard.js +7 -555
- package/dist/servertool/handlers/followup-request-builder.d.ts +15 -7
- package/dist/servertool/handlers/followup-request-builder.js +248 -28
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +62 -169
- package/dist/servertool/handlers/iflow-model-error-retry.js +18 -28
- package/dist/servertool/handlers/recursive-detection-guard.d.ts +1 -0
- package/dist/servertool/handlers/recursive-detection-guard.js +333 -0
- package/dist/servertool/handlers/stop-message-auto.js +47 -175
- package/dist/servertool/handlers/vision.d.ts +7 -1
- package/dist/servertool/handlers/vision.js +61 -117
- package/dist/servertool/handlers/web-search.d.ts +7 -1
- package/dist/servertool/handlers/web-search.js +122 -105
- package/dist/servertool/reenter-backend.d.ts +23 -0
- package/dist/servertool/reenter-backend.js +18 -0
- package/dist/servertool/server-side-tools.d.ts +3 -2
- package/dist/servertool/server-side-tools.js +64 -10
- package/dist/servertool/types.d.ts +92 -3
- package/dist/sse/json-to-sse/event-generators/responses.js +3 -21
- package/dist/sse/shared/serializers/responses-event-serializer.d.ts +8 -0
- package/dist/sse/shared/serializers/responses-event-serializer.js +19 -0
- package/dist/sse/shared/writer.js +24 -7
- package/dist/tools/apply-patch/execution-capturer.js +3 -1
- package/dist/tools/apply-patch/json/parse-loose.d.ts +3 -0
- package/dist/tools/apply-patch/json/parse-loose.js +139 -0
- package/dist/tools/apply-patch/patch-text/context-diff.d.ts +1 -0
- package/dist/tools/apply-patch/patch-text/context-diff.js +173 -0
- package/dist/tools/apply-patch/patch-text/git-diff.d.ts +1 -0
- package/dist/tools/apply-patch/patch-text/git-diff.js +138 -0
- package/dist/tools/apply-patch/patch-text/looks-like-patch.d.ts +1 -0
- package/dist/tools/apply-patch/patch-text/looks-like-patch.js +13 -0
- package/dist/tools/apply-patch/patch-text/normalize.d.ts +3 -0
- package/dist/tools/apply-patch/patch-text/normalize.js +262 -0
- package/dist/tools/apply-patch/structured/coercion.d.ts +3 -0
- package/dist/tools/apply-patch/structured/coercion.js +82 -0
- package/dist/tools/apply-patch/validation/shared.d.ts +3 -0
- package/dist/tools/apply-patch/validation/shared.js +6 -0
- package/dist/tools/apply-patch/validator.d.ts +2 -2
- package/dist/tools/apply-patch/validator.js +6 -556
- package/package.json +1 -1
|
@@ -1,105 +1,5 @@
|
|
|
1
1
|
import { RESPONSES_INSTRUCTIONS_REASONING_FIELD } from './reasoning-normalizer.js';
|
|
2
|
-
|
|
3
|
-
'messages',
|
|
4
|
-
'tools',
|
|
5
|
-
'tool_outputs',
|
|
6
|
-
'model',
|
|
7
|
-
'temperature',
|
|
8
|
-
'top_p',
|
|
9
|
-
'top_k',
|
|
10
|
-
'max_tokens',
|
|
11
|
-
'frequency_penalty',
|
|
12
|
-
'presence_penalty',
|
|
13
|
-
'logit_bias',
|
|
14
|
-
'response_format',
|
|
15
|
-
'parallel_tool_calls',
|
|
16
|
-
'tool_choice',
|
|
17
|
-
'seed',
|
|
18
|
-
'user',
|
|
19
|
-
'metadata',
|
|
20
|
-
'stop',
|
|
21
|
-
'stop_sequences',
|
|
22
|
-
'stream'
|
|
23
|
-
];
|
|
24
|
-
const ANTHROPIC_ALLOWED_FIELDS = [
|
|
25
|
-
'model',
|
|
26
|
-
'messages',
|
|
27
|
-
'tools',
|
|
28
|
-
'system',
|
|
29
|
-
'stop_sequences',
|
|
30
|
-
'temperature',
|
|
31
|
-
'top_p',
|
|
32
|
-
'top_k',
|
|
33
|
-
'max_tokens',
|
|
34
|
-
'max_output_tokens',
|
|
35
|
-
'metadata',
|
|
36
|
-
'stream',
|
|
37
|
-
'tool_choice'
|
|
38
|
-
];
|
|
39
|
-
const RESPONSES_ALLOWED_FIELDS = [
|
|
40
|
-
'id',
|
|
41
|
-
'object',
|
|
42
|
-
'created_at',
|
|
43
|
-
'model',
|
|
44
|
-
'status',
|
|
45
|
-
'input',
|
|
46
|
-
'instructions',
|
|
47
|
-
'output',
|
|
48
|
-
'output_text',
|
|
49
|
-
'required_action',
|
|
50
|
-
'response_id',
|
|
51
|
-
'previous_response_id',
|
|
52
|
-
'tool_outputs',
|
|
53
|
-
'tools',
|
|
54
|
-
'metadata',
|
|
55
|
-
'include',
|
|
56
|
-
'store',
|
|
57
|
-
'user',
|
|
58
|
-
'response_format',
|
|
59
|
-
'temperature',
|
|
60
|
-
'top_p',
|
|
61
|
-
'top_k',
|
|
62
|
-
'max_tokens',
|
|
63
|
-
'max_output_tokens',
|
|
64
|
-
'logit_bias',
|
|
65
|
-
'seed',
|
|
66
|
-
'parallel_tool_calls',
|
|
67
|
-
'tool_choice',
|
|
68
|
-
'stream',
|
|
69
|
-
'instructions_is_raw'
|
|
70
|
-
];
|
|
71
|
-
const GEMINI_ALLOWED_FIELDS = [
|
|
72
|
-
'model',
|
|
73
|
-
'contents',
|
|
74
|
-
'systemInstruction',
|
|
75
|
-
'system_instruction',
|
|
76
|
-
'generationConfig',
|
|
77
|
-
'generation_config',
|
|
78
|
-
'safetySettings',
|
|
79
|
-
'safety_settings',
|
|
80
|
-
'metadata',
|
|
81
|
-
'toolConfig',
|
|
82
|
-
'tool_config',
|
|
83
|
-
'tools',
|
|
84
|
-
'tool_choice',
|
|
85
|
-
'parallelToolCalls',
|
|
86
|
-
'parallel_tool_calls',
|
|
87
|
-
'responseMimeType',
|
|
88
|
-
'response_mime_type',
|
|
89
|
-
'stopSequences',
|
|
90
|
-
'stop_sequences',
|
|
91
|
-
'cachedContent',
|
|
92
|
-
'prompt',
|
|
93
|
-
'response',
|
|
94
|
-
'candidates',
|
|
95
|
-
'usageMetadata',
|
|
96
|
-
'responseMetadata',
|
|
97
|
-
'promptFeedback',
|
|
98
|
-
'modelVersion',
|
|
99
|
-
'client',
|
|
100
|
-
'user',
|
|
101
|
-
'stream'
|
|
102
|
-
];
|
|
2
|
+
import { ANTHROPIC_ALLOWED_FIELDS, GEMINI_ALLOWED_FIELDS, OPENAI_CHAT_ALLOWED_FIELDS, OPENAI_RESPONSES_ALLOWED_FIELDS } from './protocol-field-allowlists.js';
|
|
103
3
|
function reasoningAction(idPrefix) {
|
|
104
4
|
return {
|
|
105
5
|
name: 'reasoning.extract',
|
|
@@ -134,7 +34,7 @@ const RESPONSES_POLICY = {
|
|
|
134
34
|
reasoningAction('responses_reasoning'),
|
|
135
35
|
toolCallNormalizationAction('responses_tool_call'),
|
|
136
36
|
{ name: 'tools.ensure-placeholders' },
|
|
137
|
-
{ name: 'metadata.extra-fields', options: { allowedKeys:
|
|
37
|
+
{ name: 'metadata.extra-fields', options: { allowedKeys: OPENAI_RESPONSES_ALLOWED_FIELDS } },
|
|
138
38
|
{ name: 'metadata.provider-field', options: { field: 'metadata', target: 'providerMetadata' } },
|
|
139
39
|
{ name: 'metadata.provider-sentinel', options: { sentinel: '__rcc_provider_metadata', target: 'providerMetadata' } }
|
|
140
40
|
],
|
|
@@ -146,7 +46,7 @@ const RESPONSES_POLICY = {
|
|
|
146
46
|
{ name: 'messages.ensure-output-fields', options: { toolFallback: 'Tool call completed (no output).' } },
|
|
147
47
|
{ name: 'messages.ensure-system-instruction' },
|
|
148
48
|
reasoningAction('responses_reasoning'),
|
|
149
|
-
{ name: 'metadata.extra-fields', options: { allowedKeys:
|
|
49
|
+
{ name: 'metadata.extra-fields', options: { allowedKeys: OPENAI_RESPONSES_ALLOWED_FIELDS } },
|
|
150
50
|
{ name: 'metadata.provider-field', options: { field: 'metadata', target: 'providerMetadata' } },
|
|
151
51
|
{ name: 'metadata.provider-sentinel', options: { sentinel: '__rcc_provider_metadata', target: 'providerMetadata' } }
|
|
152
52
|
]
|
|
@@ -156,12 +56,12 @@ const RESPONSES_POLICY = {
|
|
|
156
56
|
{ name: 'reasoning.attach-output' },
|
|
157
57
|
reasoningAction('responses_reasoning'),
|
|
158
58
|
toolCallNormalizationAction('responses_tool_call'),
|
|
159
|
-
{ name: 'metadata.extra-fields', options: { allowedKeys:
|
|
59
|
+
{ name: 'metadata.extra-fields', options: { allowedKeys: OPENAI_RESPONSES_ALLOWED_FIELDS } }
|
|
160
60
|
],
|
|
161
61
|
outbound: [
|
|
162
62
|
reasoningAction('responses_reasoning'),
|
|
163
63
|
toolCallNormalizationAction('responses_tool_call'),
|
|
164
|
-
{ name: 'metadata.extra-fields', options: { allowedKeys:
|
|
64
|
+
{ name: 'metadata.extra-fields', options: { allowedKeys: OPENAI_RESPONSES_ALLOWED_FIELDS } }
|
|
165
65
|
]
|
|
166
66
|
}
|
|
167
67
|
};
|
|
@@ -175,40 +175,114 @@ export function buildGeminiToolsFromBridge(defs) {
|
|
|
175
175
|
const props = isPlainRecord(propsRaw) ? propsRaw : {};
|
|
176
176
|
const lowered = String(name || '').trim().toLowerCase();
|
|
177
177
|
if (lowered === 'exec_command') {
|
|
178
|
-
// Keep Gemini tool schema aligned with historical
|
|
179
|
-
//
|
|
180
|
-
//
|
|
178
|
+
// Keep Gemini tool schema aligned with both historical Codex and our runtime tool signature:
|
|
179
|
+
// - Codex CLI: { cmd, workdir }
|
|
180
|
+
// - Some history: { command, workdir }
|
|
181
|
+
// Strict Gemini validation (and/or tool selection) is sensitive to schema/arg drift.
|
|
182
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'cmd') && Object.prototype.hasOwnProperty.call(props, 'command')) {
|
|
183
|
+
props.cmd = props.command;
|
|
184
|
+
}
|
|
181
185
|
if (!Object.prototype.hasOwnProperty.call(props, 'command') && Object.prototype.hasOwnProperty.call(props, 'cmd')) {
|
|
182
186
|
props.command = props.cmd;
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
catch {
|
|
187
|
-
// ignore
|
|
188
|
-
}
|
|
187
|
+
}
|
|
188
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'cmd')) {
|
|
189
|
+
props.cmd = { type: 'string' };
|
|
189
190
|
}
|
|
190
191
|
if (!Object.prototype.hasOwnProperty.call(props, 'command')) {
|
|
191
192
|
props.command = { type: 'string' };
|
|
192
193
|
}
|
|
194
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'workdir')) {
|
|
195
|
+
props.workdir = { type: 'string' };
|
|
196
|
+
}
|
|
193
197
|
params.properties = props;
|
|
194
|
-
|
|
198
|
+
// Avoid hard required keys for Gemini: the model may emit either alias (cmd/command),
|
|
199
|
+
// and "required" mismatch surfaces as MALFORMED_FUNCTION_CALL (empty reply) upstream.
|
|
200
|
+
try {
|
|
201
|
+
delete params.required;
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
params.required = undefined;
|
|
205
|
+
}
|
|
195
206
|
return params;
|
|
196
207
|
}
|
|
197
208
|
if (lowered === 'write_stdin') {
|
|
198
|
-
|
|
209
|
+
// Keep both aliases to avoid strict validation failures when the model echoes history.
|
|
210
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'chars') && Object.prototype.hasOwnProperty.call(props, 'text')) {
|
|
211
|
+
props.chars = props.text;
|
|
212
|
+
}
|
|
213
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'text') && Object.prototype.hasOwnProperty.call(props, 'chars')) {
|
|
214
|
+
props.text = props.chars;
|
|
215
|
+
}
|
|
216
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'session_id')) {
|
|
217
|
+
props.session_id = { type: 'number' };
|
|
218
|
+
}
|
|
219
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'chars')) {
|
|
220
|
+
props.chars = { type: 'string' };
|
|
221
|
+
}
|
|
222
|
+
params.properties = props;
|
|
223
|
+
try {
|
|
224
|
+
delete params.required;
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
params.required = undefined;
|
|
228
|
+
}
|
|
199
229
|
return params;
|
|
200
230
|
}
|
|
201
231
|
if (lowered === 'apply_patch') {
|
|
202
|
-
//
|
|
232
|
+
// Gemini function calling needs JSON args, but Codex tool docs often describe apply_patch as "freeform".
|
|
233
|
+
// To prevent MALFORMED_FUNCTION_CALL, accept common aliases used by various bridge layers:
|
|
234
|
+
// - patch: canonical patch text
|
|
235
|
+
// - input/instructions/text: historical aliases containing patch text
|
|
203
236
|
if (!Object.prototype.hasOwnProperty.call(props, 'patch')) {
|
|
204
|
-
props.patch = {
|
|
237
|
+
props.patch = {
|
|
238
|
+
type: 'string',
|
|
239
|
+
description: 'Patch text (*** Begin Patch / *** End Patch or GNU unified diff).'
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'input')) {
|
|
243
|
+
props.input = {
|
|
244
|
+
type: 'string',
|
|
245
|
+
description: 'Alias of patch (patch text). Prefer patch.'
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'instructions')) {
|
|
249
|
+
props.instructions = {
|
|
250
|
+
type: 'string',
|
|
251
|
+
description: 'Alias of patch (patch text). Prefer patch.'
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'text')) {
|
|
255
|
+
props.text = {
|
|
256
|
+
type: 'string',
|
|
257
|
+
description: 'Alias of patch (patch text). Prefer patch.'
|
|
258
|
+
};
|
|
205
259
|
}
|
|
206
260
|
params.properties = props;
|
|
207
|
-
|
|
261
|
+
try {
|
|
262
|
+
delete params.required;
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
params.required = undefined;
|
|
266
|
+
}
|
|
208
267
|
return params;
|
|
209
268
|
}
|
|
210
269
|
return params;
|
|
211
270
|
};
|
|
271
|
+
const rewriteDescription = (name, description) => {
|
|
272
|
+
const lowered = String(name || '').trim().toLowerCase();
|
|
273
|
+
if (lowered === 'apply_patch') {
|
|
274
|
+
return ('Edit files by providing patch text in `patch` (string). ' +
|
|
275
|
+
'Supports "*** Begin Patch" / "*** End Patch" or GNU unified diff. ' +
|
|
276
|
+
'`input`/`instructions`/`text` are accepted as aliases.');
|
|
277
|
+
}
|
|
278
|
+
if (lowered === 'exec_command') {
|
|
279
|
+
return ('Run a shell command. Provide `cmd` (string) (alias: `command`) and optional `workdir` (string).');
|
|
280
|
+
}
|
|
281
|
+
if (lowered === 'write_stdin') {
|
|
282
|
+
return 'Write to an existing exec session. Provide `session_id` (number) and optional `chars` (string).';
|
|
283
|
+
}
|
|
284
|
+
return description;
|
|
285
|
+
};
|
|
212
286
|
defs.forEach((def) => {
|
|
213
287
|
if (!def || typeof def !== 'object') {
|
|
214
288
|
return;
|
|
@@ -232,7 +306,7 @@ export function buildGeminiToolsFromBridge(defs) {
|
|
|
232
306
|
functionDeclarations: [
|
|
233
307
|
{
|
|
234
308
|
name,
|
|
235
|
-
description,
|
|
309
|
+
description: rewriteDescription(name, description),
|
|
236
310
|
parameters
|
|
237
311
|
}
|
|
238
312
|
]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const OPENAI_CHAT_ALLOWED_FIELDS: readonly ["messages", "tools", "tool_outputs", "model", "temperature", "top_p", "top_k", "max_tokens", "frequency_penalty", "presence_penalty", "logit_bias", "response_format", "parallel_tool_calls", "tool_choice", "seed", "user", "metadata", "stop", "stop_sequences", "stream"];
|
|
2
|
+
export declare const ANTHROPIC_ALLOWED_FIELDS: readonly ["model", "messages", "tools", "system", "stop_sequences", "temperature", "top_p", "top_k", "max_tokens", "max_output_tokens", "metadata", "stream", "tool_choice"];
|
|
3
|
+
export declare const OPENAI_RESPONSES_ALLOWED_FIELDS: readonly ["id", "object", "created_at", "model", "status", "input", "instructions", "output", "output_text", "required_action", "response_id", "previous_response_id", "tool_outputs", "tools", "metadata", "include", "store", "user", "response_format", "temperature", "top_p", "top_k", "max_tokens", "max_output_tokens", "logit_bias", "seed", "parallel_tool_calls", "tool_choice", "stream", "instructions_is_raw"];
|
|
4
|
+
export declare const GEMINI_ALLOWED_FIELDS: readonly ["model", "contents", "systemInstruction", "system_instruction", "generationConfig", "generation_config", "safetySettings", "safety_settings", "metadata", "toolConfig", "tool_config", "tools", "tool_choice", "parallelToolCalls", "parallel_tool_calls", "responseMimeType", "response_mime_type", "stopSequences", "stop_sequences", "cachedContent", "prompt", "response", "candidates", "usageMetadata", "responseMetadata", "promptFeedback", "modelVersion", "client", "user", "stream"];
|
|
5
|
+
export declare const OPENAI_RESPONSES_PARAMETERS_WRAPPER_ALLOW_KEYS: readonly ["temperature", "top_p", "max_output_tokens", "seed", "logit_bias", "user", "parallel_tool_calls", "tool_choice", "response_format", "stream", "stop", "stop_sequences", "modalities", "top_k"];
|
|
6
|
+
export declare const OPENAI_CHAT_PARAMETERS_WRAPPER_ALLOW_KEYS: readonly ["temperature", "top_p", "top_k", "max_tokens", "frequency_penalty", "presence_penalty", "logit_bias", "seed", "user", "parallel_tool_calls", "tool_choice", "response_format", "stream", "stop", "stop_sequences"];
|
|
7
|
+
export declare const ANTHROPIC_PARAMETERS_WRAPPER_ALLOW_KEYS: readonly ["stop_sequences", "temperature", "top_p", "top_k", "max_tokens", "max_output_tokens", "metadata", "stream", "tool_choice"];
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
export const OPENAI_CHAT_ALLOWED_FIELDS = [
|
|
2
|
+
'messages',
|
|
3
|
+
'tools',
|
|
4
|
+
'tool_outputs',
|
|
5
|
+
'model',
|
|
6
|
+
'temperature',
|
|
7
|
+
'top_p',
|
|
8
|
+
'top_k',
|
|
9
|
+
'max_tokens',
|
|
10
|
+
'frequency_penalty',
|
|
11
|
+
'presence_penalty',
|
|
12
|
+
'logit_bias',
|
|
13
|
+
'response_format',
|
|
14
|
+
'parallel_tool_calls',
|
|
15
|
+
'tool_choice',
|
|
16
|
+
'seed',
|
|
17
|
+
'user',
|
|
18
|
+
'metadata',
|
|
19
|
+
'stop',
|
|
20
|
+
'stop_sequences',
|
|
21
|
+
'stream'
|
|
22
|
+
];
|
|
23
|
+
export const ANTHROPIC_ALLOWED_FIELDS = [
|
|
24
|
+
'model',
|
|
25
|
+
'messages',
|
|
26
|
+
'tools',
|
|
27
|
+
'system',
|
|
28
|
+
'stop_sequences',
|
|
29
|
+
'temperature',
|
|
30
|
+
'top_p',
|
|
31
|
+
'top_k',
|
|
32
|
+
'max_tokens',
|
|
33
|
+
'max_output_tokens',
|
|
34
|
+
'metadata',
|
|
35
|
+
'stream',
|
|
36
|
+
'tool_choice'
|
|
37
|
+
];
|
|
38
|
+
export const OPENAI_RESPONSES_ALLOWED_FIELDS = [
|
|
39
|
+
'id',
|
|
40
|
+
'object',
|
|
41
|
+
'created_at',
|
|
42
|
+
'model',
|
|
43
|
+
'status',
|
|
44
|
+
'input',
|
|
45
|
+
'instructions',
|
|
46
|
+
'output',
|
|
47
|
+
'output_text',
|
|
48
|
+
'required_action',
|
|
49
|
+
'response_id',
|
|
50
|
+
'previous_response_id',
|
|
51
|
+
'tool_outputs',
|
|
52
|
+
'tools',
|
|
53
|
+
'metadata',
|
|
54
|
+
'include',
|
|
55
|
+
'store',
|
|
56
|
+
'user',
|
|
57
|
+
'response_format',
|
|
58
|
+
'temperature',
|
|
59
|
+
'top_p',
|
|
60
|
+
'top_k',
|
|
61
|
+
'max_tokens',
|
|
62
|
+
'max_output_tokens',
|
|
63
|
+
'logit_bias',
|
|
64
|
+
'seed',
|
|
65
|
+
'parallel_tool_calls',
|
|
66
|
+
'tool_choice',
|
|
67
|
+
'stream',
|
|
68
|
+
'instructions_is_raw'
|
|
69
|
+
];
|
|
70
|
+
export const GEMINI_ALLOWED_FIELDS = [
|
|
71
|
+
'model',
|
|
72
|
+
'contents',
|
|
73
|
+
'systemInstruction',
|
|
74
|
+
'system_instruction',
|
|
75
|
+
'generationConfig',
|
|
76
|
+
'generation_config',
|
|
77
|
+
'safetySettings',
|
|
78
|
+
'safety_settings',
|
|
79
|
+
'metadata',
|
|
80
|
+
'toolConfig',
|
|
81
|
+
'tool_config',
|
|
82
|
+
'tools',
|
|
83
|
+
'tool_choice',
|
|
84
|
+
'parallelToolCalls',
|
|
85
|
+
'parallel_tool_calls',
|
|
86
|
+
'responseMimeType',
|
|
87
|
+
'response_mime_type',
|
|
88
|
+
'stopSequences',
|
|
89
|
+
'stop_sequences',
|
|
90
|
+
'cachedContent',
|
|
91
|
+
'prompt',
|
|
92
|
+
'response',
|
|
93
|
+
'candidates',
|
|
94
|
+
'usageMetadata',
|
|
95
|
+
'responseMetadata',
|
|
96
|
+
'promptFeedback',
|
|
97
|
+
'modelVersion',
|
|
98
|
+
'client',
|
|
99
|
+
'user',
|
|
100
|
+
'stream'
|
|
101
|
+
];
|
|
102
|
+
export const OPENAI_RESPONSES_PARAMETERS_WRAPPER_ALLOW_KEYS = [
|
|
103
|
+
'temperature',
|
|
104
|
+
'top_p',
|
|
105
|
+
'max_output_tokens',
|
|
106
|
+
'seed',
|
|
107
|
+
'logit_bias',
|
|
108
|
+
'user',
|
|
109
|
+
'parallel_tool_calls',
|
|
110
|
+
'tool_choice',
|
|
111
|
+
'response_format',
|
|
112
|
+
'stream',
|
|
113
|
+
'stop',
|
|
114
|
+
'stop_sequences',
|
|
115
|
+
'modalities',
|
|
116
|
+
'top_k'
|
|
117
|
+
];
|
|
118
|
+
export const OPENAI_CHAT_PARAMETERS_WRAPPER_ALLOW_KEYS = [
|
|
119
|
+
'temperature',
|
|
120
|
+
'top_p',
|
|
121
|
+
'top_k',
|
|
122
|
+
'max_tokens',
|
|
123
|
+
'frequency_penalty',
|
|
124
|
+
'presence_penalty',
|
|
125
|
+
'logit_bias',
|
|
126
|
+
'seed',
|
|
127
|
+
'user',
|
|
128
|
+
'parallel_tool_calls',
|
|
129
|
+
'tool_choice',
|
|
130
|
+
'response_format',
|
|
131
|
+
'stream',
|
|
132
|
+
'stop',
|
|
133
|
+
'stop_sequences'
|
|
134
|
+
];
|
|
135
|
+
export const ANTHROPIC_PARAMETERS_WRAPPER_ALLOW_KEYS = [
|
|
136
|
+
'stop_sequences',
|
|
137
|
+
'temperature',
|
|
138
|
+
'top_p',
|
|
139
|
+
'top_k',
|
|
140
|
+
'max_tokens',
|
|
141
|
+
'max_output_tokens',
|
|
142
|
+
'metadata',
|
|
143
|
+
'stream',
|
|
144
|
+
'tool_choice'
|
|
145
|
+
];
|
|
@@ -97,11 +97,13 @@ export function normalizeMessageReasoningTools(message, options) {
|
|
|
97
97
|
const trimmed = (cleanedText || '').trim();
|
|
98
98
|
writeReasoningContent(message, trimmed);
|
|
99
99
|
// Chat 统一行为:如果 message 本身没有正文内容,但存在非空 reasoning_content,
|
|
100
|
-
// 则将思考内容提升到正文,前后包裹 [思考]
|
|
100
|
+
// 则将思考内容提升到正文,前后包裹 [思考] 标记,避免客户端只看到"空回答"。
|
|
101
101
|
const rawContent = message.content;
|
|
102
102
|
if ((typeof rawContent !== 'string' || rawContent.trim().length === 0) &&
|
|
103
103
|
trimmed.length > 0) {
|
|
104
|
-
|
|
104
|
+
// 检查是否已经包含 [思考] 标签,避免重复嵌套
|
|
105
|
+
const hasThinkingTags = trimmed.includes('[思考]') && trimmed.includes('[/思考]');
|
|
106
|
+
message.content = hasThinkingTags ? trimmed : `[思考]\n${trimmed}\n[/思考]`;
|
|
105
107
|
}
|
|
106
108
|
if (!Array.isArray(toolCalls) || toolCalls.length === 0) {
|
|
107
109
|
return { toolCallsAdded: 0, cleanedReasoning: trimmed };
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
const DEFAULT_SNAPSHOT_ROOT = path.join(os.homedir(), '.routecodex', 'codex-samples');
|
|
5
|
+
const DEFAULT_ERRORSAMPLES_ROOT = path.join(os.homedir(), '.routecodex', 'errorsamples');
|
|
5
6
|
const PENDING_PROVIDER_DIR = '__pending__';
|
|
6
7
|
const POLICY_VIOLATIONS_DIR = '__policy_violations__';
|
|
7
8
|
function resolveSnapshotRoot() {
|
|
@@ -12,6 +13,75 @@ function resolveSnapshotRoot() {
|
|
|
12
13
|
}
|
|
13
14
|
return DEFAULT_SNAPSHOT_ROOT;
|
|
14
15
|
}
|
|
16
|
+
function resolveErrorsamplesRoot() {
|
|
17
|
+
const envOverride = process.env.ROUTECODEX_ERRORSAMPLES_DIR ||
|
|
18
|
+
process.env.ROUTECODEX_ERROR_SAMPLES_DIR;
|
|
19
|
+
if (envOverride && String(envOverride).trim()) {
|
|
20
|
+
return path.resolve(String(envOverride).trim());
|
|
21
|
+
}
|
|
22
|
+
return DEFAULT_ERRORSAMPLES_ROOT;
|
|
23
|
+
}
|
|
24
|
+
function safeErrorsampleName(name) {
|
|
25
|
+
return String(name || 'sample').replace(/[^\w.-]/g, '_');
|
|
26
|
+
}
|
|
27
|
+
async function cleanupZeroByteJsonFiles(dir) {
|
|
28
|
+
let entries = [];
|
|
29
|
+
try {
|
|
30
|
+
entries = await fs.readdir(dir);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const candidates = entries.filter((name) => name.endsWith('.json'));
|
|
36
|
+
if (candidates.length === 0) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
await Promise.allSettled(candidates.map(async (name) => {
|
|
40
|
+
const full = path.join(dir, name);
|
|
41
|
+
try {
|
|
42
|
+
const st = await fs.stat(full);
|
|
43
|
+
if (!st.isFile() || st.size > 0) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
await fs.unlink(full);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// ignore cleanup failures
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
async function writeUniqueErrorsampleFile(dir, baseName, contents) {
|
|
54
|
+
const parsed = path.parse(baseName);
|
|
55
|
+
const ext = parsed.ext || '.json';
|
|
56
|
+
const stem = parsed.name || 'sample';
|
|
57
|
+
const tmpDir = path.join(dir, '_tmp');
|
|
58
|
+
try {
|
|
59
|
+
await fs.mkdir(tmpDir, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// ignore; fallback to direct writes
|
|
63
|
+
}
|
|
64
|
+
for (let i = 0; i < 32; i += 1) {
|
|
65
|
+
const suffix = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
66
|
+
const fileName = `${stem}_${suffix}${ext}`;
|
|
67
|
+
try {
|
|
68
|
+
const dest = path.join(dir, fileName);
|
|
69
|
+
const tmp = path.join(tmpDir, `${fileName}.tmp`);
|
|
70
|
+
await fs.writeFile(tmp, contents, { encoding: 'utf-8', flag: 'wx' });
|
|
71
|
+
await fs.rename(tmp, dest);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (toErrorCode(error) === 'EEXIST') {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// last resort (best-effort overwrite into a timestamped file)
|
|
82
|
+
const fallback = `${stem}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}${ext}`;
|
|
83
|
+
await fs.writeFile(path.join(dir, fallback), contents, { encoding: 'utf-8' });
|
|
84
|
+
}
|
|
15
85
|
function resolveSnapshotFolder(endpoint) {
|
|
16
86
|
const lowered = (endpoint || '').toLowerCase();
|
|
17
87
|
if (lowered.includes('/responses')) {
|
|
@@ -177,6 +247,9 @@ function readNumberField(value) {
|
|
|
177
247
|
function isHubPolicyStage(stage) {
|
|
178
248
|
return typeof stage === 'string' && stage.startsWith('hub_policy.');
|
|
179
249
|
}
|
|
250
|
+
function isHubToolSurfaceStage(stage) {
|
|
251
|
+
return typeof stage === 'string' && stage.startsWith('hub_toolsurface.');
|
|
252
|
+
}
|
|
180
253
|
function hasPolicyViolations(value) {
|
|
181
254
|
if (!value || typeof value !== 'object') {
|
|
182
255
|
return false;
|
|
@@ -195,6 +268,24 @@ function hasPolicyViolations(value) {
|
|
|
195
268
|
}
|
|
196
269
|
return false;
|
|
197
270
|
}
|
|
271
|
+
function hasToolSurfaceDiff(value) {
|
|
272
|
+
if (!value || typeof value !== 'object') {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
const obj = value;
|
|
276
|
+
const diffCount = readNumberField(obj.diffCount);
|
|
277
|
+
if (typeof diffCount === 'number' && diffCount > 0) {
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
// Response-side tool surface mismatch detection records expected/detected protocol
|
|
281
|
+
// without a numeric diffCount; treat any protocol mismatch as a diff.
|
|
282
|
+
const expected = readStringField(obj.expectedProtocol);
|
|
283
|
+
const detected = readStringField(obj.detectedProtocol);
|
|
284
|
+
if (expected && detected && expected !== detected) {
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
198
289
|
function hasPolicyEnforcementChanges(value) {
|
|
199
290
|
if (!value || typeof value !== 'object') {
|
|
200
291
|
return false;
|
|
@@ -214,11 +305,25 @@ async function writeUniqueFile(dir, baseName, contents) {
|
|
|
214
305
|
const parsed = path.parse(baseName);
|
|
215
306
|
const ext = parsed.ext || '.json';
|
|
216
307
|
const stem = parsed.name || 'snapshot';
|
|
308
|
+
const tmpPrefix = `.__tmp_${stem}_${process.pid}_${Date.now()}`;
|
|
217
309
|
for (let i = 0; i < 64; i += 1) {
|
|
218
310
|
const name = i === 0 ? `${stem}${ext}` : `${stem}_${i}${ext}`;
|
|
219
311
|
try {
|
|
220
|
-
|
|
221
|
-
|
|
312
|
+
const dest = path.join(dir, name);
|
|
313
|
+
const tmp = path.join(dir, `${tmpPrefix}_${Math.random().toString(36).slice(2, 8)}${ext}`);
|
|
314
|
+
await fs.writeFile(tmp, contents, { encoding: 'utf-8', flag: 'wx' });
|
|
315
|
+
try {
|
|
316
|
+
await fs.link(tmp, dest);
|
|
317
|
+
await fs.unlink(tmp).catch(() => { });
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
await fs.unlink(tmp).catch(() => { });
|
|
322
|
+
if (toErrorCode(error) === 'EEXIST') {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
throw error;
|
|
326
|
+
}
|
|
222
327
|
}
|
|
223
328
|
catch (error) {
|
|
224
329
|
if (toErrorCode(error) === 'EEXIST') {
|
|
@@ -228,7 +333,15 @@ async function writeUniqueFile(dir, baseName, contents) {
|
|
|
228
333
|
}
|
|
229
334
|
}
|
|
230
335
|
const fallback = `${stem}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}${ext}`;
|
|
231
|
-
|
|
336
|
+
const dest = path.join(dir, fallback);
|
|
337
|
+
const tmp = path.join(dir, `${tmpPrefix}_${Math.random().toString(36).slice(2, 8)}${ext}`);
|
|
338
|
+
await fs.writeFile(tmp, contents, { encoding: 'utf-8', flag: 'wx' });
|
|
339
|
+
try {
|
|
340
|
+
await fs.link(tmp, dest);
|
|
341
|
+
}
|
|
342
|
+
finally {
|
|
343
|
+
await fs.unlink(tmp).catch(() => { });
|
|
344
|
+
}
|
|
232
345
|
}
|
|
233
346
|
const requestProviderIndex = globalThis
|
|
234
347
|
.__routecodexSnapshotProviderIndex ||
|
|
@@ -313,6 +426,23 @@ async function writeSnapshotFile(options, rootOverride) {
|
|
|
313
426
|
}
|
|
314
427
|
const dir = path.join(root, folder, providerToken, groupRequestToken);
|
|
315
428
|
await fs.mkdir(dir, { recursive: true });
|
|
429
|
+
// Write a stable runtime marker for this request folder (best-effort).
|
|
430
|
+
try {
|
|
431
|
+
const metaPath = path.join(dir, '__runtime.json');
|
|
432
|
+
const payload = JSON.stringify({
|
|
433
|
+
timestamp: new Date().toISOString(),
|
|
434
|
+
versions: {
|
|
435
|
+
routecodex: process.env.ROUTECODEX_VERSION,
|
|
436
|
+
routecodexBuildTime: process.env.ROUTECODEX_BUILD_TIME,
|
|
437
|
+
llmswitchCore: process.env.ROUTECODEX_LLMSWITCH_CORE_VERSION,
|
|
438
|
+
node: process.version
|
|
439
|
+
}
|
|
440
|
+
}, null, 2);
|
|
441
|
+
await fs.writeFile(metaPath, payload, { encoding: 'utf-8', flag: 'wx' }).catch(() => { });
|
|
442
|
+
}
|
|
443
|
+
catch {
|
|
444
|
+
// ignore
|
|
445
|
+
}
|
|
316
446
|
const spacing = options.verbosity === 'minimal' ? undefined : 2;
|
|
317
447
|
const payload = spacing !== undefined
|
|
318
448
|
? JSON.stringify(options.data, null, spacing)
|
|
@@ -342,4 +472,37 @@ export async function writeSnapshotViaHooks(options) {
|
|
|
342
472
|
catch {
|
|
343
473
|
// never block callers
|
|
344
474
|
}
|
|
475
|
+
// 3) Tool surface diffs (copy-on-diff only) → ~/.routecodex/errorsamples/tool-surface/
|
|
476
|
+
try {
|
|
477
|
+
if (isHubToolSurfaceStage(stage) && hasToolSurfaceDiff(options.data)) {
|
|
478
|
+
const root = resolveErrorsamplesRoot();
|
|
479
|
+
const dir = path.join(root, safeErrorsampleName('tool-surface'));
|
|
480
|
+
await fs.mkdir(dir, { recursive: true });
|
|
481
|
+
// Best-effort cleanup: when the process exits abruptly, an in-flight async
|
|
482
|
+
// write can leave behind 0-byte placeholder files. Keep the observable
|
|
483
|
+
// directory clean so operators can tail it.
|
|
484
|
+
await cleanupZeroByteJsonFiles(dir);
|
|
485
|
+
const stamp = new Date().toISOString();
|
|
486
|
+
const payload = {
|
|
487
|
+
kind: 'hub_toolsurface_diff',
|
|
488
|
+
timestamp: stamp,
|
|
489
|
+
endpoint: options.endpoint,
|
|
490
|
+
stage,
|
|
491
|
+
requestId: options.requestId,
|
|
492
|
+
providerKey: options.providerKey,
|
|
493
|
+
groupRequestId: options.groupRequestId,
|
|
494
|
+
runtime: {
|
|
495
|
+
routecodexVersion: process.env.ROUTECODEX_VERSION,
|
|
496
|
+
routecodexBuildTime: process.env.ROUTECODEX_BUILD_TIME,
|
|
497
|
+
llmswitchCore: process.env.ROUTECODEX_LLMSWITCH_CORE_VERSION,
|
|
498
|
+
node: process.version
|
|
499
|
+
},
|
|
500
|
+
observation: options.data
|
|
501
|
+
};
|
|
502
|
+
await writeUniqueErrorsampleFile(dir, `${safeErrorsampleName(stage)}.json`, JSON.stringify(payload, null, 2));
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
catch {
|
|
506
|
+
// never block callers
|
|
507
|
+
}
|
|
345
508
|
}
|