@jsonstudio/llms 0.6.631 → 0.6.743
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/codecs/anthropic-openai-codec.js +0 -5
- package/dist/conversion/codecs/openai-openai-codec.js +0 -6
- package/dist/conversion/codecs/responses-openai-codec.js +1 -7
- package/dist/conversion/hub/node-support.js +5 -4
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +14 -1
- package/dist/conversion/hub/pipeline/hub-pipeline.js +82 -18
- package/dist/conversion/hub/pipeline/session-identifiers.js +132 -2
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +130 -15
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +47 -0
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +4 -2
- package/dist/conversion/hub/process/chat-process.js +2 -0
- package/dist/conversion/hub/response/provider-response.js +6 -1
- package/dist/conversion/hub/snapshot-recorder.js +8 -1
- package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.js +0 -7
- package/dist/conversion/responses/responses-openai-bridge.js +47 -7
- package/dist/conversion/shared/compaction-detect.d.ts +2 -0
- package/dist/conversion/shared/compaction-detect.js +53 -0
- package/dist/conversion/shared/errors.d.ts +1 -1
- package/dist/conversion/shared/reasoning-tool-normalizer.js +7 -0
- package/dist/conversion/shared/snapshot-hooks.d.ts +2 -0
- package/dist/conversion/shared/snapshot-hooks.js +180 -4
- package/dist/conversion/shared/snapshot-utils.d.ts +4 -0
- package/dist/conversion/shared/snapshot-utils.js +4 -0
- package/dist/conversion/shared/tool-filter-pipeline.js +3 -9
- package/dist/conversion/shared/tool-governor.d.ts +2 -0
- package/dist/conversion/shared/tool-governor.js +101 -13
- package/dist/conversion/shared/tool-harvester.js +42 -2
- package/dist/conversion/shared/tooling.d.ts +33 -0
- package/dist/conversion/shared/tooling.js +27 -0
- package/dist/filters/index.d.ts +0 -2
- package/dist/filters/index.js +0 -2
- package/dist/filters/special/request-tools-normalize.d.ts +11 -0
- package/dist/filters/special/request-tools-normalize.js +13 -50
- package/dist/filters/special/response-apply-patch-toon-decode.js +410 -67
- package/dist/filters/special/response-tool-arguments-stringify.js +25 -16
- package/dist/filters/special/response-tool-arguments-toon-decode.js +8 -76
- package/dist/filters/utils/snapshot-writer.js +42 -4
- package/dist/guidance/index.js +8 -2
- package/dist/router/virtual-router/engine-health.js +0 -4
- package/dist/router/virtual-router/engine-selection.d.ts +2 -1
- package/dist/router/virtual-router/engine-selection.js +101 -9
- package/dist/router/virtual-router/engine.d.ts +5 -1
- package/dist/router/virtual-router/engine.js +188 -5
- package/dist/router/virtual-router/routing-instructions.d.ts +6 -0
- package/dist/router/virtual-router/routing-instructions.js +18 -3
- package/dist/router/virtual-router/sticky-session-store.d.ts +1 -0
- package/dist/router/virtual-router/sticky-session-store.js +36 -0
- package/dist/router/virtual-router/types.d.ts +22 -0
- package/dist/servertool/engine.js +335 -9
- package/dist/servertool/handlers/compaction-detect.d.ts +1 -0
- package/dist/servertool/handlers/compaction-detect.js +1 -0
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +29 -5
- package/dist/servertool/handlers/iflow-model-error-retry.js +17 -0
- package/dist/servertool/handlers/stop-message-auto.js +199 -19
- package/dist/servertool/server-side-tools.d.ts +0 -1
- package/dist/servertool/server-side-tools.js +0 -1
- package/dist/servertool/types.d.ts +1 -0
- package/dist/tools/apply-patch-structured.js +52 -15
- package/dist/tools/tool-registry.js +537 -15
- package/dist/utils/toon.d.ts +4 -0
- package/dist/utils/toon.js +75 -0
- package/package.json +4 -2
- package/dist/test-output/virtual-router/results.json +0 -1
- package/dist/test-output/virtual-router/summary.json +0 -12
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// enforceChatBudget: 为避免在请求侧引入多余依赖,这里提供最小实现(保留形状,不裁剪)。
|
|
5
5
|
import { augmentOpenAITools } from '../../guidance/index.js';
|
|
6
6
|
import { validateToolCall } from '../../tools/tool-registry.js';
|
|
7
|
+
import { repairFindMeta } from './tooling.js';
|
|
7
8
|
function isObject(v) { return !!v && typeof v === 'object' && !Array.isArray(v); }
|
|
8
9
|
// Note: tool schema strict augmentation removed per alignment
|
|
9
10
|
function enforceChatBudget(chat, _modelId) { return chat; }
|
|
@@ -25,8 +26,8 @@ function tryWriteSnapshot(options, stage, data) {
|
|
|
25
26
|
const ep = String(snap.endpoint || 'chat').toLowerCase();
|
|
26
27
|
const group = ep.includes('responses') ? 'openai-responses' : ep.includes('messages') ? 'anthropic-messages' : 'openai-chat';
|
|
27
28
|
const rid = String(snap.requestId || `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`);
|
|
28
|
-
const dir = path.join(base, group);
|
|
29
|
-
const file = path.join(dir,
|
|
29
|
+
const dir = path.join(base, group, '__pending__', rid);
|
|
30
|
+
const file = path.join(dir, `govern-${stage}.json`);
|
|
30
31
|
if (fs.existsSync(file))
|
|
31
32
|
return; // 不重复
|
|
32
33
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -209,7 +210,7 @@ export function processChatRequestTools(request, opts) {
|
|
|
209
210
|
// 4) Enforce payload budget (context bytes) with minimal loss policy
|
|
210
211
|
const modelId = typeof canonical?.model === 'string' ? String(canonical.model) : 'unknown';
|
|
211
212
|
const budgeted = enforceChatBudget(canonical, modelId);
|
|
212
|
-
return
|
|
213
|
+
return normalizeSpecialToolCallsOnRequest(budgeted);
|
|
213
214
|
}
|
|
214
215
|
catch {
|
|
215
216
|
return out;
|
|
@@ -220,7 +221,7 @@ export function processChatRequestTools(request, opts) {
|
|
|
220
221
|
* - Canonicalize textual tool markup to tool_calls; ensure finish_reason and content=null policy
|
|
221
222
|
*/
|
|
222
223
|
import { repairArgumentsToString, parseLenient } from './jsonish.js';
|
|
223
|
-
function normalizeApplyPatchToolCallsOnResponse(chat) {
|
|
224
|
+
export function normalizeApplyPatchToolCallsOnResponse(chat) {
|
|
224
225
|
try {
|
|
225
226
|
const out = JSON.parse(JSON.stringify(chat));
|
|
226
227
|
const choices = Array.isArray(out?.choices) ? out.choices : [];
|
|
@@ -241,6 +242,19 @@ function normalizeApplyPatchToolCallsOnResponse(chat) {
|
|
|
241
242
|
if (validation && validation.ok && typeof validation.normalizedArgs === 'string') {
|
|
242
243
|
fn.arguments = validation.normalizedArgs;
|
|
243
244
|
}
|
|
245
|
+
else if (validation && !validation.ok) {
|
|
246
|
+
try {
|
|
247
|
+
const reason = validation.reason ?? 'unknown';
|
|
248
|
+
const snippet = typeof argsStr === 'string' && argsStr.trim().length
|
|
249
|
+
? argsStr.trim().slice(0, 200).replace(/\s+/g, ' ')
|
|
250
|
+
: '';
|
|
251
|
+
// eslint-disable-next-line no-console
|
|
252
|
+
console.error(`\x1b[31m[apply_patch][precheck][response] validation_failed reason=${reason}${snippet ? ` args=${snippet}` : ''}\x1b[0m`);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// logging best-effort
|
|
256
|
+
}
|
|
257
|
+
}
|
|
244
258
|
}
|
|
245
259
|
catch {
|
|
246
260
|
// best-effort per tool_call
|
|
@@ -253,14 +267,32 @@ function normalizeApplyPatchToolCallsOnResponse(chat) {
|
|
|
253
267
|
return chat;
|
|
254
268
|
}
|
|
255
269
|
}
|
|
256
|
-
function normalizeApplyPatchToolCallsOnRequest(request) {
|
|
270
|
+
export function normalizeApplyPatchToolCallsOnRequest(request) {
|
|
271
|
+
return normalizeSpecialToolCallsOnRequest(request);
|
|
272
|
+
}
|
|
273
|
+
function normalizeSpecialToolCallsOnRequest(request) {
|
|
257
274
|
try {
|
|
258
275
|
const out = JSON.parse(JSON.stringify(request));
|
|
259
276
|
const messages = Array.isArray(out?.messages) ? out.messages : [];
|
|
260
|
-
|
|
277
|
+
// 仅针对「当轮」工具调用做校验与形态修复:选择最后一条 assistant 消息
|
|
278
|
+
let lastAssistantIndex = -1;
|
|
279
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
280
|
+
const candidate = messages[i];
|
|
281
|
+
if (!candidate || typeof candidate !== 'object')
|
|
282
|
+
continue;
|
|
283
|
+
if (candidate.role === 'assistant') {
|
|
284
|
+
lastAssistantIndex = i;
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (lastAssistantIndex === -1) {
|
|
289
|
+
return out;
|
|
290
|
+
}
|
|
291
|
+
for (let i = 0; i < messages.length; i += 1) {
|
|
292
|
+
const msg = messages[i];
|
|
261
293
|
if (!msg || typeof msg !== 'object')
|
|
262
294
|
continue;
|
|
263
|
-
if (
|
|
295
|
+
if (i !== lastAssistantIndex)
|
|
264
296
|
continue;
|
|
265
297
|
const tcs = Array.isArray(msg.tool_calls) ? msg.tool_calls : [];
|
|
266
298
|
if (!tcs.length)
|
|
@@ -269,13 +301,48 @@ function normalizeApplyPatchToolCallsOnRequest(request) {
|
|
|
269
301
|
try {
|
|
270
302
|
const fn = tc && tc.function ? tc.function : undefined;
|
|
271
303
|
const name = typeof fn?.name === 'string' ? String(fn.name).trim().toLowerCase() : '';
|
|
272
|
-
if (name !== 'apply_patch')
|
|
273
|
-
continue;
|
|
274
304
|
const rawArgs = fn?.arguments;
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
305
|
+
// apply_patch 兼容:统一生成 { patch, input }
|
|
306
|
+
if (name === 'apply_patch') {
|
|
307
|
+
const argsStr = repairArgumentsToString(rawArgs);
|
|
308
|
+
const validation = validateToolCall('apply_patch', argsStr);
|
|
309
|
+
if (validation && validation.ok && typeof validation.normalizedArgs === 'string') {
|
|
310
|
+
fn.arguments = validation.normalizedArgs;
|
|
311
|
+
}
|
|
312
|
+
else if (validation && !validation.ok) {
|
|
313
|
+
try {
|
|
314
|
+
const reason = validation.reason ?? 'unknown';
|
|
315
|
+
const snippet = typeof argsStr === 'string' && argsStr.trim().length
|
|
316
|
+
? argsStr.trim().slice(0, 200).replace(/\s+/g, ' ')
|
|
317
|
+
: '';
|
|
318
|
+
// eslint-disable-next-line no-console
|
|
319
|
+
console.error(`\x1b[31m[apply_patch][precheck][request] validation_failed reason=${reason}${snippet ? ` args=${snippet}` : ''}\x1b[0m`);
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
// logging best-effort
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
// exec_command 兼容:TOON / map / string 一律收敛为 { cmd, command, workdir, ... }
|
|
328
|
+
if (name === 'exec_command') {
|
|
329
|
+
const argsStr = repairArgumentsToString(rawArgs);
|
|
330
|
+
let parsed;
|
|
331
|
+
try {
|
|
332
|
+
parsed = JSON.parse(argsStr);
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
parsed = parseLenient(argsStr);
|
|
336
|
+
}
|
|
337
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
338
|
+
const normalized = normalizeExecCommandArgs(parsed);
|
|
339
|
+
try {
|
|
340
|
+
fn.arguments = JSON.stringify(normalized ?? {});
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
fn.arguments = '{}';
|
|
344
|
+
}
|
|
345
|
+
}
|
|
279
346
|
}
|
|
280
347
|
}
|
|
281
348
|
catch {
|
|
@@ -289,6 +356,27 @@ function normalizeApplyPatchToolCallsOnRequest(request) {
|
|
|
289
356
|
return request;
|
|
290
357
|
}
|
|
291
358
|
}
|
|
359
|
+
function normalizeExecCommandArgs(args) {
|
|
360
|
+
try {
|
|
361
|
+
const out = { ...args };
|
|
362
|
+
const rawCmd = typeof out.cmd === 'string' && out.cmd.trim().length
|
|
363
|
+
? String(out.cmd)
|
|
364
|
+
: typeof out.command === 'string' && out.command.trim().length
|
|
365
|
+
? String(out.command)
|
|
366
|
+
: undefined;
|
|
367
|
+
if (rawCmd) {
|
|
368
|
+
const fixed = repairFindMeta(rawCmd);
|
|
369
|
+
out.cmd = fixed;
|
|
370
|
+
if (typeof out.command === 'string') {
|
|
371
|
+
out.command = fixed;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return out;
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
return args;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
292
380
|
function enhanceResponseToolArguments(chat) {
|
|
293
381
|
try {
|
|
294
382
|
const enable = String(process?.env?.RCC_TOOL_ENHANCE ?? '1').trim() !== '0';
|
|
@@ -80,8 +80,48 @@ function extractFromTextual(content, ctx) {
|
|
|
80
80
|
}
|
|
81
81
|
return events;
|
|
82
82
|
}
|
|
83
|
-
// 3)
|
|
84
|
-
|
|
83
|
+
// 3) <invoke name="tool"> wrapper with <parameter name="key">value</parameter>
|
|
84
|
+
try {
|
|
85
|
+
const invokeRe = /<invoke\s+name="([^">]+)"[^>]*>([\s\S]*?)<\/invoke>/gi;
|
|
86
|
+
let mInvoke;
|
|
87
|
+
const invokeEvents = [];
|
|
88
|
+
let idx = 0;
|
|
89
|
+
while ((mInvoke = invokeRe.exec(content)) !== null) {
|
|
90
|
+
const toolName = (mInvoke[1] || '').trim();
|
|
91
|
+
const inner = mInvoke[2] || '';
|
|
92
|
+
if (!toolName || !inner)
|
|
93
|
+
continue;
|
|
94
|
+
const paramRe = /<parameter\s+name="([^">]+)"[^>]*>([\s\S]*?)<\/parameter>/gi;
|
|
95
|
+
const argsObj = {};
|
|
96
|
+
let mParam;
|
|
97
|
+
while ((mParam = paramRe.exec(inner)) !== null) {
|
|
98
|
+
const key = String((mParam[1] || '').trim());
|
|
99
|
+
const raw = (mParam[2] || '').trim();
|
|
100
|
+
if (!key)
|
|
101
|
+
continue;
|
|
102
|
+
let value = raw;
|
|
103
|
+
try {
|
|
104
|
+
value = JSON.parse(raw);
|
|
105
|
+
}
|
|
106
|
+
catch { /* keep string */ }
|
|
107
|
+
argsObj[key] = value;
|
|
108
|
+
}
|
|
109
|
+
const id = genId(ctx, idx);
|
|
110
|
+
invokeEvents.push({ tool_calls: [{ index: idx, id, type: 'function', function: { name: toolName } }] });
|
|
111
|
+
const argStr = toJsonString(argsObj);
|
|
112
|
+
const parts = chunkString(argStr, Math.max(32, Math.min(1024, ctx?.chunkSize || 256)));
|
|
113
|
+
for (const d of parts) {
|
|
114
|
+
invokeEvents.push({ tool_calls: [{ index: idx, id, type: 'function', function: { arguments: d } }] });
|
|
115
|
+
}
|
|
116
|
+
idx += 1;
|
|
117
|
+
}
|
|
118
|
+
if (invokeEvents.length) {
|
|
119
|
+
return invokeEvents;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch { /* ignore textual invoke parse errors */ }
|
|
123
|
+
// 4) rcc.tool.v1 JSON envelope (removed)
|
|
124
|
+
// 5) Generic <tool_call> textual block with <arg_key>/<arg_value>
|
|
85
125
|
try {
|
|
86
126
|
// Explicit wrapper form
|
|
87
127
|
const blockRe = /<tool_call[^>]*>[\s\S]*?<\/tool_call>/gi;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for standard tool normalization (shell packing rules).
|
|
3
|
+
* The goal is deterministic, minimal shaping so executors succeed consistently.
|
|
4
|
+
*/
|
|
5
|
+
export interface ShellArgs {
|
|
6
|
+
command: string | string[];
|
|
7
|
+
workdir?: string;
|
|
8
|
+
timeout_ms?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Minimal, idempotent repairs for common `find` invocations inside shell scripts:
|
|
12
|
+
* - ensure `-exec … ;` is escaped as `-exec … \;`
|
|
13
|
+
* - collapse multiple backslashes before `;` into a single backslash
|
|
14
|
+
* - escape bare parentheses used in predicates: `(` / `)` → `\(` / `\)`
|
|
15
|
+
*/
|
|
16
|
+
export declare function repairFindMeta(script: string): string;
|
|
17
|
+
export declare function splitCommandString(input: string): string[];
|
|
18
|
+
/**
|
|
19
|
+
* Pack shell arguments per unified rules:
|
|
20
|
+
* - command: string -> ["bash","-lc","<string>"]
|
|
21
|
+
* - command: tokens[]
|
|
22
|
+
* - if starts with ["cd", path, ...rest]:
|
|
23
|
+
* - set workdir to path when absent
|
|
24
|
+
* - if rest empty => command=["pwd"]
|
|
25
|
+
* - else if rest has control tokens => command=["bash","-lc", join(rest)]
|
|
26
|
+
* - else command=rest (argv)
|
|
27
|
+
* - else if tokens contain control tokens => command=["bash","-lc", join(tokens)]
|
|
28
|
+
* - else command=tokens (argv)
|
|
29
|
+
* - join(rest) uses single-space join without extra quoting
|
|
30
|
+
*/
|
|
31
|
+
export declare function packShellArgs(input: Record<string, unknown>): Record<string, unknown>;
|
|
32
|
+
export declare function flattenByComma(arr: string[]): string[];
|
|
33
|
+
export declare function chunkString(s: string, minParts?: number, maxParts?: number, targetChunk?: number): string[];
|
|
@@ -5,6 +5,33 @@
|
|
|
5
5
|
// We intentionally do NOT evaluate shell control operators (&&, |, etc.).
|
|
6
6
|
// Codex CLI executor runs argv directly (execvp-like), not through a shell.
|
|
7
7
|
// So we avoid wrapping with "bash -lc" and leave such tokens as-is.
|
|
8
|
+
/**
|
|
9
|
+
* Minimal, idempotent repairs for common `find` invocations inside shell scripts:
|
|
10
|
+
* - ensure `-exec … ;` is escaped as `-exec … \;`
|
|
11
|
+
* - collapse multiple backslashes before `;` into a single backslash
|
|
12
|
+
* - escape bare parentheses used in predicates: `(` / `)` → `\(` / `\)`
|
|
13
|
+
*/
|
|
14
|
+
export function repairFindMeta(script) {
|
|
15
|
+
try {
|
|
16
|
+
const s = String(script ?? '');
|
|
17
|
+
if (!s)
|
|
18
|
+
return s;
|
|
19
|
+
const hasFind = /(^|\s)find\s/.test(s);
|
|
20
|
+
if (!hasFind)
|
|
21
|
+
return s;
|
|
22
|
+
let out = s;
|
|
23
|
+
// Only escape semicolon not already escaped (negative lookbehind)
|
|
24
|
+
out = out.replace(/-exec([^;]*?)(?<!\\);/g, (_m, g1) => `-exec${g1} \\;`);
|
|
25
|
+
// Collapse multiple backslashes immediately before ; into a single backslash
|
|
26
|
+
out = out.replace(/-exec([^;]*?)\\+;/g, (_m, g1) => `-exec${g1} \\;`);
|
|
27
|
+
// Escape parentheses only when not already escaped
|
|
28
|
+
out = out.replace(/(?<!\\)\(/g, '\\(').replace(/(?<!\\)\)/g, '\\)');
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return script;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
8
35
|
function toStringArray(v) {
|
|
9
36
|
if (Array.isArray(v))
|
|
10
37
|
return v.map((x) => String(x));
|
package/dist/filters/index.d.ts
CHANGED
|
@@ -12,8 +12,6 @@ export * from './special/response-tool-text-canonicalize.js';
|
|
|
12
12
|
export * from './special/response-tool-arguments-stringify.js';
|
|
13
13
|
export * from './special/response-finish-invariants.js';
|
|
14
14
|
export * from './special/request-tools-normalize.js';
|
|
15
|
-
export * from './special/response-tool-arguments-toon-decode.js';
|
|
16
|
-
export * from './special/response-apply-patch-toon-decode.js';
|
|
17
15
|
export * from './special/response-tool-arguments-blacklist.js';
|
|
18
16
|
export * from './special/response-tool-arguments-schema-converge.js';
|
|
19
17
|
export * from './special/response-tool-arguments-whitelist.js';
|
package/dist/filters/index.js
CHANGED
|
@@ -14,8 +14,6 @@ export * from './special/response-tool-arguments-stringify.js';
|
|
|
14
14
|
export * from './special/response-finish-invariants.js';
|
|
15
15
|
// TOON support (default ON via RCC_TOON_ENABLE unless explicitly disabled)
|
|
16
16
|
export * from './special/request-tools-normalize.js';
|
|
17
|
-
export * from './special/response-tool-arguments-toon-decode.js';
|
|
18
|
-
export * from './special/response-apply-patch-toon-decode.js';
|
|
19
17
|
// Arguments policy filters (synced)
|
|
20
18
|
export * from './special/response-tool-arguments-blacklist.js';
|
|
21
19
|
export * from './special/response-tool-arguments-schema-converge.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Normalize OpenAI tools definitions at the final request stage.
|
|
4
|
+
* - Enforces { command: string | string[], workdir?: string } shape for shell-like tools.
|
|
5
|
+
* - Best-effort; never throws.
|
|
6
|
+
*/
|
|
7
|
+
export declare class RequestOpenAIToolsNormalizeFilter implements Filter<JsonObject> {
|
|
8
|
+
readonly name = "request_openai_tools_normalize";
|
|
9
|
+
readonly stage: FilterContext['stage'];
|
|
10
|
+
apply(input: JsonObject): Promise<FilterResult<JsonObject>>;
|
|
11
|
+
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildShellDescription, hasApplyPatchToolDeclared, isShellToolName } from '../../tools/tool-description-utils.js';
|
|
2
2
|
function isObject(v) {
|
|
3
3
|
return !!v && typeof v === 'object' && !Array.isArray(v);
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
6
|
* Normalize OpenAI tools definitions at the final request stage.
|
|
7
|
-
* -
|
|
8
|
-
* - Otherwise enforces { command: string | string[], workdir?: string } shape.
|
|
7
|
+
* - Enforces { command: string | string[], workdir?: string } shape for shell-like tools.
|
|
9
8
|
* - Best-effort; never throws.
|
|
10
9
|
*/
|
|
11
10
|
export class RequestOpenAIToolsNormalizeFilter {
|
|
@@ -14,23 +13,6 @@ export class RequestOpenAIToolsNormalizeFilter {
|
|
|
14
13
|
async apply(input) {
|
|
15
14
|
try {
|
|
16
15
|
const out = JSON.parse(JSON.stringify(input || {}));
|
|
17
|
-
const messages = Array.isArray(out.messages) ? out.messages : [];
|
|
18
|
-
const lastUser = [...messages].reverse().find((m) => m && m.role === 'user');
|
|
19
|
-
let lastText = '';
|
|
20
|
-
if (typeof lastUser?.content === 'string') {
|
|
21
|
-
lastText = lastUser.content;
|
|
22
|
-
}
|
|
23
|
-
else if (Array.isArray(lastUser?.content)) {
|
|
24
|
-
for (const p of lastUser.content) {
|
|
25
|
-
if (p && typeof p.text === 'string' && p.text.trim()) {
|
|
26
|
-
lastText = p.text;
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
const looksHeredoc = /<<[-']?\w+/.test(lastText);
|
|
32
|
-
const toonEnv = String(process?.env?.RCC_TOON_ENABLE || process?.env?.ROUTECODEX_TOON_ENABLE || '').toLowerCase();
|
|
33
|
-
const toonEnabled = toonEnv ? !(toonEnv === '0' || toonEnv === 'false' || toonEnv === 'off') : true; // default ON
|
|
34
16
|
const tools = Array.isArray(out.tools) ? out.tools : [];
|
|
35
17
|
const hasApplyPatchTool = hasApplyPatchToolDeclared(tools);
|
|
36
18
|
if (!tools.length) {
|
|
@@ -79,37 +61,18 @@ export class RequestOpenAIToolsNormalizeFilter {
|
|
|
79
61
|
try {
|
|
80
62
|
const rawToolName = String(dst.function.name || '');
|
|
81
63
|
const isShell = isShellToolName(rawToolName);
|
|
82
|
-
const isApplyPatch = normalizeToolName(rawToolName) === 'apply_patch';
|
|
83
64
|
if (isShell) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
type: '
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
additionalProperties: false
|
|
96
|
-
};
|
|
97
|
-
const toonDescription = 'Use TOON: put arguments into arguments.toon as multi-line key: value (e.g., command / workdir).';
|
|
98
|
-
dst.function.description = appendApplyPatchReminder(toonDescription, hasApplyPatchTool);
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
dst.function.parameters = {
|
|
102
|
-
type: 'object',
|
|
103
|
-
properties: {
|
|
104
|
-
command: { oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }] },
|
|
105
|
-
workdir: { type: 'string' }
|
|
106
|
-
},
|
|
107
|
-
required: ['command'],
|
|
108
|
-
additionalProperties: false
|
|
109
|
-
};
|
|
110
|
-
const label = rawToolName && rawToolName.trim().length > 0 ? rawToolName.trim() : 'shell';
|
|
111
|
-
dst.function.description = buildShellDescription(label, hasApplyPatchTool);
|
|
112
|
-
}
|
|
65
|
+
dst.function.parameters = {
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: {
|
|
68
|
+
command: { oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }] },
|
|
69
|
+
workdir: { type: 'string' }
|
|
70
|
+
},
|
|
71
|
+
required: ['command'],
|
|
72
|
+
additionalProperties: false
|
|
73
|
+
};
|
|
74
|
+
const label = rawToolName && rawToolName.trim().length > 0 ? rawToolName.trim() : 'shell';
|
|
75
|
+
dst.function.description = buildShellDescription(label, hasApplyPatchTool);
|
|
113
76
|
}
|
|
114
77
|
}
|
|
115
78
|
catch { /* ignore */ }
|