risicare 0.2.2 → 0.3.0
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/frameworks/instructor.cjs +45 -17
- package/dist/frameworks/instructor.cjs.map +1 -1
- package/dist/frameworks/instructor.js +47 -17
- package/dist/frameworks/instructor.js.map +1 -1
- package/dist/frameworks/langchain.cjs +73 -6
- package/dist/frameworks/langchain.cjs.map +1 -1
- package/dist/frameworks/langchain.d.cts +20 -4
- package/dist/frameworks/langchain.d.ts +20 -4
- package/dist/frameworks/langchain.js +75 -6
- package/dist/frameworks/langchain.js.map +1 -1
- package/dist/frameworks/langgraph.cjs +73 -6
- package/dist/frameworks/langgraph.cjs.map +1 -1
- package/dist/frameworks/langgraph.js +75 -6
- package/dist/frameworks/langgraph.js.map +1 -1
- package/dist/frameworks/llamaindex.cjs +41 -14
- package/dist/frameworks/llamaindex.cjs.map +1 -1
- package/dist/frameworks/llamaindex.js +43 -14
- package/dist/frameworks/llamaindex.js.map +1 -1
- package/dist/index.cjs +1494 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +436 -1
- package/dist/index.d.ts +436 -1
- package/dist/index.js +1515 -67
- package/dist/index.js.map +1 -1
- package/dist/providers/anthropic/index.cjs +74 -24
- package/dist/providers/anthropic/index.cjs.map +1 -1
- package/dist/providers/anthropic/index.js +76 -24
- package/dist/providers/anthropic/index.js.map +1 -1
- package/dist/providers/bedrock/index.cjs +81 -24
- package/dist/providers/bedrock/index.cjs.map +1 -1
- package/dist/providers/bedrock/index.js +83 -24
- package/dist/providers/bedrock/index.js.map +1 -1
- package/dist/providers/cerebras/index.cjs +78 -25
- package/dist/providers/cerebras/index.cjs.map +1 -1
- package/dist/providers/cerebras/index.js +80 -25
- package/dist/providers/cerebras/index.js.map +1 -1
- package/dist/providers/cohere/index.cjs +95 -25
- package/dist/providers/cohere/index.cjs.map +1 -1
- package/dist/providers/cohere/index.js +97 -25
- package/dist/providers/cohere/index.js.map +1 -1
- package/dist/providers/google/index.cjs +77 -25
- package/dist/providers/google/index.cjs.map +1 -1
- package/dist/providers/google/index.js +79 -25
- package/dist/providers/google/index.js.map +1 -1
- package/dist/providers/groq/index.cjs +80 -25
- package/dist/providers/groq/index.cjs.map +1 -1
- package/dist/providers/groq/index.js +82 -25
- package/dist/providers/groq/index.js.map +1 -1
- package/dist/providers/huggingface/index.cjs +80 -25
- package/dist/providers/huggingface/index.cjs.map +1 -1
- package/dist/providers/huggingface/index.js +82 -25
- package/dist/providers/huggingface/index.js.map +1 -1
- package/dist/providers/mistral/index.cjs +72 -24
- package/dist/providers/mistral/index.cjs.map +1 -1
- package/dist/providers/mistral/index.js +74 -24
- package/dist/providers/mistral/index.js.map +1 -1
- package/dist/providers/ollama/index.cjs +83 -25
- package/dist/providers/ollama/index.cjs.map +1 -1
- package/dist/providers/ollama/index.js +85 -25
- package/dist/providers/ollama/index.js.map +1 -1
- package/dist/providers/openai/index.cjs +1429 -28
- package/dist/providers/openai/index.cjs.map +1 -1
- package/dist/providers/openai/index.js +1447 -28
- package/dist/providers/openai/index.js.map +1 -1
- package/dist/providers/together/index.cjs +80 -25
- package/dist/providers/together/index.cjs.map +1 -1
- package/dist/providers/together/index.js +82 -25
- package/dist/providers/together/index.js.map +1 -1
- package/dist/providers/vercel-ai/index.cjs +45 -17
- package/dist/providers/vercel-ai/index.cjs.map +1 -1
- package/dist/providers/vercel-ai/index.js +47 -17
- package/dist/providers/vercel-ai/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,1419 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
6
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
7
|
+
}) : x)(function(x) {
|
|
8
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
9
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
10
|
+
});
|
|
11
|
+
var __esm = (fn, res) => function __init() {
|
|
12
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
13
|
+
};
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
|
|
28
|
+
// src/globals.ts
|
|
29
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
30
|
+
function getClient() {
|
|
31
|
+
return G[PREFIX + "client"];
|
|
32
|
+
}
|
|
33
|
+
function setClient(client) {
|
|
34
|
+
G[PREFIX + "client"] = client;
|
|
35
|
+
}
|
|
36
|
+
function getTracer() {
|
|
37
|
+
return G[PREFIX + "tracer"];
|
|
38
|
+
}
|
|
39
|
+
function setTracer(tracer) {
|
|
40
|
+
G[PREFIX + "tracer"] = tracer;
|
|
41
|
+
}
|
|
42
|
+
function getContextStorage() {
|
|
43
|
+
if (!G[PREFIX + "ctx"]) {
|
|
44
|
+
G[PREFIX + "ctx"] = new AsyncLocalStorage();
|
|
45
|
+
}
|
|
46
|
+
return G[PREFIX + "ctx"];
|
|
47
|
+
}
|
|
48
|
+
function getRegistry() {
|
|
49
|
+
if (!G[PREFIX + "registry"]) {
|
|
50
|
+
G[PREFIX + "registry"] = /* @__PURE__ */ new Map();
|
|
51
|
+
}
|
|
52
|
+
return G[PREFIX + "registry"];
|
|
53
|
+
}
|
|
54
|
+
function getOpCount() {
|
|
55
|
+
return G[PREFIX + "opcount"] ?? 0;
|
|
56
|
+
}
|
|
57
|
+
function setOpCount(n) {
|
|
58
|
+
G[PREFIX + "opcount"] = n;
|
|
59
|
+
}
|
|
60
|
+
function getDebug() {
|
|
61
|
+
return G[PREFIX + "debug"] ?? false;
|
|
62
|
+
}
|
|
63
|
+
function setDebugFlag(enabled) {
|
|
64
|
+
G[PREFIX + "debug"] = enabled;
|
|
65
|
+
}
|
|
66
|
+
var G, PREFIX;
|
|
67
|
+
var init_globals = __esm({
|
|
68
|
+
"src/globals.ts"() {
|
|
69
|
+
"use strict";
|
|
70
|
+
G = globalThis;
|
|
71
|
+
PREFIX = "__risicare_";
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// src/utils/log.ts
|
|
76
|
+
function setDebug(enabled) {
|
|
77
|
+
setDebugFlag(enabled);
|
|
78
|
+
}
|
|
79
|
+
function debug(msg) {
|
|
80
|
+
if (getDebug()) {
|
|
81
|
+
process.stderr.write(`[risicare] ${msg}
|
|
82
|
+
`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function warn(msg) {
|
|
86
|
+
process.stderr.write(`[risicare] WARNING: ${msg}
|
|
87
|
+
`);
|
|
88
|
+
}
|
|
89
|
+
var init_log = __esm({
|
|
90
|
+
"src/utils/log.ts"() {
|
|
91
|
+
"use strict";
|
|
92
|
+
init_globals();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// src/runtime/config.ts
|
|
97
|
+
function resolveFixRuntimeConfig(config) {
|
|
98
|
+
return {
|
|
99
|
+
apiEndpoint: config.apiEndpoint,
|
|
100
|
+
apiKey: config.apiKey,
|
|
101
|
+
enabled: config.enabled ?? true,
|
|
102
|
+
cacheEnabled: config.cacheEnabled ?? true,
|
|
103
|
+
cacheTtlMs: config.cacheTtlMs ?? 3e5,
|
|
104
|
+
cacheMaxEntries: config.cacheMaxEntries ?? 1e3,
|
|
105
|
+
autoRefresh: config.autoRefresh ?? true,
|
|
106
|
+
refreshIntervalMs: config.refreshIntervalMs ?? 6e4,
|
|
107
|
+
dryRun: config.dryRun ?? false,
|
|
108
|
+
abTestingEnabled: config.abTestingEnabled ?? true,
|
|
109
|
+
timeoutMs: config.timeoutMs ?? 1e3,
|
|
110
|
+
debug: config.debug ?? false
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function matchesError(fix, errorCode) {
|
|
114
|
+
if (fix.errorCode === errorCode) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
if (fix.errorCode.endsWith("*")) {
|
|
118
|
+
const prefix = fix.errorCode.slice(0, -1);
|
|
119
|
+
return errorCode.startsWith(prefix);
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
function shouldApply(fix, sessionHash) {
|
|
124
|
+
if (fix.trafficPercentage >= 100) return true;
|
|
125
|
+
if (fix.trafficPercentage <= 0) return false;
|
|
126
|
+
const bucket = sessionHash % 100;
|
|
127
|
+
return bucket < fix.trafficPercentage;
|
|
128
|
+
}
|
|
129
|
+
function activeFixFromApiResponse(item) {
|
|
130
|
+
return {
|
|
131
|
+
fixId: item.id ?? item.fix_id ?? item.fixId ?? "",
|
|
132
|
+
deploymentId: item.deployment_id ?? item.deploymentId ?? "",
|
|
133
|
+
errorCode: item.error_code ?? item.errorCode ?? "",
|
|
134
|
+
fixType: item.fix_type ?? item.fixType ?? "prompt",
|
|
135
|
+
config: item.config ?? {},
|
|
136
|
+
trafficPercentage: item.traffic_percentage ?? item.trafficPercentage ?? 100,
|
|
137
|
+
version: item.version ?? 1
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
var init_config = __esm({
|
|
141
|
+
"src/runtime/config.ts"() {
|
|
142
|
+
"use strict";
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// src/runtime/cache.ts
|
|
147
|
+
var FixCache;
|
|
148
|
+
var init_cache = __esm({
|
|
149
|
+
"src/runtime/cache.ts"() {
|
|
150
|
+
"use strict";
|
|
151
|
+
init_config();
|
|
152
|
+
init_log();
|
|
153
|
+
FixCache = class {
|
|
154
|
+
_ttlMs;
|
|
155
|
+
_maxEntries;
|
|
156
|
+
_enabled;
|
|
157
|
+
_cache;
|
|
158
|
+
_stats;
|
|
159
|
+
constructor(config) {
|
|
160
|
+
this._enabled = config?.cacheEnabled ?? true;
|
|
161
|
+
this._ttlMs = config?.cacheTtlMs ?? 3e5;
|
|
162
|
+
this._maxEntries = config?.cacheMaxEntries ?? 1e3;
|
|
163
|
+
this._cache = /* @__PURE__ */ new Map();
|
|
164
|
+
this._stats = {
|
|
165
|
+
hits: 0,
|
|
166
|
+
misses: 0,
|
|
167
|
+
evictions: 0,
|
|
168
|
+
size: 0,
|
|
169
|
+
lastRefresh: null
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/** Whether caching is enabled. */
|
|
173
|
+
get enabled() {
|
|
174
|
+
return this._enabled;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get a fix by error code.
|
|
178
|
+
*
|
|
179
|
+
* Checks exact match first, then scans for wildcard matches.
|
|
180
|
+
* Expired entries are evicted on access.
|
|
181
|
+
*/
|
|
182
|
+
get(errorCode) {
|
|
183
|
+
try {
|
|
184
|
+
if (!this._enabled) return null;
|
|
185
|
+
const now = Date.now();
|
|
186
|
+
const exactEntry = this._cache.get(errorCode);
|
|
187
|
+
if (exactEntry) {
|
|
188
|
+
if (exactEntry.expiresAt > now) {
|
|
189
|
+
this._stats.hits++;
|
|
190
|
+
return exactEntry.fix;
|
|
191
|
+
}
|
|
192
|
+
this._cache.delete(errorCode);
|
|
193
|
+
this._stats.evictions++;
|
|
194
|
+
}
|
|
195
|
+
for (const [key, entry] of this._cache) {
|
|
196
|
+
if (entry.expiresAt <= now) {
|
|
197
|
+
this._cache.delete(key);
|
|
198
|
+
this._stats.evictions++;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (matchesError(entry.fix, errorCode)) {
|
|
202
|
+
this._stats.hits++;
|
|
203
|
+
return entry.fix;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
this._stats.misses++;
|
|
207
|
+
return null;
|
|
208
|
+
} catch (e) {
|
|
209
|
+
debug(`FixCache.get error: ${e}`);
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Add a single fix to the cache.
|
|
215
|
+
*
|
|
216
|
+
* Evicts the oldest entry if at capacity.
|
|
217
|
+
*/
|
|
218
|
+
set(fix) {
|
|
219
|
+
try {
|
|
220
|
+
if (!this._enabled) return;
|
|
221
|
+
if (this._cache.size >= this._maxEntries) {
|
|
222
|
+
this._evictOldest();
|
|
223
|
+
}
|
|
224
|
+
const now = Date.now();
|
|
225
|
+
this._cache.set(fix.errorCode, {
|
|
226
|
+
fix,
|
|
227
|
+
createdAt: now,
|
|
228
|
+
expiresAt: now + this._ttlMs
|
|
229
|
+
});
|
|
230
|
+
this._stats.size = this._cache.size;
|
|
231
|
+
} catch (e) {
|
|
232
|
+
debug(`FixCache.set error: ${e}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Replace all cached fixes (bulk refresh).
|
|
237
|
+
*
|
|
238
|
+
* Clears the cache and populates with the given fixes.
|
|
239
|
+
*/
|
|
240
|
+
setAll(fixes) {
|
|
241
|
+
try {
|
|
242
|
+
if (!this._enabled) return;
|
|
243
|
+
this._cache.clear();
|
|
244
|
+
const now = Date.now();
|
|
245
|
+
for (const fix of fixes) {
|
|
246
|
+
if (this._cache.size >= this._maxEntries) break;
|
|
247
|
+
this._cache.set(fix.errorCode, {
|
|
248
|
+
fix,
|
|
249
|
+
createdAt: now,
|
|
250
|
+
expiresAt: now + this._ttlMs
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
this._stats.size = this._cache.size;
|
|
254
|
+
this._stats.lastRefresh = now;
|
|
255
|
+
} catch (e) {
|
|
256
|
+
debug(`FixCache.setAll error: ${e}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Get all non-expired fixes.
|
|
261
|
+
*/
|
|
262
|
+
getAll() {
|
|
263
|
+
try {
|
|
264
|
+
const now = Date.now();
|
|
265
|
+
const valid = [];
|
|
266
|
+
const expired = [];
|
|
267
|
+
for (const [key, entry] of this._cache) {
|
|
268
|
+
if (entry.expiresAt > now) {
|
|
269
|
+
valid.push(entry.fix);
|
|
270
|
+
} else {
|
|
271
|
+
expired.push(key);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
for (const key of expired) {
|
|
275
|
+
this._cache.delete(key);
|
|
276
|
+
this._stats.evictions++;
|
|
277
|
+
}
|
|
278
|
+
this._stats.size = this._cache.size;
|
|
279
|
+
return valid;
|
|
280
|
+
} catch (e) {
|
|
281
|
+
debug(`FixCache.getAll error: ${e}`);
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/** Clear all cache entries. Returns the number of entries cleared. */
|
|
286
|
+
clear() {
|
|
287
|
+
const count = this._cache.size;
|
|
288
|
+
this._cache.clear();
|
|
289
|
+
this._stats.size = 0;
|
|
290
|
+
return count;
|
|
291
|
+
}
|
|
292
|
+
/** Get cache statistics. */
|
|
293
|
+
get stats() {
|
|
294
|
+
this._stats.size = this._cache.size;
|
|
295
|
+
return { ...this._stats };
|
|
296
|
+
}
|
|
297
|
+
/** Evict the oldest cache entry. */
|
|
298
|
+
_evictOldest() {
|
|
299
|
+
if (this._cache.size === 0) return;
|
|
300
|
+
let oldestKey = null;
|
|
301
|
+
let oldestTime = Infinity;
|
|
302
|
+
for (const [key, entry] of this._cache) {
|
|
303
|
+
if (entry.createdAt < oldestTime) {
|
|
304
|
+
oldestTime = entry.createdAt;
|
|
305
|
+
oldestKey = key;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (oldestKey !== null) {
|
|
309
|
+
this._cache.delete(oldestKey);
|
|
310
|
+
this._stats.evictions++;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// src/runtime/applier.ts
|
|
318
|
+
import { createHash } from "crypto";
|
|
319
|
+
function emptyResult(fix, fixType) {
|
|
320
|
+
return {
|
|
321
|
+
applied: false,
|
|
322
|
+
fixId: fix.fixId,
|
|
323
|
+
deploymentId: fix.deploymentId,
|
|
324
|
+
fixType,
|
|
325
|
+
modifications: {}
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
var MAX_FIX_CONTENT_LENGTH, FixApplier;
|
|
329
|
+
var init_applier = __esm({
|
|
330
|
+
"src/runtime/applier.ts"() {
|
|
331
|
+
"use strict";
|
|
332
|
+
init_config();
|
|
333
|
+
init_log();
|
|
334
|
+
MAX_FIX_CONTENT_LENGTH = 1e4;
|
|
335
|
+
FixApplier = class {
|
|
336
|
+
_config;
|
|
337
|
+
_cache;
|
|
338
|
+
_applicationLog = [];
|
|
339
|
+
constructor(config, cache) {
|
|
340
|
+
this._config = resolveFixRuntimeConfig(config);
|
|
341
|
+
this._cache = cache;
|
|
342
|
+
}
|
|
343
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
344
|
+
// Fix Lookup
|
|
345
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
346
|
+
/**
|
|
347
|
+
* Get applicable fix for an error code.
|
|
348
|
+
*
|
|
349
|
+
* Considers A/B testing bucket if sessionId is provided. Uses
|
|
350
|
+
* crypto.createHash('md5') for deterministic bucketing across restarts.
|
|
351
|
+
*/
|
|
352
|
+
getFixForError(errorCode, sessionId) {
|
|
353
|
+
try {
|
|
354
|
+
const fix = this._cache.get(errorCode);
|
|
355
|
+
if (!fix) return null;
|
|
356
|
+
if (sessionId && this._config.abTestingEnabled) {
|
|
357
|
+
const hashHex = createHash("md5").update(sessionId).digest("hex").substring(0, 8);
|
|
358
|
+
const sessionHash = parseInt(hashHex, 16);
|
|
359
|
+
if (!shouldApply(fix, sessionHash)) {
|
|
360
|
+
debug(`Fix ${fix.fixId} not applied (A/B control group)`);
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return fix;
|
|
365
|
+
} catch (e) {
|
|
366
|
+
debug(`getFixForError error: ${e}`);
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
371
|
+
// 1. Prompt Fix
|
|
372
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
373
|
+
/**
|
|
374
|
+
* Apply a prompt fix to messages.
|
|
375
|
+
*
|
|
376
|
+
* Supports 4 modification types:
|
|
377
|
+
* - prepend: Add content before first system message
|
|
378
|
+
* - append: Add content after first system message
|
|
379
|
+
* - replace: Regex replace across all messages
|
|
380
|
+
* - few_shot: Insert user/assistant example pairs after system message
|
|
381
|
+
*/
|
|
382
|
+
applyPromptFix(fix, messages) {
|
|
383
|
+
try {
|
|
384
|
+
const cfg = fix.config;
|
|
385
|
+
const modificationType = cfg.modification_type ?? cfg.modificationType ?? "append";
|
|
386
|
+
const target = cfg.target ?? "system";
|
|
387
|
+
let content = cfg.content ?? "";
|
|
388
|
+
if (content.length > MAX_FIX_CONTENT_LENGTH) {
|
|
389
|
+
warn(
|
|
390
|
+
`Fix ${fix.fixId} content truncated from ${content.length} to ${MAX_FIX_CONTENT_LENGTH} chars`
|
|
391
|
+
);
|
|
392
|
+
content = content.slice(0, MAX_FIX_CONTENT_LENGTH);
|
|
393
|
+
}
|
|
394
|
+
const result = emptyResult(fix, "prompt");
|
|
395
|
+
if (this._config.dryRun) {
|
|
396
|
+
result.modifications = {
|
|
397
|
+
type: modificationType,
|
|
398
|
+
target,
|
|
399
|
+
contentPreview: content.slice(0, 100)
|
|
400
|
+
};
|
|
401
|
+
return { messages, result };
|
|
402
|
+
}
|
|
403
|
+
let modified = messages.map((m) => ({ ...m }));
|
|
404
|
+
if (modificationType === "prepend") {
|
|
405
|
+
modified = this._prependToTarget(modified, target, content);
|
|
406
|
+
result.applied = true;
|
|
407
|
+
result.modifications = { type: "prepend", target };
|
|
408
|
+
} else if (modificationType === "append") {
|
|
409
|
+
modified = this._appendToTarget(modified, target, content);
|
|
410
|
+
result.applied = true;
|
|
411
|
+
result.modifications = { type: "append", target };
|
|
412
|
+
} else if (modificationType === "replace") {
|
|
413
|
+
const pattern = cfg.pattern ?? "";
|
|
414
|
+
if (pattern) {
|
|
415
|
+
modified = this._replaceInMessages(modified, pattern, content);
|
|
416
|
+
result.applied = true;
|
|
417
|
+
result.modifications = { type: "replace", pattern };
|
|
418
|
+
}
|
|
419
|
+
} else if (modificationType === "few_shot") {
|
|
420
|
+
const examples = cfg.examples ?? [];
|
|
421
|
+
if (examples.length > 0) {
|
|
422
|
+
modified = this._addFewShotExamples(modified, examples);
|
|
423
|
+
result.applied = true;
|
|
424
|
+
result.modifications = { type: "few_shot", count: examples.length };
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return { messages: modified, result };
|
|
428
|
+
} catch (e) {
|
|
429
|
+
debug(`applyPromptFix error: ${e}`);
|
|
430
|
+
return {
|
|
431
|
+
messages,
|
|
432
|
+
result: {
|
|
433
|
+
applied: false,
|
|
434
|
+
fixId: fix.fixId,
|
|
435
|
+
fixType: "prompt",
|
|
436
|
+
modifications: {},
|
|
437
|
+
error: String(e)
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
443
|
+
// 2. Parameter Fix
|
|
444
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
445
|
+
/**
|
|
446
|
+
* Apply a parameter fix — merge config.parameters into params.
|
|
447
|
+
*/
|
|
448
|
+
applyParameterFix(fix, params) {
|
|
449
|
+
try {
|
|
450
|
+
const newParams = fix.config.parameters ?? {};
|
|
451
|
+
const result = emptyResult(fix, "parameter");
|
|
452
|
+
if (this._config.dryRun) {
|
|
453
|
+
result.modifications = { parameters: newParams };
|
|
454
|
+
return { params, result };
|
|
455
|
+
}
|
|
456
|
+
const modified = { ...params, ...newParams };
|
|
457
|
+
result.applied = true;
|
|
458
|
+
result.modifications = { parameters: newParams };
|
|
459
|
+
return { params: modified, result };
|
|
460
|
+
} catch (e) {
|
|
461
|
+
debug(`applyParameterFix error: ${e}`);
|
|
462
|
+
return {
|
|
463
|
+
params,
|
|
464
|
+
result: {
|
|
465
|
+
applied: false,
|
|
466
|
+
fixId: fix.fixId,
|
|
467
|
+
fixType: "parameter",
|
|
468
|
+
modifications: {},
|
|
469
|
+
error: String(e)
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
475
|
+
// 3. Retry Fix (async — uses setTimeout for sleeping)
|
|
476
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
477
|
+
/**
|
|
478
|
+
* Apply a retry fix to an async operation.
|
|
479
|
+
*
|
|
480
|
+
* Retries with exponential backoff + jitter using
|
|
481
|
+
* `await new Promise(r => setTimeout(r, delay))`.
|
|
482
|
+
*/
|
|
483
|
+
async applyRetryFix(fix, operation) {
|
|
484
|
+
const cfg = fix.config;
|
|
485
|
+
const maxRetries = cfg.max_retries ?? cfg.maxRetries ?? 3;
|
|
486
|
+
const initialDelayMs = cfg.initial_delay_ms ?? cfg.initialDelayMs ?? 1e3;
|
|
487
|
+
const maxDelayMs = cfg.max_delay_ms ?? cfg.maxDelayMs ?? 3e4;
|
|
488
|
+
const exponentialBase = cfg.exponential_base ?? cfg.exponentialBase ?? 2;
|
|
489
|
+
const jitter = cfg.jitter ?? true;
|
|
490
|
+
const retryOn = cfg.retry_on ?? cfg.retryOn ?? [];
|
|
491
|
+
const result = emptyResult(fix, "retry");
|
|
492
|
+
if (this._config.dryRun) {
|
|
493
|
+
result.modifications = { maxRetries, initialDelayMs };
|
|
494
|
+
const value = await operation();
|
|
495
|
+
return { value, result };
|
|
496
|
+
}
|
|
497
|
+
let lastError = null;
|
|
498
|
+
let attempts = 0;
|
|
499
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
500
|
+
attempts++;
|
|
501
|
+
try {
|
|
502
|
+
const value = await operation();
|
|
503
|
+
result.applied = true;
|
|
504
|
+
result.modifications = { attempts };
|
|
505
|
+
return { value, result };
|
|
506
|
+
} catch (e) {
|
|
507
|
+
lastError = e instanceof Error ? e : new Error(String(e));
|
|
508
|
+
const errorType = lastError.constructor.name;
|
|
509
|
+
if (retryOn.length > 0 && !retryOn.includes(errorType)) {
|
|
510
|
+
throw lastError;
|
|
511
|
+
}
|
|
512
|
+
if (attempt < maxRetries) {
|
|
513
|
+
let delayMs = Math.min(
|
|
514
|
+
initialDelayMs * Math.pow(exponentialBase, attempt),
|
|
515
|
+
maxDelayMs
|
|
516
|
+
);
|
|
517
|
+
if (jitter) {
|
|
518
|
+
delayMs *= 0.5 + Math.random();
|
|
519
|
+
}
|
|
520
|
+
debug(
|
|
521
|
+
`Retry ${attempt + 1}/${maxRetries} after ${Math.round(delayMs)}ms`
|
|
522
|
+
);
|
|
523
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
result.applied = true;
|
|
528
|
+
result.error = lastError?.message;
|
|
529
|
+
result.modifications = { attempts, exhausted: true };
|
|
530
|
+
throw lastError;
|
|
531
|
+
}
|
|
532
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
533
|
+
// 4. Fallback Fix
|
|
534
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
535
|
+
/**
|
|
536
|
+
* Apply a fallback fix.
|
|
537
|
+
*
|
|
538
|
+
* - 'model': Replace params.model with the fallback model
|
|
539
|
+
* - 'default': Set params._fallbackResponse for the caller to use
|
|
540
|
+
*/
|
|
541
|
+
applyFallbackFix(fix, params) {
|
|
542
|
+
try {
|
|
543
|
+
const cfg = fix.config;
|
|
544
|
+
const fallbackType = cfg.fallback_type ?? cfg.fallbackType ?? "model";
|
|
545
|
+
const fallbackConfig = cfg.fallback_config ?? cfg.fallbackConfig ?? {};
|
|
546
|
+
const result = emptyResult(fix, "fallback");
|
|
547
|
+
if (this._config.dryRun) {
|
|
548
|
+
result.modifications = { fallbackType, fallbackConfig };
|
|
549
|
+
return { params, result };
|
|
550
|
+
}
|
|
551
|
+
const modified = { ...params };
|
|
552
|
+
if (fallbackType === "model") {
|
|
553
|
+
const fallbackModel = fallbackConfig.model;
|
|
554
|
+
if (fallbackModel) {
|
|
555
|
+
modified.model = fallbackModel;
|
|
556
|
+
result.applied = true;
|
|
557
|
+
result.modifications = { model: fallbackModel };
|
|
558
|
+
}
|
|
559
|
+
} else if (fallbackType === "default") {
|
|
560
|
+
const defaultResponse = fallbackConfig.response;
|
|
561
|
+
if (defaultResponse !== void 0) {
|
|
562
|
+
modified._fallbackResponse = defaultResponse;
|
|
563
|
+
result.applied = true;
|
|
564
|
+
result.modifications = { defaultResponse: true };
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return { params: modified, result };
|
|
568
|
+
} catch (e) {
|
|
569
|
+
debug(`applyFallbackFix error: ${e}`);
|
|
570
|
+
return {
|
|
571
|
+
params,
|
|
572
|
+
result: {
|
|
573
|
+
applied: false,
|
|
574
|
+
fixId: fix.fixId,
|
|
575
|
+
fixType: "fallback",
|
|
576
|
+
modifications: {},
|
|
577
|
+
error: String(e)
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
583
|
+
// 5. Guard Fix
|
|
584
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
585
|
+
/**
|
|
586
|
+
* Apply a guard fix (validation).
|
|
587
|
+
*
|
|
588
|
+
* Guard types:
|
|
589
|
+
* - content_filter: Regex-based blocked patterns
|
|
590
|
+
* - format_check: JSON.parse() validity
|
|
591
|
+
* - input_validation / output_validation: min/max length
|
|
592
|
+
*/
|
|
593
|
+
applyGuardFix(fix, content, isInput = true) {
|
|
594
|
+
try {
|
|
595
|
+
const cfg = fix.config;
|
|
596
|
+
const guardType = cfg.guard_type ?? cfg.guardType ?? "output_validation";
|
|
597
|
+
const guardConfig = cfg.guard_config ?? cfg.guardConfig ?? {};
|
|
598
|
+
const result = emptyResult(fix, "guard");
|
|
599
|
+
if (isInput && guardType !== "input_validation" && guardType !== "content_filter") {
|
|
600
|
+
return { content, passed: true, result };
|
|
601
|
+
}
|
|
602
|
+
if (!isInput && guardType !== "output_validation" && guardType !== "format_check") {
|
|
603
|
+
return { content, passed: true, result };
|
|
604
|
+
}
|
|
605
|
+
if (this._config.dryRun) {
|
|
606
|
+
result.modifications = { guardType };
|
|
607
|
+
return { content, passed: true, result };
|
|
608
|
+
}
|
|
609
|
+
let passed = true;
|
|
610
|
+
if (guardType === "content_filter") {
|
|
611
|
+
const blockedPatterns = guardConfig.blocked_patterns ?? guardConfig.blockedPatterns ?? [];
|
|
612
|
+
for (const pattern of blockedPatterns) {
|
|
613
|
+
const regex = this._safeCompileRegex(pattern);
|
|
614
|
+
if (regex && regex.test(content)) {
|
|
615
|
+
passed = false;
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
} else if (guardType === "format_check") {
|
|
620
|
+
const requiredFormat = guardConfig.format;
|
|
621
|
+
if (requiredFormat === "json") {
|
|
622
|
+
try {
|
|
623
|
+
JSON.parse(content);
|
|
624
|
+
} catch {
|
|
625
|
+
passed = false;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
} else if (guardType === "input_validation" || guardType === "output_validation") {
|
|
629
|
+
const minLength = guardConfig.min_length ?? guardConfig.minLength ?? 0;
|
|
630
|
+
const maxLength = guardConfig.max_length ?? guardConfig.maxLength ?? Infinity;
|
|
631
|
+
if (content.length < minLength || content.length > maxLength) {
|
|
632
|
+
passed = false;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
result.applied = true;
|
|
636
|
+
result.modifications = { passed, guardType };
|
|
637
|
+
return { content, passed, result };
|
|
638
|
+
} catch (e) {
|
|
639
|
+
debug(`applyGuardFix error: ${e}`);
|
|
640
|
+
return {
|
|
641
|
+
content,
|
|
642
|
+
passed: true,
|
|
643
|
+
result: {
|
|
644
|
+
applied: false,
|
|
645
|
+
fixId: fix.fixId,
|
|
646
|
+
fixType: "guard",
|
|
647
|
+
modifications: {},
|
|
648
|
+
error: String(e)
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
654
|
+
// Application Log
|
|
655
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
656
|
+
/** Log a fix application result. */
|
|
657
|
+
logApplication(result) {
|
|
658
|
+
this._applicationLog.push(result);
|
|
659
|
+
if (this._applicationLog.length > 1e3) {
|
|
660
|
+
this._applicationLog = this._applicationLog.slice(-500);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
/** Get the application log. */
|
|
664
|
+
getApplicationLog() {
|
|
665
|
+
return [...this._applicationLog];
|
|
666
|
+
}
|
|
667
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
668
|
+
// Private Helpers
|
|
669
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
670
|
+
/**
|
|
671
|
+
* Safely compile a regex pattern from untrusted input.
|
|
672
|
+
* Returns null if invalid or too long.
|
|
673
|
+
*/
|
|
674
|
+
_safeCompileRegex(pattern, maxLength = 500) {
|
|
675
|
+
if (!pattern || pattern.length > maxLength) return null;
|
|
676
|
+
try {
|
|
677
|
+
return new RegExp(pattern, "i");
|
|
678
|
+
} catch {
|
|
679
|
+
debug(`Invalid regex pattern (length=${pattern.length})`);
|
|
680
|
+
return null;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
/** Prepend content to the first message matching the target role. */
|
|
684
|
+
_prependToTarget(messages, target, content) {
|
|
685
|
+
return messages.map((msg) => {
|
|
686
|
+
if (msg.role === target) {
|
|
687
|
+
return {
|
|
688
|
+
...msg,
|
|
689
|
+
content: content + "\n\n" + (msg.content ?? "")
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
return msg;
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
/** Append content to the first message matching the target role. */
|
|
696
|
+
_appendToTarget(messages, target, content) {
|
|
697
|
+
return messages.map((msg) => {
|
|
698
|
+
if (msg.role === target) {
|
|
699
|
+
return {
|
|
700
|
+
...msg,
|
|
701
|
+
content: (msg.content ?? "") + "\n\n" + content
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
return msg;
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
/** Regex replace across all messages. */
|
|
708
|
+
_replaceInMessages(messages, pattern, replacement) {
|
|
709
|
+
const regex = this._safeCompileRegex(pattern);
|
|
710
|
+
if (!regex) {
|
|
711
|
+
debug("Skipping invalid regex pattern in prompt fix");
|
|
712
|
+
return messages;
|
|
713
|
+
}
|
|
714
|
+
return messages.map((msg) => {
|
|
715
|
+
const msgContent = msg.content;
|
|
716
|
+
if (typeof msgContent === "string") {
|
|
717
|
+
return { ...msg, content: msgContent.replace(regex, replacement) };
|
|
718
|
+
}
|
|
719
|
+
return msg;
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
/** Add few-shot examples after the first system message. */
|
|
723
|
+
_addFewShotExamples(messages, examples) {
|
|
724
|
+
const result = [];
|
|
725
|
+
let systemFound = false;
|
|
726
|
+
for (const msg of messages) {
|
|
727
|
+
result.push(msg);
|
|
728
|
+
if (msg.role === "system" && !systemFound) {
|
|
729
|
+
systemFound = true;
|
|
730
|
+
for (const example of examples) {
|
|
731
|
+
if (example.user) {
|
|
732
|
+
result.push({ role: "user", content: example.user });
|
|
733
|
+
}
|
|
734
|
+
if (example.assistant) {
|
|
735
|
+
result.push({ role: "assistant", content: example.assistant });
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return result;
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
// src/runtime/loader.ts
|
|
747
|
+
var SDK_VERSION2, FixLoader;
|
|
748
|
+
var init_loader = __esm({
|
|
749
|
+
"src/runtime/loader.ts"() {
|
|
750
|
+
"use strict";
|
|
751
|
+
init_config();
|
|
752
|
+
init_config();
|
|
753
|
+
init_log();
|
|
754
|
+
SDK_VERSION2 = "0.1.3";
|
|
755
|
+
FixLoader = class _FixLoader {
|
|
756
|
+
_config;
|
|
757
|
+
_cache;
|
|
758
|
+
_refreshTimer = null;
|
|
759
|
+
_lastLoadTime = null;
|
|
760
|
+
_consecutiveFailures = 0;
|
|
761
|
+
_circuitOpenUntil = 0;
|
|
762
|
+
_onLoadCallbacks = [];
|
|
763
|
+
// Circuit breaker settings
|
|
764
|
+
static CIRCUIT_BREAKER_THRESHOLD = 3;
|
|
765
|
+
static CIRCUIT_BREAKER_COOLDOWN_MS = 3e4;
|
|
766
|
+
constructor(config, cache) {
|
|
767
|
+
this._config = resolveFixRuntimeConfig(config);
|
|
768
|
+
this._cache = cache;
|
|
769
|
+
}
|
|
770
|
+
/** Timestamp of last successful load. */
|
|
771
|
+
get lastLoadTime() {
|
|
772
|
+
return this._lastLoadTime;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Start the loader: perform initial load and start background refresh.
|
|
776
|
+
*
|
|
777
|
+
* Never throws — failures are logged and the runtime degrades gracefully.
|
|
778
|
+
*/
|
|
779
|
+
start() {
|
|
780
|
+
try {
|
|
781
|
+
if (!this._config.enabled) {
|
|
782
|
+
debug("Fix runtime disabled, skipping loader start");
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
if (!this._config.apiKey) {
|
|
786
|
+
warn("No API key configured, fixes will not be loaded");
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
this.loadSync().catch((e) => {
|
|
790
|
+
debug(`Initial fix load failed: ${e}`);
|
|
791
|
+
});
|
|
792
|
+
if (this._config.autoRefresh) {
|
|
793
|
+
this._startRefreshInterval();
|
|
794
|
+
}
|
|
795
|
+
} catch (e) {
|
|
796
|
+
debug(`FixLoader.start error: ${e}`);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
/** Stop the loader: clear the refresh interval. */
|
|
800
|
+
stop() {
|
|
801
|
+
try {
|
|
802
|
+
if (this._refreshTimer !== null) {
|
|
803
|
+
clearInterval(this._refreshTimer);
|
|
804
|
+
this._refreshTimer = null;
|
|
805
|
+
}
|
|
806
|
+
} catch (e) {
|
|
807
|
+
debug(`FixLoader.stop error: ${e}`);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Register a callback invoked after each successful load.
|
|
812
|
+
*/
|
|
813
|
+
onLoad(callback) {
|
|
814
|
+
this._onLoadCallbacks.push(callback);
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Load fixes from the API.
|
|
818
|
+
*
|
|
819
|
+
* Name kept as "loadSync" for parity with the Python SDK, but this
|
|
820
|
+
* returns a Promise in JS (there's no synchronous fetch in Node.js).
|
|
821
|
+
*/
|
|
822
|
+
async loadSync() {
|
|
823
|
+
if (!this._config.apiKey) {
|
|
824
|
+
return [];
|
|
825
|
+
}
|
|
826
|
+
const now = Date.now();
|
|
827
|
+
if (this._consecutiveFailures >= _FixLoader.CIRCUIT_BREAKER_THRESHOLD && now < this._circuitOpenUntil) {
|
|
828
|
+
debug("Fix loader circuit breaker open \u2014 skipping load");
|
|
829
|
+
return this._cache.getAll();
|
|
830
|
+
}
|
|
831
|
+
try {
|
|
832
|
+
const endpoint = this._config.apiEndpoint.replace(/\/+$/, "");
|
|
833
|
+
const url = `${endpoint}/api/v1/fixes/active`;
|
|
834
|
+
const controller = new AbortController();
|
|
835
|
+
const timeoutId = setTimeout(
|
|
836
|
+
() => controller.abort(),
|
|
837
|
+
this._config.timeoutMs
|
|
838
|
+
);
|
|
839
|
+
const response = await fetch(url, {
|
|
840
|
+
method: "GET",
|
|
841
|
+
headers: {
|
|
842
|
+
"Authorization": `Bearer ${this._config.apiKey}`,
|
|
843
|
+
"Content-Type": "application/json",
|
|
844
|
+
"X-Risicare-SDK-Version": SDK_VERSION2
|
|
845
|
+
},
|
|
846
|
+
signal: controller.signal
|
|
847
|
+
});
|
|
848
|
+
clearTimeout(timeoutId);
|
|
849
|
+
if (!response.ok) {
|
|
850
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
851
|
+
}
|
|
852
|
+
const data = await response.json();
|
|
853
|
+
const fixes = this._parseResponse(data);
|
|
854
|
+
this._onLoadSuccess(fixes);
|
|
855
|
+
return fixes;
|
|
856
|
+
} catch (e) {
|
|
857
|
+
this._onLoadFailure(e);
|
|
858
|
+
throw e;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
/** Get currently cached fixes without hitting the API. */
|
|
862
|
+
getCached() {
|
|
863
|
+
return this._cache.getAll();
|
|
864
|
+
}
|
|
865
|
+
// ─── Private ────────────────────────────────────────────────────────────
|
|
866
|
+
_parseResponse(data) {
|
|
867
|
+
const fixes = [];
|
|
868
|
+
const items = data.fixes ?? data.data ?? [];
|
|
869
|
+
if (!Array.isArray(items)) return fixes;
|
|
870
|
+
for (const item of items) {
|
|
871
|
+
try {
|
|
872
|
+
if (item && typeof item === "object") {
|
|
873
|
+
fixes.push(activeFixFromApiResponse(item));
|
|
874
|
+
}
|
|
875
|
+
} catch (e) {
|
|
876
|
+
debug(`Failed to parse fix item: ${e}`);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return fixes;
|
|
880
|
+
}
|
|
881
|
+
_onLoadSuccess(fixes) {
|
|
882
|
+
this._lastLoadTime = Date.now();
|
|
883
|
+
this._consecutiveFailures = 0;
|
|
884
|
+
this._cache.setAll(fixes);
|
|
885
|
+
for (const callback of this._onLoadCallbacks) {
|
|
886
|
+
try {
|
|
887
|
+
callback(fixes);
|
|
888
|
+
} catch (e) {
|
|
889
|
+
debug(`Load callback error: ${e}`);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
debug(`Loaded ${fixes.length} active fixes`);
|
|
893
|
+
}
|
|
894
|
+
_onLoadFailure(error) {
|
|
895
|
+
this._consecutiveFailures++;
|
|
896
|
+
if (this._consecutiveFailures >= _FixLoader.CIRCUIT_BREAKER_THRESHOLD) {
|
|
897
|
+
this._circuitOpenUntil = Date.now() + _FixLoader.CIRCUIT_BREAKER_COOLDOWN_MS;
|
|
898
|
+
warn(
|
|
899
|
+
`Fix loader circuit breaker opened after ${this._consecutiveFailures} failures. Cooldown: ${_FixLoader.CIRCUIT_BREAKER_COOLDOWN_MS / 1e3}s`
|
|
900
|
+
);
|
|
901
|
+
} else {
|
|
902
|
+
debug(
|
|
903
|
+
`Fix load failed (attempt ${this._consecutiveFailures}): ${error.message}`
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
_startRefreshInterval() {
|
|
908
|
+
const tick = () => {
|
|
909
|
+
this.loadSync().catch((e) => {
|
|
910
|
+
debug(`Background fix refresh failed: ${e}`);
|
|
911
|
+
});
|
|
912
|
+
};
|
|
913
|
+
this._refreshTimer = setInterval(
|
|
914
|
+
() => {
|
|
915
|
+
if (this._consecutiveFailures >= _FixLoader.CIRCUIT_BREAKER_THRESHOLD && Date.now() < this._circuitOpenUntil) {
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
tick();
|
|
919
|
+
},
|
|
920
|
+
this._config.refreshIntervalMs
|
|
921
|
+
);
|
|
922
|
+
this._refreshTimer.unref();
|
|
923
|
+
debug("Started fix refresh interval");
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
// src/runtime/interceptors.ts
|
|
930
|
+
function createInterceptContext(operationType, operationName, options) {
|
|
931
|
+
return {
|
|
932
|
+
operationType,
|
|
933
|
+
operationName,
|
|
934
|
+
sessionId: options?.sessionId,
|
|
935
|
+
traceId: options?.traceId,
|
|
936
|
+
errorCode: options?.errorCode,
|
|
937
|
+
attempt: 1,
|
|
938
|
+
appliedFixes: []
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
function classifyError(error) {
|
|
942
|
+
const errorType = error.constructor.name.toLowerCase();
|
|
943
|
+
const errorMsg = error.message.toLowerCase();
|
|
944
|
+
if (errorType.includes("timeout") || errorMsg.includes("timeout") || errorMsg.includes("aborted")) {
|
|
945
|
+
return "TOOL.EXECUTION.TIMEOUT";
|
|
946
|
+
}
|
|
947
|
+
if (errorMsg.includes("rate") && errorMsg.includes("limit")) {
|
|
948
|
+
return "TOOL.EXECUTION.RATE_LIMIT";
|
|
949
|
+
}
|
|
950
|
+
if (errorMsg.includes("429") || errorMsg.includes("too many requests")) {
|
|
951
|
+
return "TOOL.EXECUTION.RATE_LIMIT";
|
|
952
|
+
}
|
|
953
|
+
if (errorType.includes("connection") || errorMsg.includes("connection") || errorMsg.includes("econnrefused") || errorMsg.includes("econnreset") || errorMsg.includes("fetch failed")) {
|
|
954
|
+
return "TOOL.EXECUTION.CONNECTION_ERROR";
|
|
955
|
+
}
|
|
956
|
+
if (errorMsg.includes("auth") || errorMsg.includes("401") || errorMsg.includes("403") || errorMsg.includes("unauthorized") || errorMsg.includes("forbidden")) {
|
|
957
|
+
return "TOOL.EXECUTION.AUTH_ERROR";
|
|
958
|
+
}
|
|
959
|
+
if (errorType.includes("syntaxerror") || errorType.includes("json") || errorMsg.includes("json") || errorMsg.includes("unexpected token")) {
|
|
960
|
+
return "OUTPUT.FORMAT.JSON_INVALID";
|
|
961
|
+
}
|
|
962
|
+
return "TOOL.EXECUTION.FAILURE";
|
|
963
|
+
}
|
|
964
|
+
var DefaultFixInterceptor;
|
|
965
|
+
var init_interceptors = __esm({
|
|
966
|
+
"src/runtime/interceptors.ts"() {
|
|
967
|
+
"use strict";
|
|
968
|
+
init_config();
|
|
969
|
+
init_log();
|
|
970
|
+
DefaultFixInterceptor = class {
|
|
971
|
+
_config;
|
|
972
|
+
_cache;
|
|
973
|
+
_applier;
|
|
974
|
+
constructor(config, cache, applier) {
|
|
975
|
+
this._config = resolveFixRuntimeConfig(config);
|
|
976
|
+
this._cache = cache;
|
|
977
|
+
this._applier = applier;
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Apply pre-call fixes.
|
|
981
|
+
*
|
|
982
|
+
* - If retrying after error: look up fix by error_code, apply
|
|
983
|
+
* prompt/parameter/fallback fixes
|
|
984
|
+
* - Runs input guard fixes on all message content
|
|
985
|
+
*/
|
|
986
|
+
preCall(ctx, messages, params) {
|
|
987
|
+
try {
|
|
988
|
+
if (!this._config.enabled) {
|
|
989
|
+
return { messages, params };
|
|
990
|
+
}
|
|
991
|
+
let modifiedMessages = messages;
|
|
992
|
+
let modifiedParams = { ...params };
|
|
993
|
+
if (ctx.errorCode) {
|
|
994
|
+
const fix = this._applier.getFixForError(ctx.errorCode, ctx.sessionId);
|
|
995
|
+
if (fix) {
|
|
996
|
+
debug(`Applying fix ${fix.fixId} for ${ctx.errorCode}`);
|
|
997
|
+
if (fix.fixType === "prompt" && messages) {
|
|
998
|
+
const msgRecords = messages;
|
|
999
|
+
const { messages: newMessages, result } = this._applier.applyPromptFix(fix, msgRecords);
|
|
1000
|
+
modifiedMessages = newMessages;
|
|
1001
|
+
ctx.appliedFixes.push(result);
|
|
1002
|
+
this._applier.logApplication(result);
|
|
1003
|
+
} else if (fix.fixType === "parameter") {
|
|
1004
|
+
const { params: newParams, result } = this._applier.applyParameterFix(fix, modifiedParams);
|
|
1005
|
+
modifiedParams = newParams;
|
|
1006
|
+
ctx.appliedFixes.push(result);
|
|
1007
|
+
this._applier.logApplication(result);
|
|
1008
|
+
} else if (fix.fixType === "fallback") {
|
|
1009
|
+
const { params: newParams, result } = this._applier.applyFallbackFix(fix, modifiedParams);
|
|
1010
|
+
modifiedParams = newParams;
|
|
1011
|
+
ctx.appliedFixes.push(result);
|
|
1012
|
+
this._applier.logApplication(result);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
if (modifiedMessages && this._config.enabled) {
|
|
1017
|
+
for (const msg of modifiedMessages) {
|
|
1018
|
+
const content = msg.content;
|
|
1019
|
+
if (typeof content === "string") {
|
|
1020
|
+
for (const fix of this._cache.getAll()) {
|
|
1021
|
+
if (fix.fixType === "guard") {
|
|
1022
|
+
const { result } = this._applier.applyGuardFix(
|
|
1023
|
+
fix,
|
|
1024
|
+
content,
|
|
1025
|
+
true
|
|
1026
|
+
);
|
|
1027
|
+
if (result.applied) {
|
|
1028
|
+
ctx.appliedFixes.push(result);
|
|
1029
|
+
this._applier.logApplication(result);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
return { messages: modifiedMessages, params: modifiedParams };
|
|
1037
|
+
} catch (e) {
|
|
1038
|
+
debug(`DefaultFixInterceptor.preCall error: ${e}`);
|
|
1039
|
+
return { messages, params };
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Apply post-call fixes (output validation).
|
|
1044
|
+
*
|
|
1045
|
+
* Runs output guard fixes on response content.
|
|
1046
|
+
*/
|
|
1047
|
+
postCall(ctx, response) {
|
|
1048
|
+
try {
|
|
1049
|
+
if (!this._config.enabled) {
|
|
1050
|
+
return { response, shouldContinue: true };
|
|
1051
|
+
}
|
|
1052
|
+
for (const fix of this._cache.getAll()) {
|
|
1053
|
+
if (fix.fixType !== "guard") continue;
|
|
1054
|
+
const content = this._extractResponseContent(response);
|
|
1055
|
+
if (!content) continue;
|
|
1056
|
+
const { passed, result } = this._applier.applyGuardFix(
|
|
1057
|
+
fix,
|
|
1058
|
+
content,
|
|
1059
|
+
false
|
|
1060
|
+
);
|
|
1061
|
+
if (result.applied) {
|
|
1062
|
+
ctx.appliedFixes.push(result);
|
|
1063
|
+
this._applier.logApplication(result);
|
|
1064
|
+
}
|
|
1065
|
+
if (!passed) {
|
|
1066
|
+
warn(`Output guard failed for fix ${fix.fixId}`);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
return { response, shouldContinue: true };
|
|
1070
|
+
} catch (e) {
|
|
1071
|
+
debug(`DefaultFixInterceptor.postCall error: ${e}`);
|
|
1072
|
+
return { response, shouldContinue: true };
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Handle errors and decide whether to retry.
|
|
1077
|
+
*
|
|
1078
|
+
* Classifies the error, looks up retry/fallback fixes, and returns
|
|
1079
|
+
* whether the caller should retry.
|
|
1080
|
+
*/
|
|
1081
|
+
onError(ctx, error) {
|
|
1082
|
+
try {
|
|
1083
|
+
if (!this._config.enabled) {
|
|
1084
|
+
return { shouldRetry: false, modifiedParams: null };
|
|
1085
|
+
}
|
|
1086
|
+
const errorCode = classifyError(error);
|
|
1087
|
+
ctx.errorCode = errorCode;
|
|
1088
|
+
ctx.errorMessage = error.message;
|
|
1089
|
+
const fix = this._applier.getFixForError(errorCode, ctx.sessionId);
|
|
1090
|
+
if (!fix) {
|
|
1091
|
+
return { shouldRetry: false, modifiedParams: null };
|
|
1092
|
+
}
|
|
1093
|
+
if (fix.fixType === "retry") {
|
|
1094
|
+
const maxRetries = fix.config.max_retries ?? fix.config.maxRetries ?? 3;
|
|
1095
|
+
if (ctx.attempt <= maxRetries) {
|
|
1096
|
+
debug(
|
|
1097
|
+
`Retry fix ${fix.fixId}: attempt ${ctx.attempt}/${maxRetries}`
|
|
1098
|
+
);
|
|
1099
|
+
const result = {
|
|
1100
|
+
applied: true,
|
|
1101
|
+
fixId: fix.fixId,
|
|
1102
|
+
deploymentId: fix.deploymentId,
|
|
1103
|
+
fixType: "retry",
|
|
1104
|
+
modifications: { attempt: ctx.attempt }
|
|
1105
|
+
};
|
|
1106
|
+
ctx.appliedFixes.push(result);
|
|
1107
|
+
this._applier.logApplication(result);
|
|
1108
|
+
return { shouldRetry: true, modifiedParams: null };
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
if (fix.fixType === "fallback") {
|
|
1112
|
+
const { params: modifiedParams, result } = this._applier.applyFallbackFix(fix, {});
|
|
1113
|
+
if (result.applied) {
|
|
1114
|
+
ctx.appliedFixes.push(result);
|
|
1115
|
+
this._applier.logApplication(result);
|
|
1116
|
+
return { shouldRetry: true, modifiedParams };
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return { shouldRetry: false, modifiedParams: null };
|
|
1120
|
+
} catch (e) {
|
|
1121
|
+
debug(`DefaultFixInterceptor.onError error: ${e}`);
|
|
1122
|
+
return { shouldRetry: false, modifiedParams: null };
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
// ─── Private ────────────────────────────────────────────────────────────
|
|
1126
|
+
/** Extract text content from a response object. */
|
|
1127
|
+
_extractResponseContent(response) {
|
|
1128
|
+
if (typeof response === "string") return response;
|
|
1129
|
+
if (response && typeof response === "object") {
|
|
1130
|
+
const resp = response;
|
|
1131
|
+
const choices = resp.choices;
|
|
1132
|
+
if (Array.isArray(choices) && choices.length > 0) {
|
|
1133
|
+
const first = choices[0];
|
|
1134
|
+
const message = first?.message;
|
|
1135
|
+
if (typeof message?.content === "string") {
|
|
1136
|
+
return message.content;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
const content = resp.content;
|
|
1140
|
+
if (typeof content === "string") return content;
|
|
1141
|
+
if (Array.isArray(content) && content.length > 0) {
|
|
1142
|
+
const first = content[0];
|
|
1143
|
+
if (typeof first?.text === "string") return first.text;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
return null;
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
// src/runtime/runtime.ts
|
|
1153
|
+
var runtime_exports = {};
|
|
1154
|
+
__export(runtime_exports, {
|
|
1155
|
+
FixRuntime: () => FixRuntime,
|
|
1156
|
+
getFixRuntime: () => getFixRuntime,
|
|
1157
|
+
initFixRuntime: () => initFixRuntime,
|
|
1158
|
+
setFixRuntime: () => setFixRuntime,
|
|
1159
|
+
shutdownFixRuntime: () => shutdownFixRuntime
|
|
1160
|
+
});
|
|
1161
|
+
function getFixRuntime() {
|
|
1162
|
+
return G2[RUNTIME_KEY];
|
|
1163
|
+
}
|
|
1164
|
+
function setFixRuntime(runtime) {
|
|
1165
|
+
G2[RUNTIME_KEY] = runtime;
|
|
1166
|
+
}
|
|
1167
|
+
function initFixRuntime(config) {
|
|
1168
|
+
try {
|
|
1169
|
+
const existing = G2[RUNTIME_KEY];
|
|
1170
|
+
if (existing) {
|
|
1171
|
+
debug("Fix runtime already initialized");
|
|
1172
|
+
return existing;
|
|
1173
|
+
}
|
|
1174
|
+
const runtime = new FixRuntime(config);
|
|
1175
|
+
runtime.start();
|
|
1176
|
+
G2[RUNTIME_KEY] = runtime;
|
|
1177
|
+
return runtime;
|
|
1178
|
+
} catch (e) {
|
|
1179
|
+
warn(`initFixRuntime failed: ${e}`);
|
|
1180
|
+
const fallback = new FixRuntime({ ...config, enabled: false });
|
|
1181
|
+
G2[RUNTIME_KEY] = fallback;
|
|
1182
|
+
return fallback;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
function shutdownFixRuntime() {
|
|
1186
|
+
try {
|
|
1187
|
+
const runtime = G2[RUNTIME_KEY];
|
|
1188
|
+
if (runtime) {
|
|
1189
|
+
runtime.stop();
|
|
1190
|
+
}
|
|
1191
|
+
G2[RUNTIME_KEY] = void 0;
|
|
1192
|
+
} catch (e) {
|
|
1193
|
+
debug(`shutdownFixRuntime error: ${e}`);
|
|
1194
|
+
G2[RUNTIME_KEY] = void 0;
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
var G2, RUNTIME_KEY, FixRuntime;
|
|
1198
|
+
var init_runtime = __esm({
|
|
1199
|
+
"src/runtime/runtime.ts"() {
|
|
1200
|
+
"use strict";
|
|
1201
|
+
init_cache();
|
|
1202
|
+
init_config();
|
|
1203
|
+
init_applier();
|
|
1204
|
+
init_loader();
|
|
1205
|
+
init_interceptors();
|
|
1206
|
+
init_log();
|
|
1207
|
+
G2 = globalThis;
|
|
1208
|
+
RUNTIME_KEY = "__risicare_fix_runtime";
|
|
1209
|
+
FixRuntime = class {
|
|
1210
|
+
_config;
|
|
1211
|
+
_cache;
|
|
1212
|
+
_loader;
|
|
1213
|
+
_applier;
|
|
1214
|
+
_interceptor;
|
|
1215
|
+
_started = false;
|
|
1216
|
+
// Effectiveness tracking
|
|
1217
|
+
_fixApplications = /* @__PURE__ */ new Map();
|
|
1218
|
+
_fixSuccesses = /* @__PURE__ */ new Map();
|
|
1219
|
+
constructor(config) {
|
|
1220
|
+
this._config = resolveFixRuntimeConfig(config);
|
|
1221
|
+
this._cache = new FixCache(this._config);
|
|
1222
|
+
this._loader = new FixLoader(config, this._cache);
|
|
1223
|
+
this._applier = new FixApplier(config, this._cache);
|
|
1224
|
+
this._interceptor = new DefaultFixInterceptor(
|
|
1225
|
+
config,
|
|
1226
|
+
this._cache,
|
|
1227
|
+
this._applier
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
// ─── Accessors ──────────────────────────────────────────────────────────
|
|
1231
|
+
/** Runtime configuration (with defaults resolved). */
|
|
1232
|
+
get config() {
|
|
1233
|
+
return this._config;
|
|
1234
|
+
}
|
|
1235
|
+
/** Whether the runtime is enabled and started. */
|
|
1236
|
+
get isEnabled() {
|
|
1237
|
+
return this._config.enabled && this._started;
|
|
1238
|
+
}
|
|
1239
|
+
/** The fix cache. */
|
|
1240
|
+
get cache() {
|
|
1241
|
+
return this._cache;
|
|
1242
|
+
}
|
|
1243
|
+
/** The fix loader. */
|
|
1244
|
+
get loader() {
|
|
1245
|
+
return this._loader;
|
|
1246
|
+
}
|
|
1247
|
+
/** The fix applier. */
|
|
1248
|
+
get applier() {
|
|
1249
|
+
return this._applier;
|
|
1250
|
+
}
|
|
1251
|
+
/** The interceptor. */
|
|
1252
|
+
get interceptor() {
|
|
1253
|
+
return this._interceptor;
|
|
1254
|
+
}
|
|
1255
|
+
// ─── Lifecycle ──────────────────────────────────────────────────────────
|
|
1256
|
+
/**
|
|
1257
|
+
* Start the runtime.
|
|
1258
|
+
*
|
|
1259
|
+
* Triggers initial fix load and starts background refresh.
|
|
1260
|
+
* Never throws — failures degrade gracefully.
|
|
1261
|
+
*/
|
|
1262
|
+
start() {
|
|
1263
|
+
try {
|
|
1264
|
+
if (this._started) return;
|
|
1265
|
+
if (!this._config.enabled) {
|
|
1266
|
+
debug("Fix runtime disabled");
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
debug("Starting fix runtime...");
|
|
1270
|
+
this._loader.start();
|
|
1271
|
+
this._started = true;
|
|
1272
|
+
debug("Fix runtime started");
|
|
1273
|
+
} catch (e) {
|
|
1274
|
+
warn(`Fix runtime start failed: ${e}`);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Stop the runtime.
|
|
1279
|
+
*
|
|
1280
|
+
* Stops background refresh and clears the cache.
|
|
1281
|
+
* Never throws.
|
|
1282
|
+
*/
|
|
1283
|
+
stop() {
|
|
1284
|
+
try {
|
|
1285
|
+
if (!this._started) return;
|
|
1286
|
+
debug("Stopping fix runtime...");
|
|
1287
|
+
this._loader.stop();
|
|
1288
|
+
this._cache.clear();
|
|
1289
|
+
this._started = false;
|
|
1290
|
+
debug("Fix runtime stopped");
|
|
1291
|
+
} catch (e) {
|
|
1292
|
+
debug(`Fix runtime stop error: ${e}`);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
// ─── Intercept API ──────────────────────────────────────────────────────
|
|
1296
|
+
/**
|
|
1297
|
+
* Intercept a call: create context and run preCall fixes.
|
|
1298
|
+
*
|
|
1299
|
+
* Returns modified messages/params and the InterceptContext for
|
|
1300
|
+
* use in subsequent interceptResponse / interceptError calls.
|
|
1301
|
+
*/
|
|
1302
|
+
interceptCall(operationType, operationName, messages, params, options) {
|
|
1303
|
+
const ctx = createInterceptContext(operationType, operationName, options);
|
|
1304
|
+
try {
|
|
1305
|
+
if (!this.isEnabled) {
|
|
1306
|
+
return { messages, params, ctx };
|
|
1307
|
+
}
|
|
1308
|
+
const result = this._interceptor.preCall(ctx, messages, params);
|
|
1309
|
+
return { messages: result.messages, params: result.params, ctx };
|
|
1310
|
+
} catch (e) {
|
|
1311
|
+
debug(`interceptCall error: ${e}`);
|
|
1312
|
+
return { messages, params, ctx };
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Intercept a response: run postCall fixes (output validation).
|
|
1317
|
+
*/
|
|
1318
|
+
interceptResponse(ctx, response) {
|
|
1319
|
+
try {
|
|
1320
|
+
if (!this.isEnabled) {
|
|
1321
|
+
return { response, shouldContinue: true };
|
|
1322
|
+
}
|
|
1323
|
+
const result = this._interceptor.postCall(ctx, response);
|
|
1324
|
+
this._trackSuccess(ctx);
|
|
1325
|
+
return result;
|
|
1326
|
+
} catch (e) {
|
|
1327
|
+
debug(`interceptResponse error: ${e}`);
|
|
1328
|
+
return { response, shouldContinue: true };
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Intercept an error: decide on retry/fallback.
|
|
1333
|
+
*/
|
|
1334
|
+
interceptError(ctx, error) {
|
|
1335
|
+
try {
|
|
1336
|
+
if (!this.isEnabled) {
|
|
1337
|
+
return { shouldRetry: false, modifiedParams: null };
|
|
1338
|
+
}
|
|
1339
|
+
ctx.attempt++;
|
|
1340
|
+
const result = this._interceptor.onError(ctx, error);
|
|
1341
|
+
if (!result.shouldRetry) {
|
|
1342
|
+
this._trackFailure(ctx);
|
|
1343
|
+
}
|
|
1344
|
+
return result;
|
|
1345
|
+
} catch (e) {
|
|
1346
|
+
debug(`interceptError error: ${e}`);
|
|
1347
|
+
return { shouldRetry: false, modifiedParams: null };
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
// ─── Direct Fix Access ──────────────────────────────────────────────────
|
|
1351
|
+
/**
|
|
1352
|
+
* Get applicable fix for an error code.
|
|
1353
|
+
*/
|
|
1354
|
+
getFix(errorCode, sessionId) {
|
|
1355
|
+
try {
|
|
1356
|
+
if (!this.isEnabled) return null;
|
|
1357
|
+
return this._applier.getFixForError(errorCode, sessionId);
|
|
1358
|
+
} catch (e) {
|
|
1359
|
+
debug(`getFix error: ${e}`);
|
|
1360
|
+
return null;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Manually refresh fixes from the API.
|
|
1365
|
+
*/
|
|
1366
|
+
async refreshFixes() {
|
|
1367
|
+
try {
|
|
1368
|
+
return await this._loader.loadSync();
|
|
1369
|
+
} catch (e) {
|
|
1370
|
+
debug(`refreshFixes error: ${e}`);
|
|
1371
|
+
return [];
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
// ─── Effectiveness ──────────────────────────────────────────────────────
|
|
1375
|
+
/** Get effectiveness statistics for all fixes. */
|
|
1376
|
+
getEffectivenessStats() {
|
|
1377
|
+
const stats = {};
|
|
1378
|
+
for (const [fixId, applications] of this._fixApplications) {
|
|
1379
|
+
const successes = this._fixSuccesses.get(fixId) ?? 0;
|
|
1380
|
+
stats[fixId] = {
|
|
1381
|
+
applications,
|
|
1382
|
+
successes,
|
|
1383
|
+
successRate: applications > 0 ? successes / applications : 0
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
return stats;
|
|
1387
|
+
}
|
|
1388
|
+
// ─── Private ────────────────────────────────────────────────────────────
|
|
1389
|
+
_trackSuccess(ctx) {
|
|
1390
|
+
for (const result of ctx.appliedFixes) {
|
|
1391
|
+
if (result.applied && result.fixId) {
|
|
1392
|
+
this._fixApplications.set(
|
|
1393
|
+
result.fixId,
|
|
1394
|
+
(this._fixApplications.get(result.fixId) ?? 0) + 1
|
|
1395
|
+
);
|
|
1396
|
+
this._fixSuccesses.set(
|
|
1397
|
+
result.fixId,
|
|
1398
|
+
(this._fixSuccesses.get(result.fixId) ?? 0) + 1
|
|
1399
|
+
);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
_trackFailure(ctx) {
|
|
1404
|
+
for (const result of ctx.appliedFixes) {
|
|
1405
|
+
if (result.applied && result.fixId) {
|
|
1406
|
+
this._fixApplications.set(
|
|
1407
|
+
result.fixId,
|
|
1408
|
+
(this._fixApplications.get(result.fixId) ?? 0) + 1
|
|
1409
|
+
);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
});
|
|
1416
|
+
|
|
1
1417
|
// src/config.ts
|
|
2
1418
|
var DEFAULT_ENDPOINT = "https://app.risicare.ai";
|
|
3
1419
|
function resolveConfig(config) {
|
|
@@ -403,48 +1819,8 @@ function shouldSample(traceId, sampleRate) {
|
|
|
403
1819
|
return hash / 4294967295 < sampleRate;
|
|
404
1820
|
}
|
|
405
1821
|
|
|
406
|
-
// src/globals.ts
|
|
407
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
408
|
-
var G = globalThis;
|
|
409
|
-
var PREFIX = "__risicare_";
|
|
410
|
-
function getClient() {
|
|
411
|
-
return G[PREFIX + "client"];
|
|
412
|
-
}
|
|
413
|
-
function setClient(client) {
|
|
414
|
-
G[PREFIX + "client"] = client;
|
|
415
|
-
}
|
|
416
|
-
function getTracer() {
|
|
417
|
-
return G[PREFIX + "tracer"];
|
|
418
|
-
}
|
|
419
|
-
function setTracer(tracer) {
|
|
420
|
-
G[PREFIX + "tracer"] = tracer;
|
|
421
|
-
}
|
|
422
|
-
function getContextStorage() {
|
|
423
|
-
if (!G[PREFIX + "ctx"]) {
|
|
424
|
-
G[PREFIX + "ctx"] = new AsyncLocalStorage();
|
|
425
|
-
}
|
|
426
|
-
return G[PREFIX + "ctx"];
|
|
427
|
-
}
|
|
428
|
-
function getRegistry() {
|
|
429
|
-
if (!G[PREFIX + "registry"]) {
|
|
430
|
-
G[PREFIX + "registry"] = /* @__PURE__ */ new Map();
|
|
431
|
-
}
|
|
432
|
-
return G[PREFIX + "registry"];
|
|
433
|
-
}
|
|
434
|
-
function getOpCount() {
|
|
435
|
-
return G[PREFIX + "opcount"] ?? 0;
|
|
436
|
-
}
|
|
437
|
-
function setOpCount(n) {
|
|
438
|
-
G[PREFIX + "opcount"] = n;
|
|
439
|
-
}
|
|
440
|
-
function getDebug() {
|
|
441
|
-
return G[PREFIX + "debug"] ?? false;
|
|
442
|
-
}
|
|
443
|
-
function setDebugFlag(enabled) {
|
|
444
|
-
G[PREFIX + "debug"] = enabled;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
1822
|
// src/context/storage.ts
|
|
1823
|
+
init_globals();
|
|
448
1824
|
function storage() {
|
|
449
1825
|
return getContextStorage();
|
|
450
1826
|
}
|
|
@@ -546,7 +1922,8 @@ var Tracer = class {
|
|
|
546
1922
|
const session2 = getCurrentSession();
|
|
547
1923
|
const agent2 = getCurrentAgent();
|
|
548
1924
|
const phase = getCurrentPhase();
|
|
549
|
-
const
|
|
1925
|
+
const ctx = getContext();
|
|
1926
|
+
const traceId = opts.traceId ?? parentSpan?.traceId ?? ctx._rootTraceId ?? generateTraceId();
|
|
550
1927
|
if (!parentSpan && this._sampleRate < 1) {
|
|
551
1928
|
if (!shouldSample(traceId, this._sampleRate)) {
|
|
552
1929
|
return fn(NOOP_SPAN);
|
|
@@ -641,22 +2018,8 @@ var Tracer = class {
|
|
|
641
2018
|
}
|
|
642
2019
|
};
|
|
643
2020
|
|
|
644
|
-
// src/utils/log.ts
|
|
645
|
-
function setDebug(enabled) {
|
|
646
|
-
setDebugFlag(enabled);
|
|
647
|
-
}
|
|
648
|
-
function debug(msg) {
|
|
649
|
-
if (getDebug()) {
|
|
650
|
-
process.stderr.write(`[risicare] ${msg}
|
|
651
|
-
`);
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
function warn(msg) {
|
|
655
|
-
process.stderr.write(`[risicare] WARNING: ${msg}
|
|
656
|
-
`);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
2021
|
// src/exporters/batch.ts
|
|
2022
|
+
init_log();
|
|
660
2023
|
var BatchSpanProcessor = class _BatchSpanProcessor {
|
|
661
2024
|
_exporters;
|
|
662
2025
|
_batchSize;
|
|
@@ -807,6 +2170,7 @@ var BatchSpanProcessor = class _BatchSpanProcessor {
|
|
|
807
2170
|
};
|
|
808
2171
|
|
|
809
2172
|
// src/exporters/http.ts
|
|
2173
|
+
init_log();
|
|
810
2174
|
var SDK_VERSION = "0.1.1";
|
|
811
2175
|
var HttpExporter = class {
|
|
812
2176
|
name = "http";
|
|
@@ -932,6 +2296,8 @@ var ConsoleExporter = class {
|
|
|
932
2296
|
};
|
|
933
2297
|
|
|
934
2298
|
// src/client.ts
|
|
2299
|
+
init_log();
|
|
2300
|
+
init_globals();
|
|
935
2301
|
var RisicareClient = class {
|
|
936
2302
|
config;
|
|
937
2303
|
processor;
|
|
@@ -971,6 +2337,18 @@ var RisicareClient = class {
|
|
|
971
2337
|
traceContent: this.config.traceContent
|
|
972
2338
|
});
|
|
973
2339
|
this.processor.start();
|
|
2340
|
+
if (this.config.apiKey && this.config.enabled) {
|
|
2341
|
+
try {
|
|
2342
|
+
const { initFixRuntime: initFixRuntime2 } = (init_runtime(), __toCommonJS(runtime_exports));
|
|
2343
|
+
initFixRuntime2({
|
|
2344
|
+
apiEndpoint: this.config.endpoint,
|
|
2345
|
+
apiKey: this.config.apiKey,
|
|
2346
|
+
enabled: true,
|
|
2347
|
+
debug: this.config.debug
|
|
2348
|
+
});
|
|
2349
|
+
} catch {
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
974
2352
|
this._registerShutdownHooks();
|
|
975
2353
|
setDebug(this.config.debug);
|
|
976
2354
|
debug(`Initialized: enabled=${this.config.enabled}, endpoint=${this.config.endpoint}`);
|
|
@@ -1022,6 +2400,11 @@ function init(config) {
|
|
|
1022
2400
|
setTracer(client.tracer);
|
|
1023
2401
|
}
|
|
1024
2402
|
async function shutdown() {
|
|
2403
|
+
try {
|
|
2404
|
+
const { shutdownFixRuntime: shutdownFixRuntime2 } = (init_runtime(), __toCommonJS(runtime_exports));
|
|
2405
|
+
shutdownFixRuntime2();
|
|
2406
|
+
} catch {
|
|
2407
|
+
}
|
|
1025
2408
|
const client = getClient();
|
|
1026
2409
|
if (!client) return;
|
|
1027
2410
|
await client.shutdown();
|
|
@@ -1063,12 +2446,39 @@ function getMetrics() {
|
|
|
1063
2446
|
queueUtilization: 0
|
|
1064
2447
|
};
|
|
1065
2448
|
}
|
|
2449
|
+
var _ERROR_DEDUP_TTL_MS = 5 * 60 * 1e3;
|
|
2450
|
+
var _ERROR_DEDUP_MAX = 1e3;
|
|
2451
|
+
var _recentErrors = /* @__PURE__ */ new Map();
|
|
2452
|
+
function _errorFingerprint(err) {
|
|
2453
|
+
const raw = `${err.constructor?.name ?? "Error"}:${String(err.message ?? "").slice(0, 200)}`;
|
|
2454
|
+
const { createHash: createHash2 } = __require("crypto");
|
|
2455
|
+
return createHash2("sha256").update(raw).digest("hex").slice(0, 16);
|
|
2456
|
+
}
|
|
2457
|
+
function _isDuplicateError(fingerprint) {
|
|
2458
|
+
const now = Date.now();
|
|
2459
|
+
for (const [fp, ts] of _recentErrors) {
|
|
2460
|
+
if (now - ts > _ERROR_DEDUP_TTL_MS) _recentErrors.delete(fp);
|
|
2461
|
+
else break;
|
|
2462
|
+
}
|
|
2463
|
+
if (_recentErrors.has(fingerprint)) return true;
|
|
2464
|
+
if (_recentErrors.size >= _ERROR_DEDUP_MAX) {
|
|
2465
|
+
const oldest = _recentErrors.keys().next().value;
|
|
2466
|
+
if (oldest !== void 0) _recentErrors.delete(oldest);
|
|
2467
|
+
}
|
|
2468
|
+
_recentErrors.set(fingerprint, now);
|
|
2469
|
+
return false;
|
|
2470
|
+
}
|
|
1066
2471
|
function reportError(error, options) {
|
|
1067
2472
|
try {
|
|
1068
2473
|
const tracer = getTracer2();
|
|
1069
2474
|
if (!tracer) return;
|
|
1070
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
2475
|
+
const err = error instanceof Error ? error : new Error(String(error ?? "unknown"));
|
|
1071
2476
|
const spanName = options?.name ?? `error:${err.constructor.name}`;
|
|
2477
|
+
const fp = _errorFingerprint(err);
|
|
2478
|
+
if (_isDuplicateError(fp)) {
|
|
2479
|
+
debug(`reportError: duplicate suppressed (fp=${fp.slice(0, 8)})`);
|
|
2480
|
+
return;
|
|
2481
|
+
}
|
|
1072
2482
|
tracer.startSpan({ name: spanName, kind: "internal" /* INTERNAL */ }, (span) => {
|
|
1073
2483
|
span.setStatus("error" /* ERROR */, err.message);
|
|
1074
2484
|
span.setAttribute("error", true);
|
|
@@ -1283,8 +2693,22 @@ function getTraceContext() {
|
|
|
1283
2693
|
const span = getCurrentSpan();
|
|
1284
2694
|
const session2 = getCurrentSession();
|
|
1285
2695
|
const agent2 = getCurrentAgent();
|
|
2696
|
+
let traceId;
|
|
2697
|
+
if (span) {
|
|
2698
|
+
traceId = span.traceId;
|
|
2699
|
+
} else {
|
|
2700
|
+
const ctx = getContext();
|
|
2701
|
+
if (ctx._rootTraceId) {
|
|
2702
|
+
traceId = ctx._rootTraceId;
|
|
2703
|
+
} else {
|
|
2704
|
+
traceId = generateTraceId();
|
|
2705
|
+
if (ctx.session || ctx.agent) {
|
|
2706
|
+
ctx._rootTraceId = traceId;
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
1286
2710
|
return {
|
|
1287
|
-
traceId
|
|
2711
|
+
traceId,
|
|
1288
2712
|
spanId: span?.spanId ?? generateSpanId(),
|
|
1289
2713
|
sessionId: session2?.sessionId,
|
|
1290
2714
|
agentId: agent2?.agentId
|
|
@@ -1352,6 +2776,7 @@ function extractTraceContext(headers) {
|
|
|
1352
2776
|
}
|
|
1353
2777
|
|
|
1354
2778
|
// src/context/registry.ts
|
|
2779
|
+
init_globals();
|
|
1355
2780
|
var DEFAULT_TTL_MS = 6e4;
|
|
1356
2781
|
var MAX_ENTRIES = 1e4;
|
|
1357
2782
|
var CLEANUP_INTERVAL = 100;
|
|
@@ -1421,10 +2846,34 @@ async function* tracedStream(source, options) {
|
|
|
1421
2846
|
}
|
|
1422
2847
|
}
|
|
1423
2848
|
|
|
2849
|
+
// src/context/dedup.ts
|
|
2850
|
+
function suppressProviderInstrumentation(fn) {
|
|
2851
|
+
return runWithContext({ _suppressProviderInstrumentation: true }, fn);
|
|
2852
|
+
}
|
|
2853
|
+
function isProviderInstrumentationSuppressed() {
|
|
2854
|
+
return getContext()._suppressProviderInstrumentation === true;
|
|
2855
|
+
}
|
|
2856
|
+
|
|
1424
2857
|
// src/frameworks/langchain.ts
|
|
1425
2858
|
var RisicareCallbackHandler = class {
|
|
1426
2859
|
name = "RisicareCallbackHandler";
|
|
1427
2860
|
_spans = /* @__PURE__ */ new Map();
|
|
2861
|
+
/**
|
|
2862
|
+
* Run a function with provider instrumentation suppressed.
|
|
2863
|
+
*
|
|
2864
|
+
* Wrap your chain.invoke() call with this to prevent duplicate spans.
|
|
2865
|
+
* The handler creates LLM/tool spans, so provider proxies (patchOpenAI, etc.)
|
|
2866
|
+
* should be suppressed during chain execution.
|
|
2867
|
+
*
|
|
2868
|
+
* @example
|
|
2869
|
+
* const handler = new RisicareCallbackHandler();
|
|
2870
|
+
* const result = await handler.withSuppression(() =>
|
|
2871
|
+
* chain.invoke(input, { callbacks: [handler] })
|
|
2872
|
+
* );
|
|
2873
|
+
*/
|
|
2874
|
+
withSuppression(fn) {
|
|
2875
|
+
return suppressProviderInstrumentation(fn);
|
|
2876
|
+
}
|
|
1428
2877
|
// ── Chain lifecycle ────────────────────────────────────────────────────────
|
|
1429
2878
|
handleChainStart(chain, _inputs, runId, parentRunId) {
|
|
1430
2879
|
const tracer = getTracer2();
|
|
@@ -1602,6 +3051,7 @@ function instrumentLangGraph(graph) {
|
|
|
1602
3051
|
}
|
|
1603
3052
|
|
|
1604
3053
|
// src/frameworks/instructor.ts
|
|
3054
|
+
init_log();
|
|
1605
3055
|
function patchInstructor(client) {
|
|
1606
3056
|
return new Proxy(client, {
|
|
1607
3057
|
get(target, prop, receiver) {
|
|
@@ -1652,14 +3102,6 @@ function patchInstructor(client) {
|
|
|
1652
3102
|
});
|
|
1653
3103
|
}
|
|
1654
3104
|
|
|
1655
|
-
// src/context/dedup.ts
|
|
1656
|
-
function suppressProviderInstrumentation(fn) {
|
|
1657
|
-
return runWithContext({ _suppressProviderInstrumentation: true }, fn);
|
|
1658
|
-
}
|
|
1659
|
-
function isProviderInstrumentationSuppressed() {
|
|
1660
|
-
return getContext()._suppressProviderInstrumentation === true;
|
|
1661
|
-
}
|
|
1662
|
-
|
|
1663
3105
|
// src/frameworks/llamaindex.ts
|
|
1664
3106
|
var RisicareLlamaIndexHandler = class {
|
|
1665
3107
|
_spans = /* @__PURE__ */ new Map();
|
|
@@ -1758,6 +3200,9 @@ var RisicareLlamaIndexHandler = class {
|
|
|
1758
3200
|
return "component";
|
|
1759
3201
|
}
|
|
1760
3202
|
};
|
|
3203
|
+
|
|
3204
|
+
// src/index.ts
|
|
3205
|
+
init_runtime();
|
|
1761
3206
|
export {
|
|
1762
3207
|
AgentRole,
|
|
1763
3208
|
MessageType,
|
|
@@ -1780,12 +3225,14 @@ export {
|
|
|
1780
3225
|
getCurrentSpan,
|
|
1781
3226
|
getCurrentSpanId,
|
|
1782
3227
|
getCurrentTraceId,
|
|
3228
|
+
getFixRuntime,
|
|
1783
3229
|
getMetrics,
|
|
1784
3230
|
getSpanById,
|
|
1785
3231
|
getTraceContent,
|
|
1786
3232
|
getTraceContext,
|
|
1787
3233
|
getTracer2 as getTracer,
|
|
1788
3234
|
init,
|
|
3235
|
+
initFixRuntime,
|
|
1789
3236
|
injectTraceContext,
|
|
1790
3237
|
instrumentLangGraph,
|
|
1791
3238
|
isEnabled,
|
|
@@ -1796,6 +3243,7 @@ export {
|
|
|
1796
3243
|
score,
|
|
1797
3244
|
session,
|
|
1798
3245
|
shutdown,
|
|
3246
|
+
shutdownFixRuntime,
|
|
1799
3247
|
suppressProviderInstrumentation,
|
|
1800
3248
|
traceAct,
|
|
1801
3249
|
traceCoordinate,
|