@burtson-labs/bandit-engine 2.0.58 → 2.0.60
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/chat-NLBCURUN.mjs +16 -0
- package/dist/chat-provider.js.map +1 -1
- package/dist/chat-provider.mjs +5 -5
- package/dist/{chunk-VU5N57QZ.mjs → chunk-3AWAL2YH.mjs} +9 -9
- package/dist/{chunk-KNBWR4DS.mjs → chunk-5WQMMCZQ.mjs} +3 -3
- package/dist/{chunk-QPBG6JQE.mjs → chunk-6QTTNYF2.mjs} +2 -2
- package/dist/{chunk-POTQI33D.mjs → chunk-D55E6ZDV.mjs} +5 -5
- package/dist/{chunk-557E5VZ2.mjs → chunk-EUBVBTB3.mjs} +2 -2
- package/dist/{chunk-7ZDS33S2.mjs → chunk-IPMTNREZ.mjs} +2 -2
- package/dist/{chunk-7ZDS33S2.mjs.map → chunk-IPMTNREZ.mjs.map} +1 -1
- package/dist/{chunk-N7GCS2BH.mjs → chunk-MFDMM5MS.mjs} +388 -22
- package/dist/chunk-MFDMM5MS.mjs.map +1 -0
- package/dist/{chunk-WL7NV4WJ.mjs → chunk-PY7A3J5T.mjs} +4 -4
- package/dist/{chunk-KM7FUWCM.mjs → chunk-SRCCNBHF.mjs} +7 -3
- package/dist/chunk-SRCCNBHF.mjs.map +1 -0
- package/dist/{chunk-UFSEYVRS.mjs → chunk-VTC6AIWY.mjs} +3 -3
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +405 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10 -10
- package/dist/management/management.js +405 -17
- package/dist/management/management.js.map +1 -1
- package/dist/management/management.mjs +8 -8
- package/dist/modals/chat-modal/chat-modal.js.map +1 -1
- package/dist/modals/chat-modal/chat-modal.mjs +4 -4
- package/dist/{modelStore-XWFHNTBT.mjs → modelStore-FBPBG7TI.mjs} +2 -2
- package/dist/{public-BzsEWB08.d.mts → public-nrOOzXCZ.d.mts} +10 -0
- package/dist/{public-BzsEWB08.d.ts → public-nrOOzXCZ.d.ts} +10 -0
- package/dist/public-types.d.mts +1 -1
- package/dist/public-types.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chat-MXC6O7M5.mjs +0 -16
- package/dist/chunk-KM7FUWCM.mjs.map +0 -1
- package/dist/chunk-N7GCS2BH.mjs.map +0 -1
- /package/dist/{chat-MXC6O7M5.mjs.map → chat-NLBCURUN.mjs.map} +0 -0
- /package/dist/{chunk-VU5N57QZ.mjs.map → chunk-3AWAL2YH.mjs.map} +0 -0
- /package/dist/{chunk-KNBWR4DS.mjs.map → chunk-5WQMMCZQ.mjs.map} +0 -0
- /package/dist/{chunk-QPBG6JQE.mjs.map → chunk-6QTTNYF2.mjs.map} +0 -0
- /package/dist/{chunk-POTQI33D.mjs.map → chunk-D55E6ZDV.mjs.map} +0 -0
- /package/dist/{chunk-557E5VZ2.mjs.map → chunk-EUBVBTB3.mjs.map} +0 -0
- /package/dist/{chunk-WL7NV4WJ.mjs.map → chunk-PY7A3J5T.mjs.map} +0 -0
- /package/dist/{chunk-UFSEYVRS.mjs.map → chunk-VTC6AIWY.mjs.map} +0 -0
- /package/dist/{modelStore-XWFHNTBT.mjs.map → modelStore-FBPBG7TI.mjs.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -14862,7 +14862,11 @@ ${listMarkdown}`;
|
|
|
14862
14862
|
href,
|
|
14863
14863
|
target: "_blank",
|
|
14864
14864
|
rel: "noopener noreferrer",
|
|
14865
|
-
style: {
|
|
14865
|
+
style: {
|
|
14866
|
+
color: theme.palette.info?.main ?? theme.palette.primary.main,
|
|
14867
|
+
textDecoration: "underline",
|
|
14868
|
+
textUnderlineOffset: "2px"
|
|
14869
|
+
},
|
|
14866
14870
|
...props,
|
|
14867
14871
|
children
|
|
14868
14872
|
}
|
|
@@ -19107,6 +19111,330 @@ ${sanitize(
|
|
|
19107
19111
|
}
|
|
19108
19112
|
});
|
|
19109
19113
|
|
|
19114
|
+
// src/services/telemetry/otlpExporter.ts
|
|
19115
|
+
function redactSecretsString(s) {
|
|
19116
|
+
return s.replace(/\b(?:sk|tvly|ghp|gho|pk|rk)[-_][A-Za-z0-9]{8,}\b/gi, "[redacted]").replace(/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, "[redacted-jwt]").replace(/\bBearer\s+[A-Za-z0-9._-]{8,}/gi, "Bearer [redacted]");
|
|
19117
|
+
}
|
|
19118
|
+
function resolveTelemetryConfig(opts) {
|
|
19119
|
+
if (!opts.telemetry?.enabled) return null;
|
|
19120
|
+
const endpoint = (opts.telemetry.endpoint ?? "https://otlp.burtson.ai").replace(/\/+$/, "");
|
|
19121
|
+
const headers = { ...opts.telemetry.headers ?? {} };
|
|
19122
|
+
const hasAuth = Object.keys(headers).some((k) => k.toLowerCase() === "authorization");
|
|
19123
|
+
if (!hasAuth && opts.banditApiKey) {
|
|
19124
|
+
headers["Authorization"] = `Bearer ${opts.banditApiKey}`;
|
|
19125
|
+
}
|
|
19126
|
+
const mode = opts.telemetry.mode ?? "metrics+traces";
|
|
19127
|
+
return { endpoint, headers, mode, serviceName: opts.telemetry.serviceName ?? "bandit-web" };
|
|
19128
|
+
}
|
|
19129
|
+
function toAttrs(rec) {
|
|
19130
|
+
const out = [];
|
|
19131
|
+
for (const [key, v] of Object.entries(rec)) {
|
|
19132
|
+
if (v === void 0 || v === null || v === "") continue;
|
|
19133
|
+
if (typeof v === "boolean") out.push({ key, value: { boolValue: v } });
|
|
19134
|
+
else if (typeof v === "number")
|
|
19135
|
+
out.push({ key, value: Number.isInteger(v) ? { intValue: String(v) } : { doubleValue: v } });
|
|
19136
|
+
else out.push({ key, value: { stringValue: v } });
|
|
19137
|
+
}
|
|
19138
|
+
return out;
|
|
19139
|
+
}
|
|
19140
|
+
function hex(bytes) {
|
|
19141
|
+
const arr = new Uint8Array(bytes);
|
|
19142
|
+
if (webCrypto?.getRandomValues) webCrypto.getRandomValues(arr);
|
|
19143
|
+
return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
19144
|
+
}
|
|
19145
|
+
function histogramPoint(value, bounds, attrs, startMs, endMs) {
|
|
19146
|
+
const counts = new Array(bounds.length + 1).fill(0);
|
|
19147
|
+
let idx = bounds.findIndex((b) => value <= b);
|
|
19148
|
+
if (idx === -1) idx = bounds.length;
|
|
19149
|
+
counts[idx] = 1;
|
|
19150
|
+
return {
|
|
19151
|
+
attributes: toAttrs(attrs),
|
|
19152
|
+
startTimeUnixNano: nano(startMs),
|
|
19153
|
+
timeUnixNano: nano(endMs),
|
|
19154
|
+
count: "1",
|
|
19155
|
+
sum: value,
|
|
19156
|
+
bucketCounts: counts.map(String),
|
|
19157
|
+
explicitBounds: bounds
|
|
19158
|
+
};
|
|
19159
|
+
}
|
|
19160
|
+
function sumPoint(value, attrs, startMs, endMs) {
|
|
19161
|
+
return {
|
|
19162
|
+
attributes: toAttrs(attrs),
|
|
19163
|
+
startTimeUnixNano: nano(startMs),
|
|
19164
|
+
timeUnixNano: nano(endMs),
|
|
19165
|
+
asInt: String(Math.round(value))
|
|
19166
|
+
};
|
|
19167
|
+
}
|
|
19168
|
+
var webCrypto, nano, TTFT_BUCKETS, DURATION_BUCKETS, clip, TelemetryExporter;
|
|
19169
|
+
var init_otlpExporter = __esm({
|
|
19170
|
+
"src/services/telemetry/otlpExporter.ts"() {
|
|
19171
|
+
"use strict";
|
|
19172
|
+
init_debugLogger();
|
|
19173
|
+
webCrypto = globalThis.crypto;
|
|
19174
|
+
nano = (ms) => String(Math.round(ms * 1e6));
|
|
19175
|
+
TTFT_BUCKETS = [0.1, 0.25, 0.5, 1, 2, 5, 10, 30];
|
|
19176
|
+
DURATION_BUCKETS = [0.5, 1, 2, 5, 10, 30, 60, 120, 300];
|
|
19177
|
+
clip = (s, n = 120) => redactSecretsString(s.slice(0, n)).slice(0, n);
|
|
19178
|
+
TelemetryExporter = class {
|
|
19179
|
+
cfg;
|
|
19180
|
+
now;
|
|
19181
|
+
traceId = "";
|
|
19182
|
+
turn = null;
|
|
19183
|
+
llm = null;
|
|
19184
|
+
llmFirstChunkMs = 0;
|
|
19185
|
+
openTools = [];
|
|
19186
|
+
completedSpans = [];
|
|
19187
|
+
model = "";
|
|
19188
|
+
turnChunkChars = 0;
|
|
19189
|
+
turnTokens = 0;
|
|
19190
|
+
ttftSeconds = null;
|
|
19191
|
+
constructor(cfg, opts) {
|
|
19192
|
+
this.cfg = cfg;
|
|
19193
|
+
this.now = opts?.now ?? (() => Date.now());
|
|
19194
|
+
}
|
|
19195
|
+
startTurn(goal, model) {
|
|
19196
|
+
this.traceId = hex(16);
|
|
19197
|
+
this.model = model;
|
|
19198
|
+
this.turnChunkChars = 0;
|
|
19199
|
+
this.turnTokens = 0;
|
|
19200
|
+
this.ttftSeconds = null;
|
|
19201
|
+
this.llm = null;
|
|
19202
|
+
this.llmFirstChunkMs = 0;
|
|
19203
|
+
this.openTools = [];
|
|
19204
|
+
this.completedSpans = [];
|
|
19205
|
+
this.turn = {
|
|
19206
|
+
spanId: hex(8),
|
|
19207
|
+
name: "agent.turn",
|
|
19208
|
+
startMs: this.now(),
|
|
19209
|
+
attrs: { "gen_ai.request.model": model, "bandit.turn.goal": clip(goal, 160) }
|
|
19210
|
+
};
|
|
19211
|
+
}
|
|
19212
|
+
/** Fed from the chat turn lifecycle. Best-effort; swallows bad payloads. */
|
|
19213
|
+
onEvent(type, payload) {
|
|
19214
|
+
if (!this.turn) return;
|
|
19215
|
+
try {
|
|
19216
|
+
const p = payload ?? {};
|
|
19217
|
+
switch (type) {
|
|
19218
|
+
case "tool_loop:llm_start":
|
|
19219
|
+
this.llm = {
|
|
19220
|
+
spanId: hex(8),
|
|
19221
|
+
parentSpanId: this.turn.spanId,
|
|
19222
|
+
name: "llm.generate",
|
|
19223
|
+
startMs: this.now(),
|
|
19224
|
+
attrs: { "gen_ai.request.model": this.model }
|
|
19225
|
+
};
|
|
19226
|
+
this.llmFirstChunkMs = 0;
|
|
19227
|
+
break;
|
|
19228
|
+
case "tool_loop:llm_chunk": {
|
|
19229
|
+
const chunk = typeof p.chunk === "string" ? p.chunk : "";
|
|
19230
|
+
if (this.llm && this.llmFirstChunkMs === 0 && chunk.length > 0) {
|
|
19231
|
+
this.llmFirstChunkMs = this.now();
|
|
19232
|
+
const ttft = (this.llmFirstChunkMs - this.llm.startMs) / 1e3;
|
|
19233
|
+
if (this.ttftSeconds === null) this.ttftSeconds = ttft;
|
|
19234
|
+
this.llm.attrs["bandit.llm.ttft_seconds"] = ttft;
|
|
19235
|
+
}
|
|
19236
|
+
this.turnChunkChars += chunk.length;
|
|
19237
|
+
this.turnTokens = Math.floor(this.turnChunkChars / 4);
|
|
19238
|
+
break;
|
|
19239
|
+
}
|
|
19240
|
+
case "tool_loop:llm_response":
|
|
19241
|
+
if (this.llm) {
|
|
19242
|
+
this.llm.endMs = this.now();
|
|
19243
|
+
if (typeof p.responseLength === "number") this.llm.attrs["bandit.llm.response_chars"] = p.responseLength;
|
|
19244
|
+
this.completedSpans.push(this.llm);
|
|
19245
|
+
this.llm = null;
|
|
19246
|
+
}
|
|
19247
|
+
break;
|
|
19248
|
+
case "tool_loop:tool_execute": {
|
|
19249
|
+
const name = typeof p.name === "string" ? p.name : "tool";
|
|
19250
|
+
const params = p.params ?? {};
|
|
19251
|
+
const primary = params.query ?? params.url ?? params.prompt ?? params.topic ?? "";
|
|
19252
|
+
this.openTools.push({
|
|
19253
|
+
spanId: hex(8),
|
|
19254
|
+
parentSpanId: this.turn.spanId,
|
|
19255
|
+
name: `tool.${name}`,
|
|
19256
|
+
startMs: this.now(),
|
|
19257
|
+
attrs: { "bandit.tool.name": name, "bandit.tool.primary": primary ? clip(primary) : void 0 }
|
|
19258
|
+
});
|
|
19259
|
+
break;
|
|
19260
|
+
}
|
|
19261
|
+
case "tool_loop:tool_result":
|
|
19262
|
+
case "tool_loop:tool_error": {
|
|
19263
|
+
const name = typeof p.name === "string" ? p.name : void 0;
|
|
19264
|
+
const span = this.takeOpenTool(name);
|
|
19265
|
+
if (span) {
|
|
19266
|
+
span.endMs = this.now();
|
|
19267
|
+
if (type === "tool_loop:tool_error" || p.isError === true) span.error = "tool error";
|
|
19268
|
+
this.completedSpans.push(span);
|
|
19269
|
+
}
|
|
19270
|
+
break;
|
|
19271
|
+
}
|
|
19272
|
+
}
|
|
19273
|
+
} catch {
|
|
19274
|
+
}
|
|
19275
|
+
}
|
|
19276
|
+
takeOpenTool(name) {
|
|
19277
|
+
if (name) {
|
|
19278
|
+
for (let i = this.openTools.length - 1; i >= 0; i -= 1) {
|
|
19279
|
+
if (this.openTools[i].name === `tool.${name}`) return this.openTools.splice(i, 1)[0];
|
|
19280
|
+
}
|
|
19281
|
+
}
|
|
19282
|
+
return this.openTools.shift();
|
|
19283
|
+
}
|
|
19284
|
+
/** Close the turn, build OTLP traces + metrics, and flush. Never rejects. */
|
|
19285
|
+
async endTurn(outcome) {
|
|
19286
|
+
const turn = this.turn;
|
|
19287
|
+
if (!turn) return;
|
|
19288
|
+
this.turn = null;
|
|
19289
|
+
const end = this.now();
|
|
19290
|
+
if (this.llm && !this.llm.endMs) {
|
|
19291
|
+
this.llm.endMs = end;
|
|
19292
|
+
this.completedSpans.push(this.llm);
|
|
19293
|
+
this.llm = null;
|
|
19294
|
+
}
|
|
19295
|
+
for (const t of this.openTools.splice(0)) {
|
|
19296
|
+
t.endMs = end;
|
|
19297
|
+
t.error = t.error ?? "incomplete";
|
|
19298
|
+
this.completedSpans.push(t);
|
|
19299
|
+
}
|
|
19300
|
+
turn.endMs = end;
|
|
19301
|
+
if (outcome?.error) turn.error = outcome.error;
|
|
19302
|
+
const traceId = this.traceId;
|
|
19303
|
+
const spans = [turn, ...this.completedSpans];
|
|
19304
|
+
const jobs = [];
|
|
19305
|
+
if (this.cfg.mode === "metrics+traces") jobs.push(this.post("/v1/traces", this.buildTraces(traceId, spans)));
|
|
19306
|
+
jobs.push(this.post("/v1/metrics", this.buildMetrics(turn)));
|
|
19307
|
+
try {
|
|
19308
|
+
await Promise.all(jobs);
|
|
19309
|
+
} catch {
|
|
19310
|
+
}
|
|
19311
|
+
}
|
|
19312
|
+
buildTraces(traceId, spans) {
|
|
19313
|
+
return {
|
|
19314
|
+
resourceSpans: [
|
|
19315
|
+
{
|
|
19316
|
+
resource: { attributes: toAttrs({ "service.name": this.cfg.serviceName }) },
|
|
19317
|
+
scopeSpans: [
|
|
19318
|
+
{
|
|
19319
|
+
scope: { name: this.cfg.serviceName },
|
|
19320
|
+
spans: spans.map((s) => ({
|
|
19321
|
+
traceId,
|
|
19322
|
+
spanId: s.spanId,
|
|
19323
|
+
parentSpanId: s.parentSpanId,
|
|
19324
|
+
name: s.name,
|
|
19325
|
+
kind: 1,
|
|
19326
|
+
startTimeUnixNano: nano(s.startMs),
|
|
19327
|
+
endTimeUnixNano: nano(s.endMs ?? s.startMs),
|
|
19328
|
+
attributes: toAttrs(s.attrs),
|
|
19329
|
+
status: s.error ? { code: 2, message: s.error.slice(0, 200) } : { code: 1 }
|
|
19330
|
+
}))
|
|
19331
|
+
}
|
|
19332
|
+
]
|
|
19333
|
+
}
|
|
19334
|
+
]
|
|
19335
|
+
};
|
|
19336
|
+
}
|
|
19337
|
+
buildMetrics(turn) {
|
|
19338
|
+
const start = turn.startMs;
|
|
19339
|
+
const end = turn.endMs ?? this.now();
|
|
19340
|
+
const metrics = [];
|
|
19341
|
+
if (this.turnTokens > 0) {
|
|
19342
|
+
metrics.push({
|
|
19343
|
+
name: "bandit.llm.tokens",
|
|
19344
|
+
sum: {
|
|
19345
|
+
aggregationTemporality: 1,
|
|
19346
|
+
isMonotonic: true,
|
|
19347
|
+
dataPoints: [sumPoint(this.turnTokens, { type: "output", "gen_ai.request.model": this.model }, start, end)]
|
|
19348
|
+
}
|
|
19349
|
+
});
|
|
19350
|
+
}
|
|
19351
|
+
if (this.ttftSeconds !== null) {
|
|
19352
|
+
metrics.push({
|
|
19353
|
+
name: "bandit.llm.ttft",
|
|
19354
|
+
unit: "s",
|
|
19355
|
+
histogram: {
|
|
19356
|
+
aggregationTemporality: 1,
|
|
19357
|
+
dataPoints: [histogramPoint(this.ttftSeconds, TTFT_BUCKETS, { "gen_ai.request.model": this.model }, start, end)]
|
|
19358
|
+
}
|
|
19359
|
+
});
|
|
19360
|
+
}
|
|
19361
|
+
metrics.push({
|
|
19362
|
+
name: "bandit.turn.duration",
|
|
19363
|
+
unit: "s",
|
|
19364
|
+
histogram: {
|
|
19365
|
+
aggregationTemporality: 1,
|
|
19366
|
+
dataPoints: [histogramPoint((end - start) / 1e3, DURATION_BUCKETS, { "gen_ai.request.model": this.model }, start, end)]
|
|
19367
|
+
}
|
|
19368
|
+
});
|
|
19369
|
+
return {
|
|
19370
|
+
resourceMetrics: [
|
|
19371
|
+
{
|
|
19372
|
+
resource: { attributes: toAttrs({ "service.name": this.cfg.serviceName }) },
|
|
19373
|
+
scopeMetrics: [{ scope: { name: this.cfg.serviceName }, metrics }]
|
|
19374
|
+
}
|
|
19375
|
+
]
|
|
19376
|
+
};
|
|
19377
|
+
}
|
|
19378
|
+
async post(path, body) {
|
|
19379
|
+
const doFetch = globalThis.fetch;
|
|
19380
|
+
if (!doFetch) return;
|
|
19381
|
+
const ctrl = new AbortController();
|
|
19382
|
+
const timer = setTimeout(() => ctrl.abort(), 4e3);
|
|
19383
|
+
try {
|
|
19384
|
+
await doFetch(`${this.cfg.endpoint}${path}`, {
|
|
19385
|
+
method: "POST",
|
|
19386
|
+
headers: { "Content-Type": "application/json", ...this.cfg.headers },
|
|
19387
|
+
body: JSON.stringify(body),
|
|
19388
|
+
signal: ctrl.signal
|
|
19389
|
+
});
|
|
19390
|
+
} catch (e) {
|
|
19391
|
+
debugLogger.debug("[telemetry] OTLP post failed", {
|
|
19392
|
+
path,
|
|
19393
|
+
error: e instanceof Error ? e.message : String(e)
|
|
19394
|
+
});
|
|
19395
|
+
} finally {
|
|
19396
|
+
clearTimeout(timer);
|
|
19397
|
+
}
|
|
19398
|
+
}
|
|
19399
|
+
};
|
|
19400
|
+
}
|
|
19401
|
+
});
|
|
19402
|
+
|
|
19403
|
+
// src/services/telemetry/index.ts
|
|
19404
|
+
function syncTelemetry() {
|
|
19405
|
+
try {
|
|
19406
|
+
const settings = usePackageSettingsStore.getState().settings;
|
|
19407
|
+
const cfg = resolveTelemetryConfig({
|
|
19408
|
+
telemetry: settings?.telemetry,
|
|
19409
|
+
banditApiKey: authenticationService.getToken() ?? void 0
|
|
19410
|
+
});
|
|
19411
|
+
active = cfg ? new TelemetryExporter(cfg) : null;
|
|
19412
|
+
} catch {
|
|
19413
|
+
active = null;
|
|
19414
|
+
}
|
|
19415
|
+
return active !== null;
|
|
19416
|
+
}
|
|
19417
|
+
function telemetryStartTurn(goal, model) {
|
|
19418
|
+
active?.startTurn(goal, model);
|
|
19419
|
+
}
|
|
19420
|
+
function telemetryEvent(type, payload) {
|
|
19421
|
+
active?.onEvent(type, payload);
|
|
19422
|
+
}
|
|
19423
|
+
function telemetryEndTurn(outcome) {
|
|
19424
|
+
void active?.endTurn(outcome);
|
|
19425
|
+
}
|
|
19426
|
+
var active;
|
|
19427
|
+
var init_telemetry = __esm({
|
|
19428
|
+
"src/services/telemetry/index.ts"() {
|
|
19429
|
+
"use strict";
|
|
19430
|
+
init_otlpExporter();
|
|
19431
|
+
init_packageSettingsStore();
|
|
19432
|
+
init_authenticationService();
|
|
19433
|
+
init_otlpExporter();
|
|
19434
|
+
active = null;
|
|
19435
|
+
}
|
|
19436
|
+
});
|
|
19437
|
+
|
|
19110
19438
|
// src/chat/hooks/useMemoryEnhancer.tsx
|
|
19111
19439
|
var import_rxjs20, MEMORY_LIMIT, MIN_MEMORY_WORDS, MERGE_THRESHOLD, REJECT_ECHO_THRESHOLD, REJECT_DUPLICATE_THRESHOLD, CONTEXTUAL_DIVERGENCE_THRESHOLD, normalizeText, isStructurallyDuplicate, isAboutBandit, hasEngagementValue, isMemoryTooShortOrGeneric, isPersonalText, mergeMemory, isVoiceShifted, sanitizeMemory, sanitizeMemoryText, shouldAcceptMemory, isContextuallyDivergent, useMemoryEnhancer;
|
|
19112
19440
|
var init_useMemoryEnhancer = __esm({
|
|
@@ -19770,7 +20098,14 @@ var init_mcpService = __esm({
|
|
|
19770
20098
|
requestOptions.body = JSON.stringify(toolCall.parameters);
|
|
19771
20099
|
}
|
|
19772
20100
|
}
|
|
19773
|
-
const
|
|
20101
|
+
const controller = new AbortController();
|
|
20102
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
20103
|
+
let response;
|
|
20104
|
+
try {
|
|
20105
|
+
response = await fetch(url, { ...requestOptions, signal: controller.signal });
|
|
20106
|
+
} finally {
|
|
20107
|
+
clearTimeout(timeoutId);
|
|
20108
|
+
}
|
|
19774
20109
|
const data = await response.json();
|
|
19775
20110
|
if (!response.ok) {
|
|
19776
20111
|
debugLogger.error("MCP tool execution failed", {
|
|
@@ -19841,6 +20176,7 @@ var init_useAIProvider = __esm({
|
|
|
19841
20176
|
import_react22 = require("react");
|
|
19842
20177
|
init_knowledgeStore();
|
|
19843
20178
|
init_aiProviderStore();
|
|
20179
|
+
init_telemetry();
|
|
19844
20180
|
init_conversationStore();
|
|
19845
20181
|
init_useMemoryEnhancer();
|
|
19846
20182
|
init_useVectorStore();
|
|
@@ -20605,6 +20941,9 @@ ${protocol}`;
|
|
|
20605
20941
|
setStreamBuffer(latestDisplayMessage);
|
|
20606
20942
|
}, delay);
|
|
20607
20943
|
};
|
|
20944
|
+
syncTelemetry();
|
|
20945
|
+
telemetryStartTurn(question, modelName);
|
|
20946
|
+
telemetryEvent("tool_loop:llm_start");
|
|
20608
20947
|
const stream = provider.chat(request);
|
|
20609
20948
|
const initialPlaceholderQuestion = lastEntry?.question;
|
|
20610
20949
|
lastPartialRef.current = { text: "", images: [...imageList], usedDocs, question };
|
|
@@ -20618,7 +20957,10 @@ ${protocol}`;
|
|
|
20618
20957
|
const sub = stream.subscribe({
|
|
20619
20958
|
next: (data) => {
|
|
20620
20959
|
if (!data?.message?.content && !data?.message?.tool_calls) return;
|
|
20621
|
-
if (data.message.content)
|
|
20960
|
+
if (data.message.content) {
|
|
20961
|
+
fullMessage += data.message.content;
|
|
20962
|
+
telemetryEvent("tool_loop:llm_chunk", { chunk: data.message.content });
|
|
20963
|
+
}
|
|
20622
20964
|
const inThinkBlock = /<think>/.test(fullMessage) && !/<think>[\s\S]*<\/think>/.test(fullMessage);
|
|
20623
20965
|
setIsThinking?.(inThinkBlock);
|
|
20624
20966
|
const visibleMessage = stripThinking(fullMessage);
|
|
@@ -20650,6 +20992,7 @@ ${protocol}`;
|
|
|
20650
20992
|
setIsThinking?.(false);
|
|
20651
20993
|
setPendingMessage(null);
|
|
20652
20994
|
setLogoVisible(false);
|
|
20995
|
+
telemetryEndTurn({ error: err?.message || "stream error" });
|
|
20653
20996
|
if (onError) {
|
|
20654
20997
|
onError(err);
|
|
20655
20998
|
}
|
|
@@ -20657,6 +21000,7 @@ ${protocol}`;
|
|
|
20657
21000
|
complete: async () => {
|
|
20658
21001
|
try {
|
|
20659
21002
|
setIsThinking?.(false);
|
|
21003
|
+
telemetryEvent("tool_loop:llm_response", { responseLength: fullMessage.length });
|
|
20660
21004
|
latestDisplayMessage = stripThinking(fullMessage);
|
|
20661
21005
|
if (!sawToolBlock) {
|
|
20662
21006
|
flushNow();
|
|
@@ -20665,6 +21009,7 @@ ${protocol}`;
|
|
|
20665
21009
|
let enhancedMessage = fullMessage;
|
|
20666
21010
|
const summarizableResults = [];
|
|
20667
21011
|
const inlineImageBlocks = [];
|
|
21012
|
+
const collectedSources = [];
|
|
20668
21013
|
if (toolCallMatches && toolCallMatches.length > 0 && mcpToolsAvailable) {
|
|
20669
21014
|
debugLogger.info("Detected tool calls in AI response", {
|
|
20670
21015
|
toolCallCount: toolCallMatches.length,
|
|
@@ -20699,10 +21044,21 @@ ${protocol}`;
|
|
|
20699
21044
|
});
|
|
20700
21045
|
const placeholderToken = `<<TOOL_LOADING_${functionName}_${Math.random().toString(36).slice(2)}>>`;
|
|
20701
21046
|
enhancedMessage = enhancedMessage.replace(match, placeholderToken);
|
|
21047
|
+
clearFlushTimer();
|
|
21048
|
+
const toolStatus = functionName === "web_search" || functionName === "web-search" ? "Searching the web\u2026" : functionName === "web_fetch" || functionName === "web-fetch" ? "Reading the page\u2026" : functionName === "image_generation" || functionName === "image-generation" ? "Generating the image\u2026" : "Working on it\u2026";
|
|
21049
|
+
const toolPreamble = stripToolBlocks(fullMessage).trim();
|
|
21050
|
+
setStreamBuffer(toolPreamble ? `${toolPreamble}
|
|
21051
|
+
|
|
21052
|
+
_${toolStatus}_` : `_${toolStatus}_`);
|
|
21053
|
+
telemetryEvent("tool_loop:tool_execute", { name: functionName, params: parsedParams });
|
|
20702
21054
|
const result = await executeMCPTool({
|
|
20703
21055
|
toolName: functionName,
|
|
20704
21056
|
parameters: parsedParams
|
|
20705
21057
|
});
|
|
21058
|
+
telemetryEvent(result.success ? "tool_loop:tool_result" : "tool_loop:tool_error", {
|
|
21059
|
+
name: functionName,
|
|
21060
|
+
isError: !result.success
|
|
21061
|
+
});
|
|
20706
21062
|
let resultText = "";
|
|
20707
21063
|
if (result.success) {
|
|
20708
21064
|
if (functionName === "web_search" || functionName === "web-search") {
|
|
@@ -20716,18 +21072,16 @@ ${protocol}`;
|
|
|
20716
21072
|
blocks.push(
|
|
20717
21073
|
items.slice(0, 6).map((item, index) => {
|
|
20718
21074
|
const title = item.title?.trim() || "Untitled";
|
|
20719
|
-
const url = item.url?.trim();
|
|
21075
|
+
const url = item.url?.trim() || "";
|
|
20720
21076
|
const snippet = item.content?.trim();
|
|
20721
|
-
|
|
20722
|
-
|
|
20723
|
-
${url}`;
|
|
21077
|
+
if (url) collectedSources.push({ title, url });
|
|
21078
|
+
let line = url ? `${index + 1}. [${title}](${url})` : `${index + 1}. ${title}`;
|
|
20724
21079
|
if (snippet) {
|
|
20725
21080
|
const truncated = snippet.length > 300 ? `${snippet.slice(0, 300)}\u2026` : snippet;
|
|
20726
|
-
line += `
|
|
20727
|
-
${truncated}`;
|
|
21081
|
+
line += ` \u2014 ${truncated}`;
|
|
20728
21082
|
}
|
|
20729
21083
|
return line;
|
|
20730
|
-
}).join("\n
|
|
21084
|
+
}).join("\n")
|
|
20731
21085
|
);
|
|
20732
21086
|
}
|
|
20733
21087
|
resultText = blocks.length ? blocks.join("\n\n") : `No results found${search.query ? ` for "${search.query}"` : ""}.`;
|
|
@@ -20809,7 +21163,7 @@ ${r.output}`).join("\n\n");
|
|
|
20809
21163
|
|
|
20810
21164
|
${toolResultsText}
|
|
20811
21165
|
|
|
20812
|
-
Using these results together with your own knowledge, answer my original question concisely and in a natural, well-formatted way.
|
|
21166
|
+
Using these results together with your own knowledge, answer my original question concisely and in a natural, well-formatted way. Reference sources by name where relevant, but do NOT paste raw URLs or a list of links \u2014 a clean Sources section is added automatically below your answer. Do NOT output tool_code or call any tools again.`
|
|
20813
21167
|
}
|
|
20814
21168
|
];
|
|
20815
21169
|
const summaryRequest = {
|
|
@@ -20819,9 +21173,22 @@ Using these results together with your own knowledge, answer my original questio
|
|
|
20819
21173
|
options: { num_predict: tokenLimit + 250 }
|
|
20820
21174
|
};
|
|
20821
21175
|
clearFlushTimer();
|
|
20822
|
-
|
|
21176
|
+
const summaryPreamble = stripToolBlocks(fullMessage).trim();
|
|
21177
|
+
setStreamBuffer(
|
|
21178
|
+
summaryPreamble ? `${summaryPreamble}
|
|
21179
|
+
|
|
21180
|
+
_Writing the answer\u2026_` : "_Writing the answer\u2026_"
|
|
21181
|
+
);
|
|
20823
21182
|
const summaryText = await new Promise((resolve) => {
|
|
20824
21183
|
let acc = "";
|
|
21184
|
+
let settled = false;
|
|
21185
|
+
let timer;
|
|
21186
|
+
const done = (value) => {
|
|
21187
|
+
if (settled) return;
|
|
21188
|
+
settled = true;
|
|
21189
|
+
if (timer) clearTimeout(timer);
|
|
21190
|
+
resolve(value);
|
|
21191
|
+
};
|
|
20825
21192
|
const summarySub = provider.chat(summaryRequest).subscribe({
|
|
20826
21193
|
next: (data) => {
|
|
20827
21194
|
if (data?.message?.content) {
|
|
@@ -20836,14 +21203,33 @@ Using these results together with your own knowledge, answer my original questio
|
|
|
20836
21203
|
debugLogger.error("Summarization pass failed", {
|
|
20837
21204
|
error: summaryErr instanceof Error ? summaryErr.message : String(summaryErr)
|
|
20838
21205
|
});
|
|
20839
|
-
|
|
21206
|
+
done("");
|
|
20840
21207
|
},
|
|
20841
|
-
complete: () =>
|
|
21208
|
+
complete: () => done(stripThinking(acc).trim())
|
|
20842
21209
|
});
|
|
20843
21210
|
currentSubRef.current = summarySub;
|
|
21211
|
+
timer = setTimeout(() => {
|
|
21212
|
+
debugLogger.warn("Summarization pass timed out; using inline tool output");
|
|
21213
|
+
try {
|
|
21214
|
+
summarySub.unsubscribe();
|
|
21215
|
+
} catch {
|
|
21216
|
+
}
|
|
21217
|
+
done("");
|
|
21218
|
+
}, 3e4);
|
|
20844
21219
|
});
|
|
20845
21220
|
if (summaryText.trim()) {
|
|
20846
|
-
|
|
21221
|
+
const sourcesMd = collectedSources.length ? `
|
|
21222
|
+
|
|
21223
|
+
**Sources**
|
|
21224
|
+
${collectedSources.slice(0, 6).map((s) => {
|
|
21225
|
+
let domain = s.url;
|
|
21226
|
+
try {
|
|
21227
|
+
domain = new URL(s.url).hostname.replace(/^www\./, "");
|
|
21228
|
+
} catch {
|
|
21229
|
+
}
|
|
21230
|
+
return `- [${s.title || domain}](${s.url}) \u2014 ${domain}`;
|
|
21231
|
+
}).join("\n")}` : "";
|
|
21232
|
+
enhancedMessage = summaryText + sourcesMd + (inlineImageBlocks.length ? `
|
|
20847
21233
|
|
|
20848
21234
|
${inlineImageBlocks.join("\n\n")}` : "");
|
|
20849
21235
|
}
|
|
@@ -20892,6 +21278,7 @@ ${inlineImageBlocks.join("\n\n")}` : "");
|
|
|
20892
21278
|
}
|
|
20893
21279
|
setInputValue("");
|
|
20894
21280
|
setPastedImages([]);
|
|
21281
|
+
telemetryEndTurn();
|
|
20895
21282
|
setTimeout(() => {
|
|
20896
21283
|
clearFlushTimer();
|
|
20897
21284
|
setPendingMessage(null);
|
|
@@ -20907,6 +21294,7 @@ ${inlineImageBlocks.join("\n\n")}` : "");
|
|
|
20907
21294
|
overrideComponentStatus("Idle");
|
|
20908
21295
|
setIsSubmitting(false);
|
|
20909
21296
|
setIsStreaming(false);
|
|
21297
|
+
telemetryEndTurn({ error: e instanceof Error ? e.message : String(e) });
|
|
20910
21298
|
}
|
|
20911
21299
|
}
|
|
20912
21300
|
});
|
|
@@ -23765,10 +24153,10 @@ var init_enhanced_mobile_conversations_modal = __esm({
|
|
|
23765
24153
|
(0, import_react30.useEffect)(() => {
|
|
23766
24154
|
setDeletedConversationIds((prev) => {
|
|
23767
24155
|
let changed = false;
|
|
23768
|
-
const
|
|
24156
|
+
const active2 = new Set(conversations.map((conv) => conv.id));
|
|
23769
24157
|
const next = /* @__PURE__ */ new Set();
|
|
23770
24158
|
prev.forEach((id) => {
|
|
23771
|
-
if (
|
|
24159
|
+
if (active2.has(id)) {
|
|
23772
24160
|
next.add(id);
|
|
23773
24161
|
} else {
|
|
23774
24162
|
changed = true;
|