@jsonstudio/rcc 0.90.872 → 0.90.876
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/build-info.js +2 -2
- package/dist/providers/core/runtime/standard-tool-text-harvest.js +10 -2
- package/dist/providers/core/runtime/standard-tool-text-harvest.js.map +1 -1
- package/dist/providers/core/runtime/standard-tool-text-request-transform.js +10 -2
- package/dist/providers/core/runtime/standard-tool-text-request-transform.js.map +1 -1
- package/dist/utils/snapshot-writer.js +162 -2
- package/dist/utils/snapshot-writer.js.map +1 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/node-support.js +5 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/snapshot-utils.js +156 -12
- package/node_modules/@jsonstudio/llms/dist/native/router_hotpath_napi.node +0 -0
- package/package.json +1 -1
- package/scripts/monitor/memory-guard.mjs +207 -0
package/dist/build-info.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { importCoreDist } from '../../../modules/llmswitch/bridge/module-loader.js';
|
|
2
|
+
const toolGovernorModule = await importCoreDist('conversion/shared/tool-governor');
|
|
3
|
+
const processChatResponseToolsFn = (() => {
|
|
4
|
+
const fn = toolGovernorModule?.processChatResponseTools;
|
|
5
|
+
if (typeof fn !== 'function') {
|
|
6
|
+
throw new Error('[standard-tool-text-harvest] processChatResponseTools not available');
|
|
7
|
+
}
|
|
8
|
+
return fn;
|
|
9
|
+
})();
|
|
2
10
|
function isRecord(value) {
|
|
3
11
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
4
12
|
}
|
|
@@ -10,7 +18,7 @@ function isRecord(value) {
|
|
|
10
18
|
* - Keep this wrapper neutral so providers share one skeleton.
|
|
11
19
|
*/
|
|
12
20
|
export function applyStandardToolTextHarvestToChatPayload(payload) {
|
|
13
|
-
const harvested =
|
|
21
|
+
const harvested = processChatResponseToolsFn(payload);
|
|
14
22
|
return (isRecord(harvested) ? harvested : payload);
|
|
15
23
|
}
|
|
16
24
|
//# sourceMappingURL=standard-tool-text-harvest.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standard-tool-text-harvest.js","sourceRoot":"","sources":["../../../../src/providers/core/runtime/standard-tool-text-harvest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"standard-tool-text-harvest.js","sourceRoot":"","sources":["../../../../src/providers/core/runtime/standard-tool-text-harvest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AAMpF,MAAM,kBAAkB,GAAG,MAAM,cAAc,CAAqB,iCAAiC,CAAC,CAAC;AACvG,MAAM,0BAA0B,GAAgE,CAAC,GAAG,EAAE;IACpG,MAAM,EAAE,GAAG,kBAAkB,EAAE,wBAAwB,CAAC;IACxD,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC,CAAC,EAAE,CAAC;AAEL,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yCAAyC,CAAoC,OAAU;IACrG,MAAM,SAAS,GAAG,0BAA0B,CAAC,OAAc,CAAC,CAAC;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAM,CAAC;AAC1D,CAAC"}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { importCoreDist } from '../../../modules/llmswitch/bridge/module-loader.js';
|
|
2
|
+
const qwenChatWebRequestTransformModule = await importCoreDist('conversion/compat/actions/qwenchat-web-request');
|
|
3
|
+
const qwenChatWebRequestTransformFn = (() => {
|
|
4
|
+
const fn = qwenChatWebRequestTransformModule?.applyQwenChatWebRequestTransform;
|
|
5
|
+
if (typeof fn !== 'function') {
|
|
6
|
+
throw new Error('[standard-tool-text-request-transform] applyQwenChatWebRequestTransform not available');
|
|
7
|
+
}
|
|
8
|
+
return fn;
|
|
9
|
+
})();
|
|
2
10
|
const TOOL_REGISTRY_FAILURE_RE = /\bTool\s+[A-Za-z0-9_.:/-]+\s+does\s+not\s+exists\b/i;
|
|
3
11
|
const TOOL_REGISTRY_FAILURE_GLOBAL_RE = /\bTool\s+[A-Za-z0-9_.:/-]+\s+does\s+not\s+exists\b/gi;
|
|
4
12
|
const TOOL_INFRA_FAILURE_RE = /(工具(?:基础设施|执行链路|执行层).{0,24}(?:不可用|无响应|异常)|当前\s*(?:session|会话).{0,24}工具.{0,24}(?:不可用|无响应|异常)|tool\s+(?:execution\s+layer|infrastructure).{0,24}(?:unavailable|no\s+response|broken|failed))/i;
|
|
@@ -114,7 +122,7 @@ function sanitizeToolTextTransformPayload(payload) {
|
|
|
114
122
|
}
|
|
115
123
|
export const standardToolTextRequestTransformRuntime = {
|
|
116
124
|
transform(payload, adapterContext) {
|
|
117
|
-
return
|
|
125
|
+
return qwenChatWebRequestTransformFn(payload, adapterContext);
|
|
118
126
|
}
|
|
119
127
|
};
|
|
120
128
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standard-tool-text-request-transform.js","sourceRoot":"","sources":["../../../../src/providers/core/runtime/standard-tool-text-request-transform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gCAAgC,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"standard-tool-text-request-transform.js","sourceRoot":"","sources":["../../../../src/providers/core/runtime/standard-tool-text-request-transform.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,133 @@ import path from 'path';
|
|
|
4
4
|
import { resolveRccSnapshotsDirFromEnv } from '../config/user-data-paths.js';
|
|
5
5
|
import { runtimeFlags } from '../runtime/runtime-flags.js';
|
|
6
6
|
let snapshotHookWriterPromise = null;
|
|
7
|
+
const DEFAULT_SERVER_SNAPSHOT_PAYLOAD_MAX_BYTES = 256 * 1024;
|
|
8
|
+
const DEFAULT_SERVER_SNAPSHOT_KEEP_RECENT_FILES = 10;
|
|
9
|
+
function resolveServerSnapshotPayloadMaxBytes() {
|
|
10
|
+
const raw = process.env.ROUTECODEX_SNAPSHOT_PAYLOAD_MAX_BYTES
|
|
11
|
+
?? process.env.RCC_SNAPSHOT_PAYLOAD_MAX_BYTES
|
|
12
|
+
?? '';
|
|
13
|
+
const parsed = Number.parseInt(String(raw).trim(), 10);
|
|
14
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
15
|
+
return parsed;
|
|
16
|
+
}
|
|
17
|
+
return DEFAULT_SERVER_SNAPSHOT_PAYLOAD_MAX_BYTES;
|
|
18
|
+
}
|
|
19
|
+
function resolveServerSnapshotKeepRecentFiles() {
|
|
20
|
+
const raw = process.env.ROUTECODEX_SNAPSHOT_KEEP_RECENT_FILES
|
|
21
|
+
?? process.env.RCC_SNAPSHOT_KEEP_RECENT_FILES
|
|
22
|
+
?? '';
|
|
23
|
+
const parsed = Number.parseInt(String(raw).trim(), 10);
|
|
24
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
27
|
+
return DEFAULT_SERVER_SNAPSHOT_KEEP_RECENT_FILES;
|
|
28
|
+
}
|
|
29
|
+
function estimateSnapshotPayloadBytes(value, options) {
|
|
30
|
+
const maxBytes = options?.maxBytes ?? Number.POSITIVE_INFINITY;
|
|
31
|
+
const depth = options?.depth ?? 0;
|
|
32
|
+
const seen = options?.seen ?? new Set();
|
|
33
|
+
if (value === null || value === undefined) {
|
|
34
|
+
return 4;
|
|
35
|
+
}
|
|
36
|
+
const valueType = typeof value;
|
|
37
|
+
if (valueType === 'string') {
|
|
38
|
+
return Math.min(maxBytes + 1, value.length * 2 + 2);
|
|
39
|
+
}
|
|
40
|
+
if (valueType === 'number') {
|
|
41
|
+
return 8;
|
|
42
|
+
}
|
|
43
|
+
if (valueType === 'boolean') {
|
|
44
|
+
return 4;
|
|
45
|
+
}
|
|
46
|
+
if (valueType === 'bigint') {
|
|
47
|
+
return String(value).length + 8;
|
|
48
|
+
}
|
|
49
|
+
if (valueType === 'symbol' || valueType === 'function') {
|
|
50
|
+
return 16;
|
|
51
|
+
}
|
|
52
|
+
if (seen.has(value)) {
|
|
53
|
+
return 8;
|
|
54
|
+
}
|
|
55
|
+
seen.add(value);
|
|
56
|
+
if (depth >= 8) {
|
|
57
|
+
return 64;
|
|
58
|
+
}
|
|
59
|
+
let bytes = 0;
|
|
60
|
+
if (Array.isArray(value)) {
|
|
61
|
+
bytes += 2;
|
|
62
|
+
for (const item of value) {
|
|
63
|
+
bytes += estimateSnapshotPayloadBytes(item, {
|
|
64
|
+
maxBytes: Math.max(0, maxBytes - bytes),
|
|
65
|
+
depth: depth + 1,
|
|
66
|
+
seen
|
|
67
|
+
});
|
|
68
|
+
if (bytes > maxBytes) {
|
|
69
|
+
return maxBytes + 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return bytes;
|
|
73
|
+
}
|
|
74
|
+
if (value && typeof value === 'object') {
|
|
75
|
+
bytes += 2;
|
|
76
|
+
for (const [key, child] of Object.entries(value)) {
|
|
77
|
+
bytes += key.length * 2 + 4;
|
|
78
|
+
bytes += estimateSnapshotPayloadBytes(child, {
|
|
79
|
+
maxBytes: Math.max(0, maxBytes - bytes),
|
|
80
|
+
depth: depth + 1,
|
|
81
|
+
seen
|
|
82
|
+
});
|
|
83
|
+
if (bytes > maxBytes) {
|
|
84
|
+
return maxBytes + 1;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return bytes;
|
|
88
|
+
}
|
|
89
|
+
return 16;
|
|
90
|
+
}
|
|
91
|
+
function summarizeSnapshotPayload(value) {
|
|
92
|
+
if (Array.isArray(value)) {
|
|
93
|
+
return {
|
|
94
|
+
type: 'array',
|
|
95
|
+
length: value.length,
|
|
96
|
+
sampleTypes: value.slice(0, 8).map((item) => typeof item)
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (value && typeof value === 'object') {
|
|
100
|
+
const record = value;
|
|
101
|
+
const keys = Object.keys(record);
|
|
102
|
+
return {
|
|
103
|
+
type: 'object',
|
|
104
|
+
keyCount: keys.length,
|
|
105
|
+
keys: keys.slice(0, 24)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (typeof value === 'string') {
|
|
109
|
+
return {
|
|
110
|
+
type: 'string',
|
|
111
|
+
length: value.length,
|
|
112
|
+
preview: value.length > 160 ? `${value.slice(0, 160)}…` : value
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
type: typeof value,
|
|
117
|
+
value: value ?? null
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function coerceServerSnapshotData(stage, data) {
|
|
121
|
+
const maxBytes = resolveServerSnapshotPayloadMaxBytes();
|
|
122
|
+
const estimatedBytes = estimateSnapshotPayloadBytes(data, { maxBytes: maxBytes + 1 });
|
|
123
|
+
if (estimatedBytes <= maxBytes) {
|
|
124
|
+
return data;
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
__snapshot_truncated: true,
|
|
128
|
+
stage,
|
|
129
|
+
maxBytes,
|
|
130
|
+
estimatedBytes,
|
|
131
|
+
summary: summarizeSnapshotPayload(data)
|
|
132
|
+
};
|
|
133
|
+
}
|
|
7
134
|
function logServerSnapshotNonBlockingError(operation, error) {
|
|
8
135
|
const reason = error instanceof Error ? error.message : String(error);
|
|
9
136
|
console.warn(`[server-snapshot] ${operation} failed (non-blocking): ${reason}`);
|
|
@@ -69,6 +196,37 @@ async function writeUniqueFile(dir, baseName, contents) {
|
|
|
69
196
|
const fallback = `${stem}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}${ext}`;
|
|
70
197
|
await fsp.writeFile(path.join(dir, fallback), contents, 'utf-8');
|
|
71
198
|
}
|
|
199
|
+
async function pruneSnapshotFilesKeepRecent(dir, maxFiles) {
|
|
200
|
+
if (!Number.isFinite(maxFiles) || maxFiles <= 0) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const entries = await fsp.readdir(dir, { withFileTypes: true });
|
|
204
|
+
const candidates = entries
|
|
205
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith('.json') && entry.name !== '__runtime.json')
|
|
206
|
+
.map((entry) => entry.name);
|
|
207
|
+
if (candidates.length <= maxFiles) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const filesWithMtime = await Promise.all(candidates.map(async (name) => {
|
|
211
|
+
const fullPath = path.join(dir, name);
|
|
212
|
+
const stat = await fsp.stat(fullPath);
|
|
213
|
+
return {
|
|
214
|
+
name,
|
|
215
|
+
fullPath,
|
|
216
|
+
mtimeMs: Number.isFinite(stat.mtimeMs) ? stat.mtimeMs : 0
|
|
217
|
+
};
|
|
218
|
+
}));
|
|
219
|
+
filesWithMtime.sort((a, b) => b.mtimeMs - a.mtimeMs || a.name.localeCompare(b.name));
|
|
220
|
+
const stale = filesWithMtime.slice(maxFiles);
|
|
221
|
+
await Promise.all(stale.map(async (file) => {
|
|
222
|
+
try {
|
|
223
|
+
await fsp.unlink(file.fullPath);
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// best-effort prune, never block request path
|
|
227
|
+
}
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
72
230
|
async function loadSnapshotHookWriter() {
|
|
73
231
|
if (!snapshotHookWriterPromise) {
|
|
74
232
|
snapshotHookWriterPromise = import('../modules/llmswitch/bridge.js')
|
|
@@ -87,6 +245,7 @@ export async function writeServerSnapshot(options) {
|
|
|
87
245
|
const endpoint = options.entryEndpoint || '/v1/chat/completions';
|
|
88
246
|
const groupRequestId = options.groupRequestId || options.requestId;
|
|
89
247
|
const providerKey = options.providerKey;
|
|
248
|
+
const data = coerceServerSnapshotData(String(options.phase), options.data);
|
|
90
249
|
// 1) 尝试通过 llmswitch-core hooks 写快照(供核心调试使用)
|
|
91
250
|
try {
|
|
92
251
|
const writeSnapshotViaHooks = await loadSnapshotHookWriter();
|
|
@@ -96,7 +255,7 @@ export async function writeServerSnapshot(options) {
|
|
|
96
255
|
requestId: options.requestId,
|
|
97
256
|
groupRequestId,
|
|
98
257
|
providerKey,
|
|
99
|
-
data
|
|
258
|
+
data,
|
|
100
259
|
verbosity: 'verbose'
|
|
101
260
|
});
|
|
102
261
|
}
|
|
@@ -122,9 +281,10 @@ export async function writeServerSnapshot(options) {
|
|
|
122
281
|
version: String(process.env.ROUTECODEX_VERSION || 'dev'),
|
|
123
282
|
buildTime: String(process.env.ROUTECODEX_BUILD_TIME || new Date().toISOString())
|
|
124
283
|
},
|
|
125
|
-
data
|
|
284
|
+
data
|
|
126
285
|
};
|
|
127
286
|
await writeUniqueFile(dir, file, JSON.stringify(payload, null, 2));
|
|
287
|
+
await pruneSnapshotFilesKeepRecent(dir, resolveServerSnapshotKeepRecentFiles());
|
|
128
288
|
}
|
|
129
289
|
catch (error) {
|
|
130
290
|
logServerSnapshotNonBlockingError(`writeLocalSnapshot:${options.phase}`, error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot-writer.js","sourceRoot":"","sources":["../../src/utils/snapshot-writer.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"snapshot-writer.js","sourceRoot":"","sources":["../../src/utils/snapshot-writer.ts"],"names":[],"mappings":"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"}
|
|
@@ -53,8 +53,11 @@ export async function runHubOutboundConversion(options) {
|
|
|
53
53
|
return payload;
|
|
54
54
|
}
|
|
55
55
|
function maybeCreateStageRecorder(adapterContext, defaultEndpoint) {
|
|
56
|
-
const flag = process.env.ROUTECODEX_HUB_SNAPSHOTS
|
|
57
|
-
|
|
56
|
+
const flag = String(process.env.ROUTECODEX_HUB_SNAPSHOTS ?? process.env.RCC_HUB_SNAPSHOTS ?? '')
|
|
57
|
+
.trim()
|
|
58
|
+
.toLowerCase();
|
|
59
|
+
const enabled = flag === '1' || flag === 'true' || flag === 'yes' || flag === 'on';
|
|
60
|
+
if (!enabled) {
|
|
58
61
|
return undefined;
|
|
59
62
|
}
|
|
60
63
|
const endpoint = defaultEndpoint || adapterContext.entryEndpoint || '/v1/chat/completions';
|
|
@@ -18,22 +18,156 @@ export async function recordSnapshot(options) {
|
|
|
18
18
|
if (!shouldRecordSnapshots())
|
|
19
19
|
return;
|
|
20
20
|
const endpoint = options.endpoint || '/v1/chat/completions';
|
|
21
|
+
const prepared = coerceSnapshotPayloadForWrite(options.stage, options.data);
|
|
21
22
|
void writeSnapshotViaHooks({
|
|
22
23
|
endpoint,
|
|
23
24
|
stage: options.stage,
|
|
24
25
|
requestId: options.requestId,
|
|
25
26
|
providerKey: options.providerKey,
|
|
26
27
|
groupRequestId: options.groupRequestId,
|
|
27
|
-
data:
|
|
28
|
+
data: prepared.data,
|
|
28
29
|
verbosity: 'verbose'
|
|
29
30
|
}).catch(() => {
|
|
30
31
|
// ignore hook errors
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
|
-
const
|
|
34
|
+
const DEFAULT_SNAPSHOT_QUEUE_MAX_ITEMS = 10;
|
|
34
35
|
const SNAPSHOT_QUEUE_BATCH_SIZE = 64;
|
|
36
|
+
const DEFAULT_SNAPSHOT_PAYLOAD_MAX_BYTES = 256 * 1024;
|
|
37
|
+
const DEFAULT_SNAPSHOT_QUEUE_MEMORY_BUDGET_BYTES = 8 * 1024 * 1024;
|
|
35
38
|
const SNAPSHOT_QUEUE = [];
|
|
39
|
+
let snapshotQueueBytes = 0;
|
|
36
40
|
let snapshotQueueDrainScheduled = false;
|
|
41
|
+
function resolvePositiveIntegerFromEnv(names, fallback) {
|
|
42
|
+
for (const name of names) {
|
|
43
|
+
const raw = process.env[name];
|
|
44
|
+
const parsed = Number.parseInt(String(raw ?? '').trim(), 10);
|
|
45
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
46
|
+
return parsed;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return fallback;
|
|
50
|
+
}
|
|
51
|
+
function resolveSnapshotPayloadMaxBytes() {
|
|
52
|
+
return resolvePositiveIntegerFromEnv(['ROUTECODEX_SNAPSHOT_PAYLOAD_MAX_BYTES', 'RCC_SNAPSHOT_PAYLOAD_MAX_BYTES'], DEFAULT_SNAPSHOT_PAYLOAD_MAX_BYTES);
|
|
53
|
+
}
|
|
54
|
+
function resolveSnapshotQueueMemoryBudgetBytes() {
|
|
55
|
+
return resolvePositiveIntegerFromEnv(['ROUTECODEX_SNAPSHOT_QUEUE_MEMORY_BUDGET_BYTES', 'RCC_SNAPSHOT_QUEUE_MEMORY_BUDGET_BYTES'], DEFAULT_SNAPSHOT_QUEUE_MEMORY_BUDGET_BYTES);
|
|
56
|
+
}
|
|
57
|
+
function resolveSnapshotQueueMaxItems() {
|
|
58
|
+
return resolvePositiveIntegerFromEnv(['ROUTECODEX_SNAPSHOT_QUEUE_MAX_ITEMS', 'RCC_SNAPSHOT_QUEUE_MAX_ITEMS'], DEFAULT_SNAPSHOT_QUEUE_MAX_ITEMS);
|
|
59
|
+
}
|
|
60
|
+
function estimateSnapshotPayloadBytes(value, options) {
|
|
61
|
+
const maxBytes = options?.maxBytes ?? Number.POSITIVE_INFINITY;
|
|
62
|
+
const depth = options?.depth ?? 0;
|
|
63
|
+
const seen = options?.seen ?? new Set();
|
|
64
|
+
if (value === null || value === undefined) {
|
|
65
|
+
return 4;
|
|
66
|
+
}
|
|
67
|
+
const valueType = typeof value;
|
|
68
|
+
if (valueType === 'string') {
|
|
69
|
+
return Math.min(maxBytes + 1, value.length * 2 + 2);
|
|
70
|
+
}
|
|
71
|
+
if (valueType === 'number') {
|
|
72
|
+
return 8;
|
|
73
|
+
}
|
|
74
|
+
if (valueType === 'boolean') {
|
|
75
|
+
return 4;
|
|
76
|
+
}
|
|
77
|
+
if (valueType === 'bigint') {
|
|
78
|
+
return String(value).length + 8;
|
|
79
|
+
}
|
|
80
|
+
if (valueType === 'symbol' || valueType === 'function') {
|
|
81
|
+
return 16;
|
|
82
|
+
}
|
|
83
|
+
if (seen.has(value)) {
|
|
84
|
+
return 8;
|
|
85
|
+
}
|
|
86
|
+
seen.add(value);
|
|
87
|
+
if (depth >= 8) {
|
|
88
|
+
return 64;
|
|
89
|
+
}
|
|
90
|
+
let bytes = 0;
|
|
91
|
+
if (Array.isArray(value)) {
|
|
92
|
+
bytes += 2;
|
|
93
|
+
for (const item of value) {
|
|
94
|
+
bytes += estimateSnapshotPayloadBytes(item, {
|
|
95
|
+
maxBytes: Math.max(0, maxBytes - bytes),
|
|
96
|
+
depth: depth + 1,
|
|
97
|
+
seen
|
|
98
|
+
});
|
|
99
|
+
if (bytes > maxBytes) {
|
|
100
|
+
return maxBytes + 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return bytes;
|
|
104
|
+
}
|
|
105
|
+
if (value && typeof value === 'object') {
|
|
106
|
+
bytes += 2;
|
|
107
|
+
for (const [key, child] of Object.entries(value)) {
|
|
108
|
+
bytes += key.length * 2 + 4;
|
|
109
|
+
bytes += estimateSnapshotPayloadBytes(child, {
|
|
110
|
+
maxBytes: Math.max(0, maxBytes - bytes),
|
|
111
|
+
depth: depth + 1,
|
|
112
|
+
seen
|
|
113
|
+
});
|
|
114
|
+
if (bytes > maxBytes) {
|
|
115
|
+
return maxBytes + 1;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return bytes;
|
|
119
|
+
}
|
|
120
|
+
return 16;
|
|
121
|
+
}
|
|
122
|
+
function summarizeSnapshotPayload(value) {
|
|
123
|
+
if (Array.isArray(value)) {
|
|
124
|
+
return {
|
|
125
|
+
type: 'array',
|
|
126
|
+
length: value.length,
|
|
127
|
+
sampleTypes: value.slice(0, 8).map((item) => typeof item)
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (value && typeof value === 'object') {
|
|
131
|
+
const record = value;
|
|
132
|
+
const keys = Object.keys(record);
|
|
133
|
+
return {
|
|
134
|
+
type: 'object',
|
|
135
|
+
keyCount: keys.length,
|
|
136
|
+
keys: keys.slice(0, 24)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (typeof value === 'string') {
|
|
140
|
+
return {
|
|
141
|
+
type: 'string',
|
|
142
|
+
length: value.length,
|
|
143
|
+
preview: value.length > 160 ? `${value.slice(0, 160)}…` : value
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
type: typeof value,
|
|
148
|
+
value: value ?? null
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function coerceSnapshotPayloadForWrite(stage, payload) {
|
|
152
|
+
const maxBytes = resolveSnapshotPayloadMaxBytes();
|
|
153
|
+
const estimatedBytes = estimateSnapshotPayloadBytes(payload, { maxBytes: maxBytes + 1 });
|
|
154
|
+
if (estimatedBytes <= maxBytes) {
|
|
155
|
+
return {
|
|
156
|
+
data: payload,
|
|
157
|
+
sizeBytes: Math.max(1, estimatedBytes)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
data: {
|
|
162
|
+
__snapshot_truncated: true,
|
|
163
|
+
stage,
|
|
164
|
+
maxBytes,
|
|
165
|
+
estimatedBytes,
|
|
166
|
+
summary: summarizeSnapshotPayload(payload)
|
|
167
|
+
},
|
|
168
|
+
sizeBytes: 512
|
|
169
|
+
};
|
|
170
|
+
}
|
|
37
171
|
function scheduleSnapshotQueueDrain() {
|
|
38
172
|
if (snapshotQueueDrainScheduled) {
|
|
39
173
|
return;
|
|
@@ -43,12 +177,13 @@ function scheduleSnapshotQueueDrain() {
|
|
|
43
177
|
snapshotQueueDrainScheduled = false;
|
|
44
178
|
let processed = 0;
|
|
45
179
|
while (SNAPSHOT_QUEUE.length > 0 && processed < SNAPSHOT_QUEUE_BATCH_SIZE) {
|
|
46
|
-
const
|
|
47
|
-
if (!
|
|
180
|
+
const item = SNAPSHOT_QUEUE.shift();
|
|
181
|
+
if (!item) {
|
|
48
182
|
continue;
|
|
49
183
|
}
|
|
184
|
+
snapshotQueueBytes = Math.max(0, snapshotQueueBytes - Math.max(1, item.sizeBytes));
|
|
50
185
|
try {
|
|
51
|
-
task();
|
|
186
|
+
item.task();
|
|
52
187
|
}
|
|
53
188
|
catch {
|
|
54
189
|
// snapshot write failures are non-blocking by design
|
|
@@ -60,12 +195,20 @@ function scheduleSnapshotQueueDrain() {
|
|
|
60
195
|
}
|
|
61
196
|
});
|
|
62
197
|
}
|
|
63
|
-
function enqueueSnapshotTask(task) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
198
|
+
function enqueueSnapshotTask(task, sizeBytes) {
|
|
199
|
+
const normalizedSize = Math.max(1, Math.floor(sizeBytes));
|
|
200
|
+
const queueBudgetBytes = resolveSnapshotQueueMemoryBudgetBytes();
|
|
201
|
+
const queueMaxItems = resolveSnapshotQueueMaxItems();
|
|
202
|
+
while (SNAPSHOT_QUEUE.length > 0
|
|
203
|
+
&& (SNAPSHOT_QUEUE.length >= queueMaxItems || snapshotQueueBytes + normalizedSize > queueBudgetBytes)) {
|
|
204
|
+
const dropped = SNAPSHOT_QUEUE.shift();
|
|
205
|
+
if (!dropped) {
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
snapshotQueueBytes = Math.max(0, snapshotQueueBytes - Math.max(1, dropped.sizeBytes));
|
|
67
209
|
}
|
|
68
|
-
SNAPSHOT_QUEUE.push(task);
|
|
210
|
+
SNAPSHOT_QUEUE.push({ task, sizeBytes: normalizedSize });
|
|
211
|
+
snapshotQueueBytes += normalizedSize;
|
|
69
212
|
scheduleSnapshotQueueDrain();
|
|
70
213
|
}
|
|
71
214
|
export function createSnapshotWriter(opts) {
|
|
@@ -74,6 +217,7 @@ export function createSnapshotWriter(opts) {
|
|
|
74
217
|
}
|
|
75
218
|
const endpoint = opts.endpoint || '/v1/chat/completions';
|
|
76
219
|
return (stage, payload) => {
|
|
220
|
+
const prepared = coerceSnapshotPayloadForWrite(stage, payload);
|
|
77
221
|
enqueueSnapshotTask(() => {
|
|
78
222
|
void recordSnapshot({
|
|
79
223
|
stage,
|
|
@@ -82,8 +226,8 @@ export function createSnapshotWriter(opts) {
|
|
|
82
226
|
folderHint: opts.folderHint,
|
|
83
227
|
providerKey: opts.providerKey,
|
|
84
228
|
groupRequestId: opts.groupRequestId,
|
|
85
|
-
data:
|
|
229
|
+
data: prepared.data
|
|
86
230
|
});
|
|
87
|
-
});
|
|
231
|
+
}, prepared.sizeBytes);
|
|
88
232
|
};
|
|
89
233
|
}
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* RouteCodex memory guard (PID-scoped, no broad kill).
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node scripts/monitor/memory-guard.mjs --port 5555 --rss-mb 8192
|
|
7
|
+
* node scripts/monitor/memory-guard.mjs --port 5555 --rss-mb 4096 --action restart
|
|
8
|
+
* node scripts/monitor/memory-guard.mjs --port 5555 --rss-mb 2048 --once
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { execSync, spawnSync } from 'node:child_process';
|
|
12
|
+
|
|
13
|
+
function parseArgs(argv) {
|
|
14
|
+
const options = {
|
|
15
|
+
port: 5555,
|
|
16
|
+
rssMb: 8192,
|
|
17
|
+
intervalMs: 2000,
|
|
18
|
+
graceMs: 5000,
|
|
19
|
+
action: 'kill', // kill | restart
|
|
20
|
+
once: false
|
|
21
|
+
};
|
|
22
|
+
const args = argv.slice(2);
|
|
23
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
24
|
+
const arg = args[i];
|
|
25
|
+
if (arg === '--port' && i + 1 < args.length) {
|
|
26
|
+
options.port = Number.parseInt(String(args[++i]), 10);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (arg === '--rss-mb' && i + 1 < args.length) {
|
|
30
|
+
options.rssMb = Number.parseInt(String(args[++i]), 10);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (arg === '--interval-ms' && i + 1 < args.length) {
|
|
34
|
+
options.intervalMs = Number.parseInt(String(args[++i]), 10);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (arg === '--grace-ms' && i + 1 < args.length) {
|
|
38
|
+
options.graceMs = Number.parseInt(String(args[++i]), 10);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (arg === '--action' && i + 1 < args.length) {
|
|
42
|
+
const action = String(args[++i]).trim().toLowerCase();
|
|
43
|
+
options.action = action === 'restart' ? 'restart' : 'kill';
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (arg === '--once') {
|
|
47
|
+
options.once = true;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (!Number.isFinite(options.port) || options.port <= 0) {
|
|
52
|
+
throw new Error(`invalid --port: ${options.port}`);
|
|
53
|
+
}
|
|
54
|
+
if (!Number.isFinite(options.rssMb) || options.rssMb <= 0) {
|
|
55
|
+
throw new Error(`invalid --rss-mb: ${options.rssMb}`);
|
|
56
|
+
}
|
|
57
|
+
if (!Number.isFinite(options.intervalMs) || options.intervalMs < 200) {
|
|
58
|
+
options.intervalMs = 2000;
|
|
59
|
+
}
|
|
60
|
+
if (!Number.isFinite(options.graceMs) || options.graceMs < 0) {
|
|
61
|
+
options.graceMs = 5000;
|
|
62
|
+
}
|
|
63
|
+
return options;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function nowIso() {
|
|
67
|
+
return new Date().toISOString();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function log(line) {
|
|
71
|
+
console.log(`[${nowIso()}] [memory-guard] ${line}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function runStdout(command) {
|
|
75
|
+
try {
|
|
76
|
+
return String(execSync(command, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }) || '').trim();
|
|
77
|
+
} catch {
|
|
78
|
+
return '';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function resolveListenerPid(port) {
|
|
83
|
+
const out = runStdout(`lsof -t -nP -iTCP:${port} -sTCP:LISTEN | head -n 1`);
|
|
84
|
+
const pid = Number.parseInt(out, 10);
|
|
85
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function readPidCommand(pid) {
|
|
89
|
+
return runStdout(`ps -o command= -p ${pid}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function readPidRssMb(pid) {
|
|
93
|
+
const out = runStdout(`ps -o rss= -p ${pid}`);
|
|
94
|
+
const rssKb = Number.parseInt(out, 10);
|
|
95
|
+
if (!Number.isFinite(rssKb) || rssKb <= 0) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return Math.round((rssKb / 1024) * 10) / 10;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isPidAlive(pid) {
|
|
102
|
+
try {
|
|
103
|
+
process.kill(pid, 0);
|
|
104
|
+
return true;
|
|
105
|
+
} catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function sleep(ms) {
|
|
111
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function isLikelyRouteCodexProcess(commandText) {
|
|
115
|
+
const text = String(commandText || '').toLowerCase();
|
|
116
|
+
return (
|
|
117
|
+
text.includes('routecodex')
|
|
118
|
+
|| text.includes('dist/index.js')
|
|
119
|
+
|| (text.includes('node') && text.includes('rcc'))
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function terminatePidScoped(pid, graceMs) {
|
|
124
|
+
log(`threshold exceeded, terminate pid=${pid} with SIGTERM`);
|
|
125
|
+
try {
|
|
126
|
+
process.kill(pid, 'SIGTERM');
|
|
127
|
+
} catch (error) {
|
|
128
|
+
log(`SIGTERM failed for pid=${pid}: ${error instanceof Error ? error.message : String(error)}`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const deadline = Date.now() + graceMs;
|
|
132
|
+
while (Date.now() < deadline) {
|
|
133
|
+
if (!isPidAlive(pid)) {
|
|
134
|
+
log(`pid=${pid} exited after SIGTERM`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
await sleep(250);
|
|
138
|
+
}
|
|
139
|
+
if (isPidAlive(pid)) {
|
|
140
|
+
log(`pid=${pid} still alive after ${graceMs}ms, escalate SIGKILL`);
|
|
141
|
+
try {
|
|
142
|
+
process.kill(pid, 'SIGKILL');
|
|
143
|
+
} catch (error) {
|
|
144
|
+
log(`SIGKILL failed for pid=${pid}: ${error instanceof Error ? error.message : String(error)}`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function restartRouteCodex(port) {
|
|
151
|
+
log(`attempt restart on port=${port}`);
|
|
152
|
+
const result = spawnSync('routecodex', ['restart', '--port', String(port)], {
|
|
153
|
+
stdio: 'inherit'
|
|
154
|
+
});
|
|
155
|
+
if ((result.status ?? 1) !== 0) {
|
|
156
|
+
log(`restart failed with status=${result.status ?? -1}`);
|
|
157
|
+
} else {
|
|
158
|
+
log('restart completed');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function evaluateOnce(options) {
|
|
163
|
+
const pid = resolveListenerPid(options.port);
|
|
164
|
+
if (!pid) {
|
|
165
|
+
log(`no listener found on port=${options.port}`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const cmd = readPidCommand(pid);
|
|
169
|
+
const rssMb = readPidRssMb(pid);
|
|
170
|
+
if (!isLikelyRouteCodexProcess(cmd)) {
|
|
171
|
+
log(`skip pid=${pid}, command does not look like RouteCodex: ${cmd}`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (rssMb === null) {
|
|
175
|
+
log(`pid=${pid} rss unavailable`);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
log(`pid=${pid} rss=${rssMb}MB threshold=${options.rssMb}MB`);
|
|
179
|
+
if (rssMb < options.rssMb) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
await terminatePidScoped(pid, options.graceMs);
|
|
183
|
+
if (options.action === 'restart') {
|
|
184
|
+
restartRouteCodex(options.port);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function main() {
|
|
189
|
+
const options = parseArgs(process.argv);
|
|
190
|
+
log(
|
|
191
|
+
`start port=${options.port} rssMb=${options.rssMb} intervalMs=${options.intervalMs} action=${options.action} once=${options.once}`
|
|
192
|
+
);
|
|
193
|
+
if (options.once) {
|
|
194
|
+
await evaluateOnce(options);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
while (true) {
|
|
198
|
+
await evaluateOnce(options);
|
|
199
|
+
await sleep(options.intervalMs);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
main().catch((error) => {
|
|
204
|
+
log(`fatal: ${error instanceof Error ? error.message : String(error)}`);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
});
|
|
207
|
+
|