@jsonstudio/llms 0.6.749 → 0.6.795
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/compat/actions/apply-patch-fixer.d.ts +1 -0
- package/dist/conversion/compat/actions/apply-patch-fixer.js +30 -0
- package/dist/conversion/compat/actions/apply-patch-format-fixer.d.ts +1 -0
- package/dist/conversion/compat/actions/apply-patch-format-fixer.js +233 -0
- package/dist/conversion/compat/actions/index.d.ts +2 -0
- package/dist/conversion/compat/actions/index.js +2 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +6 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +35 -0
- package/dist/conversion/shared/bridge-message-utils.d.ts +1 -0
- package/dist/conversion/shared/bridge-message-utils.js +7 -0
- package/dist/conversion/shared/bridge-policies.js +8 -8
- package/dist/conversion/shared/errors.d.ts +1 -1
- package/dist/conversion/shared/errors.js +3 -0
- package/dist/conversion/shared/tool-governor.js +18 -23
- package/dist/filters/special/response-tool-arguments-stringify.js +3 -22
- package/dist/router/virtual-router/engine.d.ts +5 -0
- package/dist/router/virtual-router/engine.js +21 -0
- package/dist/servertool/engine.js +74 -3
- package/dist/servertool/server-side-tools.js +19 -8
- package/dist/tools/apply-patch/regression-capturer.d.ts +12 -0
- package/dist/tools/apply-patch/regression-capturer.js +112 -0
- package/dist/tools/apply-patch/structured.d.ts +20 -0
- package/dist/tools/apply-patch/structured.js +441 -0
- package/dist/tools/apply-patch/validator.d.ts +8 -0
- package/dist/tools/apply-patch/validator.js +466 -0
- package/dist/tools/apply-patch-structured.d.ts +1 -20
- package/dist/tools/apply-patch-structured.js +1 -277
- package/dist/tools/args-json.d.ts +1 -0
- package/dist/tools/args-json.js +175 -0
- package/dist/tools/exec-command/normalize.d.ts +17 -0
- package/dist/tools/exec-command/normalize.js +112 -0
- package/dist/tools/exec-command/regression-capturer.d.ts +11 -0
- package/dist/tools/exec-command/regression-capturer.js +144 -0
- package/dist/tools/exec-command/validator.d.ts +6 -0
- package/dist/tools/exec-command/validator.js +22 -0
- package/dist/tools/patch-args-normalizer.d.ts +15 -0
- package/dist/tools/patch-args-normalizer.js +472 -0
- package/dist/tools/patch-regression-capturer.d.ts +1 -0
- package/dist/tools/patch-regression-capturer.js +1 -0
- package/dist/tools/tool-registry.js +36 -541
- package/package.json +1 -1
|
@@ -3,7 +3,64 @@ import { ProviderProtocolError } from '../conversion/shared/errors.js';
|
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { loadRoutingInstructionStateSync, saveRoutingInstructionStateSync } from '../router/virtual-router/sticky-session-store.js';
|
|
5
5
|
import { deserializeRoutingInstructionState, serializeRoutingInstructionState } from '../router/virtual-router/routing-instructions.js';
|
|
6
|
+
function parseTimeoutMs(raw, fallback) {
|
|
7
|
+
const n = typeof raw === 'string' ? Number(raw.trim()) : typeof raw === 'number' ? raw : NaN;
|
|
8
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
9
|
+
return fallback;
|
|
10
|
+
}
|
|
11
|
+
return Math.floor(n);
|
|
12
|
+
}
|
|
13
|
+
function resolveServerToolTimeoutMs() {
|
|
14
|
+
return parseTimeoutMs(process.env.ROUTECODEX_SERVERTOOL_TIMEOUT_MS ||
|
|
15
|
+
process.env.RCC_SERVERTOOL_TIMEOUT_MS ||
|
|
16
|
+
process.env.LLMSWITCH_SERVERTOOL_TIMEOUT_MS, 500_000);
|
|
17
|
+
}
|
|
18
|
+
function resolveServerToolFollowupTimeoutMs(fallback) {
|
|
19
|
+
return parseTimeoutMs(process.env.ROUTECODEX_SERVERTOOL_FOLLOWUP_TIMEOUT_MS ||
|
|
20
|
+
process.env.RCC_SERVERTOOL_FOLLOWUP_TIMEOUT_MS ||
|
|
21
|
+
process.env.LLMSWITCH_SERVERTOOL_FOLLOWUP_TIMEOUT_MS, fallback);
|
|
22
|
+
}
|
|
23
|
+
function withTimeout(promise, timeoutMs, buildError) {
|
|
24
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
25
|
+
return promise;
|
|
26
|
+
}
|
|
27
|
+
let timer;
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
timer = setTimeout(() => reject(buildError()), timeoutMs);
|
|
30
|
+
promise.then(resolve, reject).finally(() => {
|
|
31
|
+
if (timer) {
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
timer = undefined;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function isServerToolTimeoutError(error) {
|
|
39
|
+
return Boolean(error &&
|
|
40
|
+
typeof error === 'object' &&
|
|
41
|
+
typeof error.code === 'string' &&
|
|
42
|
+
error.code === 'SERVERTOOL_TIMEOUT');
|
|
43
|
+
}
|
|
44
|
+
function createServerToolTimeoutError(options) {
|
|
45
|
+
const err = new ProviderProtocolError(`[servertool] ${options.phase} timeout after ${options.timeoutMs}ms` +
|
|
46
|
+
(options.flowId ? ` flow=${options.flowId}` : ''), {
|
|
47
|
+
code: 'SERVERTOOL_TIMEOUT',
|
|
48
|
+
category: 'INTERNAL_ERROR',
|
|
49
|
+
details: {
|
|
50
|
+
requestId: options.requestId,
|
|
51
|
+
phase: options.phase,
|
|
52
|
+
flowId: options.flowId,
|
|
53
|
+
timeoutMs: options.timeoutMs,
|
|
54
|
+
attempt: options.attempt,
|
|
55
|
+
maxAttempts: options.maxAttempts
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
err.status = 504;
|
|
59
|
+
return err;
|
|
60
|
+
}
|
|
6
61
|
export async function runServerToolOrchestration(options) {
|
|
62
|
+
const serverToolTimeoutMs = resolveServerToolTimeoutMs();
|
|
63
|
+
const followupTimeoutMs = resolveServerToolFollowupTimeoutMs(serverToolTimeoutMs);
|
|
7
64
|
const engineOptions = {
|
|
8
65
|
chatResponse: options.chat,
|
|
9
66
|
adapterContext: options.adapterContext,
|
|
@@ -13,7 +70,11 @@ export async function runServerToolOrchestration(options) {
|
|
|
13
70
|
providerInvoker: options.providerInvoker,
|
|
14
71
|
reenterPipeline: options.reenterPipeline
|
|
15
72
|
};
|
|
16
|
-
const engineResult = await runServerSideToolEngine(engineOptions)
|
|
73
|
+
const engineResult = await withTimeout(runServerSideToolEngine(engineOptions), serverToolTimeoutMs, () => createServerToolTimeoutError({
|
|
74
|
+
requestId: options.requestId,
|
|
75
|
+
phase: 'engine',
|
|
76
|
+
timeoutMs: serverToolTimeoutMs
|
|
77
|
+
}));
|
|
17
78
|
if (engineResult.mode === 'passthrough' || !engineResult.execution) {
|
|
18
79
|
return {
|
|
19
80
|
chat: engineResult.finalChatResponse,
|
|
@@ -70,12 +131,19 @@ export async function runServerToolOrchestration(options) {
|
|
|
70
131
|
if (isStopMessageFlow) {
|
|
71
132
|
reservation = reserveStopMessageUsage(options.adapterContext);
|
|
72
133
|
}
|
|
73
|
-
followup = await options.reenterPipeline({
|
|
134
|
+
followup = await withTimeout(options.reenterPipeline({
|
|
74
135
|
entryEndpoint: followupEntryEndpoint,
|
|
75
136
|
requestId: followupRequestId,
|
|
76
137
|
body: engineResult.execution.followup.payload,
|
|
77
138
|
metadata
|
|
78
|
-
})
|
|
139
|
+
}), followupTimeoutMs, () => createServerToolTimeoutError({
|
|
140
|
+
requestId: options.requestId,
|
|
141
|
+
phase: 'followup',
|
|
142
|
+
timeoutMs: followupTimeoutMs,
|
|
143
|
+
flowId: engineResult.execution.flowId,
|
|
144
|
+
attempt,
|
|
145
|
+
maxAttempts
|
|
146
|
+
}));
|
|
79
147
|
lastError = undefined;
|
|
80
148
|
break;
|
|
81
149
|
}
|
|
@@ -84,6 +152,9 @@ export async function runServerToolOrchestration(options) {
|
|
|
84
152
|
rollbackStopMessageUsage(reservation);
|
|
85
153
|
reservation = null;
|
|
86
154
|
}
|
|
155
|
+
if (isServerToolTimeoutError(error)) {
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
87
158
|
lastError = error;
|
|
88
159
|
if (attempt >= maxAttempts) {
|
|
89
160
|
const wrapped = new ProviderProtocolError(`[servertool] Followup failed for flow ${engineResult.execution.flowId ?? 'unknown'} ` +
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getServerToolHandler, listAutoServerToolHandlers } from './registry.js';
|
|
2
|
+
import { ProviderProtocolError } from '../conversion/shared/errors.js';
|
|
2
3
|
import './handlers/web-search.js';
|
|
3
4
|
import './handlers/vision.js';
|
|
4
5
|
import './handlers/iflow-model-error-retry.js';
|
|
@@ -49,14 +50,24 @@ async function runHandler(handler, ctx) {
|
|
|
49
50
|
return await handler(ctx);
|
|
50
51
|
}
|
|
51
52
|
catch (error) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
const toolName = ctx && ctx.toolCall && typeof ctx.toolCall.name === 'string' && ctx.toolCall.name.trim()
|
|
54
|
+
? ctx.toolCall.name.trim()
|
|
55
|
+
: 'auto';
|
|
56
|
+
const message = error instanceof Error ? error.message : String(error ?? 'unknown');
|
|
57
|
+
const wrapped = new ProviderProtocolError(`[servertool] handler failed: ${toolName}: ${message}`, {
|
|
58
|
+
code: 'SERVERTOOL_HANDLER_FAILED',
|
|
59
|
+
category: 'INTERNAL_ERROR',
|
|
60
|
+
details: {
|
|
61
|
+
toolName,
|
|
62
|
+
requestId: ctx.options?.requestId,
|
|
63
|
+
entryEndpoint: ctx.options?.entryEndpoint,
|
|
64
|
+
providerProtocol: ctx.options?.providerProtocol,
|
|
65
|
+
error: message
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
wrapped.status = 500;
|
|
69
|
+
wrapped.cause = error;
|
|
70
|
+
throw wrapped;
|
|
60
71
|
}
|
|
61
72
|
}
|
|
62
73
|
export function extractToolCalls(chatResponse) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface RegressionSample {
|
|
2
|
+
id: string;
|
|
3
|
+
timestamp: string;
|
|
4
|
+
errorType: string;
|
|
5
|
+
originalArgs: string;
|
|
6
|
+
normalizedArgs?: string;
|
|
7
|
+
fixerResult?: string;
|
|
8
|
+
validationError?: string;
|
|
9
|
+
source?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function captureApplyPatchRegression(sample: Omit<RegressionSample, 'id' | 'timestamp'>): void;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import { createHash } from 'crypto';
|
|
5
|
+
/**
|
|
6
|
+
* Captures apply_patch payload regressions into a golden samples structure.
|
|
7
|
+
*
|
|
8
|
+
* Trigger: apply_patch validation failure OR fixer failure
|
|
9
|
+
* Condition: non-context errors only (format/json/prefix/etc.)
|
|
10
|
+
*
|
|
11
|
+
* Default destination:
|
|
12
|
+
* ~/.routecodex/golden_samples/ci-regression/apply_patch/<error-type>/
|
|
13
|
+
*
|
|
14
|
+
* Optional repo destination (explicit opt-in):
|
|
15
|
+
* ROUTECODEX_APPLY_PATCH_REGRESSION_TO_REPO=1
|
|
16
|
+
* → <repoRoot>/samples/ci-goldens/_regressions/apply_patch/<error-type>/
|
|
17
|
+
*
|
|
18
|
+
* Explicit override:
|
|
19
|
+
* ROUTECODEX_APPLY_PATCH_REGRESSION_DIR=/abs/path
|
|
20
|
+
*/
|
|
21
|
+
const MAX_SAMPLES_PER_TYPE = 50;
|
|
22
|
+
function detectRepoRootFromCwd() {
|
|
23
|
+
try {
|
|
24
|
+
let dir = process.cwd();
|
|
25
|
+
for (let i = 0; i < 12; i += 1) {
|
|
26
|
+
const marker = path.join(dir, 'samples', 'ci-goldens');
|
|
27
|
+
const pkg = path.join(dir, 'package.json');
|
|
28
|
+
if (fs.existsSync(marker) && fs.existsSync(pkg)) {
|
|
29
|
+
try {
|
|
30
|
+
const raw = fs.readFileSync(pkg, 'utf-8');
|
|
31
|
+
const json = JSON.parse(raw);
|
|
32
|
+
if (json && typeof json === 'object' && String(json.name || '') === 'routecodex') {
|
|
33
|
+
return dir;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// ignore parse errors
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const parent = path.dirname(dir);
|
|
41
|
+
if (parent === dir)
|
|
42
|
+
break;
|
|
43
|
+
dir = parent;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// ignore
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
function resolveSamplesRoot() {
|
|
52
|
+
const explicit = String(process?.env?.ROUTECODEX_APPLY_PATCH_REGRESSION_DIR || '').trim();
|
|
53
|
+
if (explicit) {
|
|
54
|
+
return path.resolve(explicit);
|
|
55
|
+
}
|
|
56
|
+
const toRepo = String(process?.env?.ROUTECODEX_APPLY_PATCH_REGRESSION_TO_REPO || '').trim() === '1';
|
|
57
|
+
if (toRepo) {
|
|
58
|
+
const repoRoot = detectRepoRootFromCwd();
|
|
59
|
+
if (repoRoot) {
|
|
60
|
+
return path.join(repoRoot, 'samples', 'ci-goldens', '_regressions', 'apply_patch');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return path.join(os.homedir(), '.routecodex', 'golden_samples', 'ci-regression', 'apply_patch');
|
|
64
|
+
}
|
|
65
|
+
function isContextError(error) {
|
|
66
|
+
if (!error)
|
|
67
|
+
return false;
|
|
68
|
+
const lower = error.toLowerCase();
|
|
69
|
+
return (lower.includes('failed to find context') ||
|
|
70
|
+
lower.includes('failed to find expected lines') ||
|
|
71
|
+
lower.includes('file not found') ||
|
|
72
|
+
lower.includes('no such file'));
|
|
73
|
+
}
|
|
74
|
+
function stableSampleId(sample) {
|
|
75
|
+
const key = `${String(sample.errorType || 'unknown')}:${String(sample.originalArgs ?? '')}:` +
|
|
76
|
+
`${String(sample.normalizedArgs ?? '')}:${String(sample.fixerResult ?? '')}:${String(sample.validationError ?? '')}`;
|
|
77
|
+
return createHash('sha1').update(key).digest('hex').slice(0, 16);
|
|
78
|
+
}
|
|
79
|
+
export function captureApplyPatchRegression(sample) {
|
|
80
|
+
try {
|
|
81
|
+
if (isContextError(sample.validationError))
|
|
82
|
+
return;
|
|
83
|
+
const root = resolveSamplesRoot();
|
|
84
|
+
const safeType = String(sample.errorType || 'unknown').replace(/[^a-z0-9-]/gi, '_');
|
|
85
|
+
const typeDir = path.join(root, safeType);
|
|
86
|
+
if (!fs.existsSync(typeDir)) {
|
|
87
|
+
fs.mkdirSync(typeDir, { recursive: true });
|
|
88
|
+
}
|
|
89
|
+
// Check limit (best-effort; keep runtime safe)
|
|
90
|
+
try {
|
|
91
|
+
const existing = fs.readdirSync(typeDir).filter((f) => f.endsWith('.json'));
|
|
92
|
+
if (existing.length >= MAX_SAMPLES_PER_TYPE)
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// ignore
|
|
97
|
+
}
|
|
98
|
+
const id = `sample_${stableSampleId(sample)}`;
|
|
99
|
+
const file = path.join(typeDir, `${id}.json`);
|
|
100
|
+
if (fs.existsSync(file))
|
|
101
|
+
return;
|
|
102
|
+
const fullSample = {
|
|
103
|
+
id,
|
|
104
|
+
timestamp: new Date().toISOString(),
|
|
105
|
+
...sample
|
|
106
|
+
};
|
|
107
|
+
fs.writeFileSync(file, JSON.stringify(fullSample, null, 2), 'utf-8');
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Silently fail to avoid disrupting runtime
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type StructuredApplyPatchKind = 'insert_after' | 'insert_before' | 'replace' | 'delete' | 'create_file' | 'delete_file';
|
|
2
|
+
export interface StructuredApplyPatchChange {
|
|
3
|
+
file?: string;
|
|
4
|
+
kind: StructuredApplyPatchKind | string;
|
|
5
|
+
anchor?: string;
|
|
6
|
+
target?: string;
|
|
7
|
+
lines?: string[] | string;
|
|
8
|
+
use_anchor_indent?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface StructuredApplyPatchPayload extends Record<string, unknown> {
|
|
11
|
+
instructions?: string;
|
|
12
|
+
file?: string;
|
|
13
|
+
changes: StructuredApplyPatchChange[];
|
|
14
|
+
}
|
|
15
|
+
export declare class StructuredApplyPatchError extends Error {
|
|
16
|
+
reason: string;
|
|
17
|
+
constructor(reason: string, message: string);
|
|
18
|
+
}
|
|
19
|
+
export declare function buildStructuredPatch(payload: StructuredApplyPatchPayload): string;
|
|
20
|
+
export declare function isStructuredApplyPatchPayload(candidate: unknown): candidate is StructuredApplyPatchPayload;
|