@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.mjs
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
2
|
chat_default
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MFDMM5MS.mjs";
|
|
4
4
|
import {
|
|
5
5
|
chat_provider_default
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-D55E6ZDV.mjs";
|
|
7
7
|
import "./chunk-ONQMRE2G.mjs";
|
|
8
8
|
import {
|
|
9
9
|
management_default,
|
|
10
10
|
useGatewayHealth,
|
|
11
11
|
useGatewayMemory,
|
|
12
12
|
useGatewayModels
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-3AWAL2YH.mjs";
|
|
14
|
+
import "./chunk-SRCCNBHF.mjs";
|
|
15
|
+
import "./chunk-VTC6AIWY.mjs";
|
|
16
|
+
import "./chunk-6QTTNYF2.mjs";
|
|
17
17
|
import {
|
|
18
18
|
defineCustomElement
|
|
19
19
|
} from "./chunk-IXIM7BNO.mjs";
|
|
20
20
|
import {
|
|
21
21
|
chat_modal_default
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-PY7A3J5T.mjs";
|
|
23
23
|
import {
|
|
24
24
|
FeedbackButton,
|
|
25
25
|
FeedbackModal,
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
useTTS,
|
|
38
38
|
useVoiceStore,
|
|
39
39
|
voiceService
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-5WQMMCZQ.mjs";
|
|
41
41
|
import {
|
|
42
42
|
DEFAULT_TIER_FEATURES,
|
|
43
43
|
FeatureFlagContext,
|
|
@@ -57,10 +57,10 @@ import {
|
|
|
57
57
|
useVectorStore,
|
|
58
58
|
vectorDatabaseService,
|
|
59
59
|
vectorMigrationService
|
|
60
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-EUBVBTB3.mjs";
|
|
61
61
|
import {
|
|
62
62
|
usePackageSettingsStore
|
|
63
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-IPMTNREZ.mjs";
|
|
64
64
|
import "./chunk-H3BYFEIE.mjs";
|
|
65
65
|
import {
|
|
66
66
|
DebugLogger,
|
|
@@ -19448,7 +19448,11 @@ ${listMarkdown}`;
|
|
|
19448
19448
|
href,
|
|
19449
19449
|
target: "_blank",
|
|
19450
19450
|
rel: "noopener noreferrer",
|
|
19451
|
-
style: {
|
|
19451
|
+
style: {
|
|
19452
|
+
color: theme.palette.info?.main ?? theme.palette.primary.main,
|
|
19453
|
+
textDecoration: "underline",
|
|
19454
|
+
textUnderlineOffset: "2px"
|
|
19455
|
+
},
|
|
19452
19456
|
...props,
|
|
19453
19457
|
children
|
|
19454
19458
|
}
|
|
@@ -21346,6 +21350,330 @@ ${sanitize(
|
|
|
21346
21350
|
}
|
|
21347
21351
|
});
|
|
21348
21352
|
|
|
21353
|
+
// src/services/telemetry/otlpExporter.ts
|
|
21354
|
+
function redactSecretsString(s) {
|
|
21355
|
+
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]");
|
|
21356
|
+
}
|
|
21357
|
+
function resolveTelemetryConfig(opts) {
|
|
21358
|
+
if (!opts.telemetry?.enabled) return null;
|
|
21359
|
+
const endpoint = (opts.telemetry.endpoint ?? "https://otlp.burtson.ai").replace(/\/+$/, "");
|
|
21360
|
+
const headers = { ...opts.telemetry.headers ?? {} };
|
|
21361
|
+
const hasAuth = Object.keys(headers).some((k) => k.toLowerCase() === "authorization");
|
|
21362
|
+
if (!hasAuth && opts.banditApiKey) {
|
|
21363
|
+
headers["Authorization"] = `Bearer ${opts.banditApiKey}`;
|
|
21364
|
+
}
|
|
21365
|
+
const mode = opts.telemetry.mode ?? "metrics+traces";
|
|
21366
|
+
return { endpoint, headers, mode, serviceName: opts.telemetry.serviceName ?? "bandit-web" };
|
|
21367
|
+
}
|
|
21368
|
+
function toAttrs(rec) {
|
|
21369
|
+
const out = [];
|
|
21370
|
+
for (const [key, v] of Object.entries(rec)) {
|
|
21371
|
+
if (v === void 0 || v === null || v === "") continue;
|
|
21372
|
+
if (typeof v === "boolean") out.push({ key, value: { boolValue: v } });
|
|
21373
|
+
else if (typeof v === "number")
|
|
21374
|
+
out.push({ key, value: Number.isInteger(v) ? { intValue: String(v) } : { doubleValue: v } });
|
|
21375
|
+
else out.push({ key, value: { stringValue: v } });
|
|
21376
|
+
}
|
|
21377
|
+
return out;
|
|
21378
|
+
}
|
|
21379
|
+
function hex(bytes) {
|
|
21380
|
+
const arr = new Uint8Array(bytes);
|
|
21381
|
+
if (webCrypto?.getRandomValues) webCrypto.getRandomValues(arr);
|
|
21382
|
+
return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
21383
|
+
}
|
|
21384
|
+
function histogramPoint(value, bounds, attrs, startMs, endMs) {
|
|
21385
|
+
const counts = new Array(bounds.length + 1).fill(0);
|
|
21386
|
+
let idx = bounds.findIndex((b) => value <= b);
|
|
21387
|
+
if (idx === -1) idx = bounds.length;
|
|
21388
|
+
counts[idx] = 1;
|
|
21389
|
+
return {
|
|
21390
|
+
attributes: toAttrs(attrs),
|
|
21391
|
+
startTimeUnixNano: nano(startMs),
|
|
21392
|
+
timeUnixNano: nano(endMs),
|
|
21393
|
+
count: "1",
|
|
21394
|
+
sum: value,
|
|
21395
|
+
bucketCounts: counts.map(String),
|
|
21396
|
+
explicitBounds: bounds
|
|
21397
|
+
};
|
|
21398
|
+
}
|
|
21399
|
+
function sumPoint(value, attrs, startMs, endMs) {
|
|
21400
|
+
return {
|
|
21401
|
+
attributes: toAttrs(attrs),
|
|
21402
|
+
startTimeUnixNano: nano(startMs),
|
|
21403
|
+
timeUnixNano: nano(endMs),
|
|
21404
|
+
asInt: String(Math.round(value))
|
|
21405
|
+
};
|
|
21406
|
+
}
|
|
21407
|
+
var webCrypto, nano, TTFT_BUCKETS, DURATION_BUCKETS, clip, TelemetryExporter;
|
|
21408
|
+
var init_otlpExporter = __esm({
|
|
21409
|
+
"src/services/telemetry/otlpExporter.ts"() {
|
|
21410
|
+
"use strict";
|
|
21411
|
+
init_debugLogger();
|
|
21412
|
+
webCrypto = globalThis.crypto;
|
|
21413
|
+
nano = (ms) => String(Math.round(ms * 1e6));
|
|
21414
|
+
TTFT_BUCKETS = [0.1, 0.25, 0.5, 1, 2, 5, 10, 30];
|
|
21415
|
+
DURATION_BUCKETS = [0.5, 1, 2, 5, 10, 30, 60, 120, 300];
|
|
21416
|
+
clip = (s, n = 120) => redactSecretsString(s.slice(0, n)).slice(0, n);
|
|
21417
|
+
TelemetryExporter = class {
|
|
21418
|
+
cfg;
|
|
21419
|
+
now;
|
|
21420
|
+
traceId = "";
|
|
21421
|
+
turn = null;
|
|
21422
|
+
llm = null;
|
|
21423
|
+
llmFirstChunkMs = 0;
|
|
21424
|
+
openTools = [];
|
|
21425
|
+
completedSpans = [];
|
|
21426
|
+
model = "";
|
|
21427
|
+
turnChunkChars = 0;
|
|
21428
|
+
turnTokens = 0;
|
|
21429
|
+
ttftSeconds = null;
|
|
21430
|
+
constructor(cfg, opts) {
|
|
21431
|
+
this.cfg = cfg;
|
|
21432
|
+
this.now = opts?.now ?? (() => Date.now());
|
|
21433
|
+
}
|
|
21434
|
+
startTurn(goal, model) {
|
|
21435
|
+
this.traceId = hex(16);
|
|
21436
|
+
this.model = model;
|
|
21437
|
+
this.turnChunkChars = 0;
|
|
21438
|
+
this.turnTokens = 0;
|
|
21439
|
+
this.ttftSeconds = null;
|
|
21440
|
+
this.llm = null;
|
|
21441
|
+
this.llmFirstChunkMs = 0;
|
|
21442
|
+
this.openTools = [];
|
|
21443
|
+
this.completedSpans = [];
|
|
21444
|
+
this.turn = {
|
|
21445
|
+
spanId: hex(8),
|
|
21446
|
+
name: "agent.turn",
|
|
21447
|
+
startMs: this.now(),
|
|
21448
|
+
attrs: { "gen_ai.request.model": model, "bandit.turn.goal": clip(goal, 160) }
|
|
21449
|
+
};
|
|
21450
|
+
}
|
|
21451
|
+
/** Fed from the chat turn lifecycle. Best-effort; swallows bad payloads. */
|
|
21452
|
+
onEvent(type, payload) {
|
|
21453
|
+
if (!this.turn) return;
|
|
21454
|
+
try {
|
|
21455
|
+
const p = payload ?? {};
|
|
21456
|
+
switch (type) {
|
|
21457
|
+
case "tool_loop:llm_start":
|
|
21458
|
+
this.llm = {
|
|
21459
|
+
spanId: hex(8),
|
|
21460
|
+
parentSpanId: this.turn.spanId,
|
|
21461
|
+
name: "llm.generate",
|
|
21462
|
+
startMs: this.now(),
|
|
21463
|
+
attrs: { "gen_ai.request.model": this.model }
|
|
21464
|
+
};
|
|
21465
|
+
this.llmFirstChunkMs = 0;
|
|
21466
|
+
break;
|
|
21467
|
+
case "tool_loop:llm_chunk": {
|
|
21468
|
+
const chunk = typeof p.chunk === "string" ? p.chunk : "";
|
|
21469
|
+
if (this.llm && this.llmFirstChunkMs === 0 && chunk.length > 0) {
|
|
21470
|
+
this.llmFirstChunkMs = this.now();
|
|
21471
|
+
const ttft = (this.llmFirstChunkMs - this.llm.startMs) / 1e3;
|
|
21472
|
+
if (this.ttftSeconds === null) this.ttftSeconds = ttft;
|
|
21473
|
+
this.llm.attrs["bandit.llm.ttft_seconds"] = ttft;
|
|
21474
|
+
}
|
|
21475
|
+
this.turnChunkChars += chunk.length;
|
|
21476
|
+
this.turnTokens = Math.floor(this.turnChunkChars / 4);
|
|
21477
|
+
break;
|
|
21478
|
+
}
|
|
21479
|
+
case "tool_loop:llm_response":
|
|
21480
|
+
if (this.llm) {
|
|
21481
|
+
this.llm.endMs = this.now();
|
|
21482
|
+
if (typeof p.responseLength === "number") this.llm.attrs["bandit.llm.response_chars"] = p.responseLength;
|
|
21483
|
+
this.completedSpans.push(this.llm);
|
|
21484
|
+
this.llm = null;
|
|
21485
|
+
}
|
|
21486
|
+
break;
|
|
21487
|
+
case "tool_loop:tool_execute": {
|
|
21488
|
+
const name = typeof p.name === "string" ? p.name : "tool";
|
|
21489
|
+
const params = p.params ?? {};
|
|
21490
|
+
const primary = params.query ?? params.url ?? params.prompt ?? params.topic ?? "";
|
|
21491
|
+
this.openTools.push({
|
|
21492
|
+
spanId: hex(8),
|
|
21493
|
+
parentSpanId: this.turn.spanId,
|
|
21494
|
+
name: `tool.${name}`,
|
|
21495
|
+
startMs: this.now(),
|
|
21496
|
+
attrs: { "bandit.tool.name": name, "bandit.tool.primary": primary ? clip(primary) : void 0 }
|
|
21497
|
+
});
|
|
21498
|
+
break;
|
|
21499
|
+
}
|
|
21500
|
+
case "tool_loop:tool_result":
|
|
21501
|
+
case "tool_loop:tool_error": {
|
|
21502
|
+
const name = typeof p.name === "string" ? p.name : void 0;
|
|
21503
|
+
const span = this.takeOpenTool(name);
|
|
21504
|
+
if (span) {
|
|
21505
|
+
span.endMs = this.now();
|
|
21506
|
+
if (type === "tool_loop:tool_error" || p.isError === true) span.error = "tool error";
|
|
21507
|
+
this.completedSpans.push(span);
|
|
21508
|
+
}
|
|
21509
|
+
break;
|
|
21510
|
+
}
|
|
21511
|
+
}
|
|
21512
|
+
} catch {
|
|
21513
|
+
}
|
|
21514
|
+
}
|
|
21515
|
+
takeOpenTool(name) {
|
|
21516
|
+
if (name) {
|
|
21517
|
+
for (let i = this.openTools.length - 1; i >= 0; i -= 1) {
|
|
21518
|
+
if (this.openTools[i].name === `tool.${name}`) return this.openTools.splice(i, 1)[0];
|
|
21519
|
+
}
|
|
21520
|
+
}
|
|
21521
|
+
return this.openTools.shift();
|
|
21522
|
+
}
|
|
21523
|
+
/** Close the turn, build OTLP traces + metrics, and flush. Never rejects. */
|
|
21524
|
+
async endTurn(outcome) {
|
|
21525
|
+
const turn = this.turn;
|
|
21526
|
+
if (!turn) return;
|
|
21527
|
+
this.turn = null;
|
|
21528
|
+
const end = this.now();
|
|
21529
|
+
if (this.llm && !this.llm.endMs) {
|
|
21530
|
+
this.llm.endMs = end;
|
|
21531
|
+
this.completedSpans.push(this.llm);
|
|
21532
|
+
this.llm = null;
|
|
21533
|
+
}
|
|
21534
|
+
for (const t of this.openTools.splice(0)) {
|
|
21535
|
+
t.endMs = end;
|
|
21536
|
+
t.error = t.error ?? "incomplete";
|
|
21537
|
+
this.completedSpans.push(t);
|
|
21538
|
+
}
|
|
21539
|
+
turn.endMs = end;
|
|
21540
|
+
if (outcome?.error) turn.error = outcome.error;
|
|
21541
|
+
const traceId = this.traceId;
|
|
21542
|
+
const spans = [turn, ...this.completedSpans];
|
|
21543
|
+
const jobs = [];
|
|
21544
|
+
if (this.cfg.mode === "metrics+traces") jobs.push(this.post("/v1/traces", this.buildTraces(traceId, spans)));
|
|
21545
|
+
jobs.push(this.post("/v1/metrics", this.buildMetrics(turn)));
|
|
21546
|
+
try {
|
|
21547
|
+
await Promise.all(jobs);
|
|
21548
|
+
} catch {
|
|
21549
|
+
}
|
|
21550
|
+
}
|
|
21551
|
+
buildTraces(traceId, spans) {
|
|
21552
|
+
return {
|
|
21553
|
+
resourceSpans: [
|
|
21554
|
+
{
|
|
21555
|
+
resource: { attributes: toAttrs({ "service.name": this.cfg.serviceName }) },
|
|
21556
|
+
scopeSpans: [
|
|
21557
|
+
{
|
|
21558
|
+
scope: { name: this.cfg.serviceName },
|
|
21559
|
+
spans: spans.map((s) => ({
|
|
21560
|
+
traceId,
|
|
21561
|
+
spanId: s.spanId,
|
|
21562
|
+
parentSpanId: s.parentSpanId,
|
|
21563
|
+
name: s.name,
|
|
21564
|
+
kind: 1,
|
|
21565
|
+
startTimeUnixNano: nano(s.startMs),
|
|
21566
|
+
endTimeUnixNano: nano(s.endMs ?? s.startMs),
|
|
21567
|
+
attributes: toAttrs(s.attrs),
|
|
21568
|
+
status: s.error ? { code: 2, message: s.error.slice(0, 200) } : { code: 1 }
|
|
21569
|
+
}))
|
|
21570
|
+
}
|
|
21571
|
+
]
|
|
21572
|
+
}
|
|
21573
|
+
]
|
|
21574
|
+
};
|
|
21575
|
+
}
|
|
21576
|
+
buildMetrics(turn) {
|
|
21577
|
+
const start = turn.startMs;
|
|
21578
|
+
const end = turn.endMs ?? this.now();
|
|
21579
|
+
const metrics = [];
|
|
21580
|
+
if (this.turnTokens > 0) {
|
|
21581
|
+
metrics.push({
|
|
21582
|
+
name: "bandit.llm.tokens",
|
|
21583
|
+
sum: {
|
|
21584
|
+
aggregationTemporality: 1,
|
|
21585
|
+
isMonotonic: true,
|
|
21586
|
+
dataPoints: [sumPoint(this.turnTokens, { type: "output", "gen_ai.request.model": this.model }, start, end)]
|
|
21587
|
+
}
|
|
21588
|
+
});
|
|
21589
|
+
}
|
|
21590
|
+
if (this.ttftSeconds !== null) {
|
|
21591
|
+
metrics.push({
|
|
21592
|
+
name: "bandit.llm.ttft",
|
|
21593
|
+
unit: "s",
|
|
21594
|
+
histogram: {
|
|
21595
|
+
aggregationTemporality: 1,
|
|
21596
|
+
dataPoints: [histogramPoint(this.ttftSeconds, TTFT_BUCKETS, { "gen_ai.request.model": this.model }, start, end)]
|
|
21597
|
+
}
|
|
21598
|
+
});
|
|
21599
|
+
}
|
|
21600
|
+
metrics.push({
|
|
21601
|
+
name: "bandit.turn.duration",
|
|
21602
|
+
unit: "s",
|
|
21603
|
+
histogram: {
|
|
21604
|
+
aggregationTemporality: 1,
|
|
21605
|
+
dataPoints: [histogramPoint((end - start) / 1e3, DURATION_BUCKETS, { "gen_ai.request.model": this.model }, start, end)]
|
|
21606
|
+
}
|
|
21607
|
+
});
|
|
21608
|
+
return {
|
|
21609
|
+
resourceMetrics: [
|
|
21610
|
+
{
|
|
21611
|
+
resource: { attributes: toAttrs({ "service.name": this.cfg.serviceName }) },
|
|
21612
|
+
scopeMetrics: [{ scope: { name: this.cfg.serviceName }, metrics }]
|
|
21613
|
+
}
|
|
21614
|
+
]
|
|
21615
|
+
};
|
|
21616
|
+
}
|
|
21617
|
+
async post(path, body) {
|
|
21618
|
+
const doFetch = globalThis.fetch;
|
|
21619
|
+
if (!doFetch) return;
|
|
21620
|
+
const ctrl = new AbortController();
|
|
21621
|
+
const timer = setTimeout(() => ctrl.abort(), 4e3);
|
|
21622
|
+
try {
|
|
21623
|
+
await doFetch(`${this.cfg.endpoint}${path}`, {
|
|
21624
|
+
method: "POST",
|
|
21625
|
+
headers: { "Content-Type": "application/json", ...this.cfg.headers },
|
|
21626
|
+
body: JSON.stringify(body),
|
|
21627
|
+
signal: ctrl.signal
|
|
21628
|
+
});
|
|
21629
|
+
} catch (e) {
|
|
21630
|
+
debugLogger.debug("[telemetry] OTLP post failed", {
|
|
21631
|
+
path,
|
|
21632
|
+
error: e instanceof Error ? e.message : String(e)
|
|
21633
|
+
});
|
|
21634
|
+
} finally {
|
|
21635
|
+
clearTimeout(timer);
|
|
21636
|
+
}
|
|
21637
|
+
}
|
|
21638
|
+
};
|
|
21639
|
+
}
|
|
21640
|
+
});
|
|
21641
|
+
|
|
21642
|
+
// src/services/telemetry/index.ts
|
|
21643
|
+
function syncTelemetry() {
|
|
21644
|
+
try {
|
|
21645
|
+
const settings = usePackageSettingsStore.getState().settings;
|
|
21646
|
+
const cfg = resolveTelemetryConfig({
|
|
21647
|
+
telemetry: settings?.telemetry,
|
|
21648
|
+
banditApiKey: authenticationService.getToken() ?? void 0
|
|
21649
|
+
});
|
|
21650
|
+
active = cfg ? new TelemetryExporter(cfg) : null;
|
|
21651
|
+
} catch {
|
|
21652
|
+
active = null;
|
|
21653
|
+
}
|
|
21654
|
+
return active !== null;
|
|
21655
|
+
}
|
|
21656
|
+
function telemetryStartTurn(goal, model) {
|
|
21657
|
+
active?.startTurn(goal, model);
|
|
21658
|
+
}
|
|
21659
|
+
function telemetryEvent(type, payload) {
|
|
21660
|
+
active?.onEvent(type, payload);
|
|
21661
|
+
}
|
|
21662
|
+
function telemetryEndTurn(outcome) {
|
|
21663
|
+
void active?.endTurn(outcome);
|
|
21664
|
+
}
|
|
21665
|
+
var active;
|
|
21666
|
+
var init_telemetry = __esm({
|
|
21667
|
+
"src/services/telemetry/index.ts"() {
|
|
21668
|
+
"use strict";
|
|
21669
|
+
init_otlpExporter();
|
|
21670
|
+
init_packageSettingsStore();
|
|
21671
|
+
init_authenticationService();
|
|
21672
|
+
init_otlpExporter();
|
|
21673
|
+
active = null;
|
|
21674
|
+
}
|
|
21675
|
+
});
|
|
21676
|
+
|
|
21349
21677
|
// src/chat/hooks/useMemoryEnhancer.tsx
|
|
21350
21678
|
var import_rxjs23, 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;
|
|
21351
21679
|
var init_useMemoryEnhancer = __esm({
|
|
@@ -22009,7 +22337,14 @@ var init_mcpService = __esm({
|
|
|
22009
22337
|
requestOptions.body = JSON.stringify(toolCall.parameters);
|
|
22010
22338
|
}
|
|
22011
22339
|
}
|
|
22012
|
-
const
|
|
22340
|
+
const controller = new AbortController();
|
|
22341
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
22342
|
+
let response;
|
|
22343
|
+
try {
|
|
22344
|
+
response = await fetch(url, { ...requestOptions, signal: controller.signal });
|
|
22345
|
+
} finally {
|
|
22346
|
+
clearTimeout(timeoutId);
|
|
22347
|
+
}
|
|
22013
22348
|
const data = await response.json();
|
|
22014
22349
|
if (!response.ok) {
|
|
22015
22350
|
debugLogger.error("MCP tool execution failed", {
|
|
@@ -22080,6 +22415,7 @@ var init_useAIProvider = __esm({
|
|
|
22080
22415
|
import_react44 = require("react");
|
|
22081
22416
|
init_knowledgeStore();
|
|
22082
22417
|
init_aiProviderStore();
|
|
22418
|
+
init_telemetry();
|
|
22083
22419
|
init_conversationStore();
|
|
22084
22420
|
init_useMemoryEnhancer();
|
|
22085
22421
|
init_useVectorStore();
|
|
@@ -22844,6 +23180,9 @@ ${protocol}`;
|
|
|
22844
23180
|
setStreamBuffer(latestDisplayMessage);
|
|
22845
23181
|
}, delay);
|
|
22846
23182
|
};
|
|
23183
|
+
syncTelemetry();
|
|
23184
|
+
telemetryStartTurn(question, modelName);
|
|
23185
|
+
telemetryEvent("tool_loop:llm_start");
|
|
22847
23186
|
const stream = provider.chat(request);
|
|
22848
23187
|
const initialPlaceholderQuestion = lastEntry?.question;
|
|
22849
23188
|
lastPartialRef.current = { text: "", images: [...imageList], usedDocs, question };
|
|
@@ -22857,7 +23196,10 @@ ${protocol}`;
|
|
|
22857
23196
|
const sub = stream.subscribe({
|
|
22858
23197
|
next: (data) => {
|
|
22859
23198
|
if (!data?.message?.content && !data?.message?.tool_calls) return;
|
|
22860
|
-
if (data.message.content)
|
|
23199
|
+
if (data.message.content) {
|
|
23200
|
+
fullMessage += data.message.content;
|
|
23201
|
+
telemetryEvent("tool_loop:llm_chunk", { chunk: data.message.content });
|
|
23202
|
+
}
|
|
22861
23203
|
const inThinkBlock = /<think>/.test(fullMessage) && !/<think>[\s\S]*<\/think>/.test(fullMessage);
|
|
22862
23204
|
setIsThinking?.(inThinkBlock);
|
|
22863
23205
|
const visibleMessage = stripThinking(fullMessage);
|
|
@@ -22889,6 +23231,7 @@ ${protocol}`;
|
|
|
22889
23231
|
setIsThinking?.(false);
|
|
22890
23232
|
setPendingMessage(null);
|
|
22891
23233
|
setLogoVisible(false);
|
|
23234
|
+
telemetryEndTurn({ error: err?.message || "stream error" });
|
|
22892
23235
|
if (onError) {
|
|
22893
23236
|
onError(err);
|
|
22894
23237
|
}
|
|
@@ -22896,6 +23239,7 @@ ${protocol}`;
|
|
|
22896
23239
|
complete: async () => {
|
|
22897
23240
|
try {
|
|
22898
23241
|
setIsThinking?.(false);
|
|
23242
|
+
telemetryEvent("tool_loop:llm_response", { responseLength: fullMessage.length });
|
|
22899
23243
|
latestDisplayMessage = stripThinking(fullMessage);
|
|
22900
23244
|
if (!sawToolBlock) {
|
|
22901
23245
|
flushNow();
|
|
@@ -22904,6 +23248,7 @@ ${protocol}`;
|
|
|
22904
23248
|
let enhancedMessage = fullMessage;
|
|
22905
23249
|
const summarizableResults = [];
|
|
22906
23250
|
const inlineImageBlocks = [];
|
|
23251
|
+
const collectedSources = [];
|
|
22907
23252
|
if (toolCallMatches && toolCallMatches.length > 0 && mcpToolsAvailable) {
|
|
22908
23253
|
debugLogger.info("Detected tool calls in AI response", {
|
|
22909
23254
|
toolCallCount: toolCallMatches.length,
|
|
@@ -22938,10 +23283,21 @@ ${protocol}`;
|
|
|
22938
23283
|
});
|
|
22939
23284
|
const placeholderToken = `<<TOOL_LOADING_${functionName}_${Math.random().toString(36).slice(2)}>>`;
|
|
22940
23285
|
enhancedMessage = enhancedMessage.replace(match, placeholderToken);
|
|
23286
|
+
clearFlushTimer();
|
|
23287
|
+
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";
|
|
23288
|
+
const toolPreamble = stripToolBlocks(fullMessage).trim();
|
|
23289
|
+
setStreamBuffer(toolPreamble ? `${toolPreamble}
|
|
23290
|
+
|
|
23291
|
+
_${toolStatus}_` : `_${toolStatus}_`);
|
|
23292
|
+
telemetryEvent("tool_loop:tool_execute", { name: functionName, params: parsedParams });
|
|
22941
23293
|
const result = await executeMCPTool({
|
|
22942
23294
|
toolName: functionName,
|
|
22943
23295
|
parameters: parsedParams
|
|
22944
23296
|
});
|
|
23297
|
+
telemetryEvent(result.success ? "tool_loop:tool_result" : "tool_loop:tool_error", {
|
|
23298
|
+
name: functionName,
|
|
23299
|
+
isError: !result.success
|
|
23300
|
+
});
|
|
22945
23301
|
let resultText = "";
|
|
22946
23302
|
if (result.success) {
|
|
22947
23303
|
if (functionName === "web_search" || functionName === "web-search") {
|
|
@@ -22955,18 +23311,16 @@ ${protocol}`;
|
|
|
22955
23311
|
blocks.push(
|
|
22956
23312
|
items.slice(0, 6).map((item, index) => {
|
|
22957
23313
|
const title = item.title?.trim() || "Untitled";
|
|
22958
|
-
const url = item.url?.trim();
|
|
23314
|
+
const url = item.url?.trim() || "";
|
|
22959
23315
|
const snippet = item.content?.trim();
|
|
22960
|
-
|
|
22961
|
-
|
|
22962
|
-
${url}`;
|
|
23316
|
+
if (url) collectedSources.push({ title, url });
|
|
23317
|
+
let line = url ? `${index + 1}. [${title}](${url})` : `${index + 1}. ${title}`;
|
|
22963
23318
|
if (snippet) {
|
|
22964
23319
|
const truncated = snippet.length > 300 ? `${snippet.slice(0, 300)}\u2026` : snippet;
|
|
22965
|
-
line += `
|
|
22966
|
-
${truncated}`;
|
|
23320
|
+
line += ` \u2014 ${truncated}`;
|
|
22967
23321
|
}
|
|
22968
23322
|
return line;
|
|
22969
|
-
}).join("\n
|
|
23323
|
+
}).join("\n")
|
|
22970
23324
|
);
|
|
22971
23325
|
}
|
|
22972
23326
|
resultText = blocks.length ? blocks.join("\n\n") : `No results found${search.query ? ` for "${search.query}"` : ""}.`;
|
|
@@ -23048,7 +23402,7 @@ ${r.output}`).join("\n\n");
|
|
|
23048
23402
|
|
|
23049
23403
|
${toolResultsText}
|
|
23050
23404
|
|
|
23051
|
-
Using these results together with your own knowledge, answer my original question concisely and in a natural, well-formatted way.
|
|
23405
|
+
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.`
|
|
23052
23406
|
}
|
|
23053
23407
|
];
|
|
23054
23408
|
const summaryRequest = {
|
|
@@ -23058,9 +23412,22 @@ Using these results together with your own knowledge, answer my original questio
|
|
|
23058
23412
|
options: { num_predict: tokenLimit + 250 }
|
|
23059
23413
|
};
|
|
23060
23414
|
clearFlushTimer();
|
|
23061
|
-
|
|
23415
|
+
const summaryPreamble = stripToolBlocks(fullMessage).trim();
|
|
23416
|
+
setStreamBuffer(
|
|
23417
|
+
summaryPreamble ? `${summaryPreamble}
|
|
23418
|
+
|
|
23419
|
+
_Writing the answer\u2026_` : "_Writing the answer\u2026_"
|
|
23420
|
+
);
|
|
23062
23421
|
const summaryText = await new Promise((resolve) => {
|
|
23063
23422
|
let acc = "";
|
|
23423
|
+
let settled = false;
|
|
23424
|
+
let timer;
|
|
23425
|
+
const done = (value) => {
|
|
23426
|
+
if (settled) return;
|
|
23427
|
+
settled = true;
|
|
23428
|
+
if (timer) clearTimeout(timer);
|
|
23429
|
+
resolve(value);
|
|
23430
|
+
};
|
|
23064
23431
|
const summarySub = provider.chat(summaryRequest).subscribe({
|
|
23065
23432
|
next: (data) => {
|
|
23066
23433
|
if (data?.message?.content) {
|
|
@@ -23075,14 +23442,33 @@ Using these results together with your own knowledge, answer my original questio
|
|
|
23075
23442
|
debugLogger.error("Summarization pass failed", {
|
|
23076
23443
|
error: summaryErr instanceof Error ? summaryErr.message : String(summaryErr)
|
|
23077
23444
|
});
|
|
23078
|
-
|
|
23445
|
+
done("");
|
|
23079
23446
|
},
|
|
23080
|
-
complete: () =>
|
|
23447
|
+
complete: () => done(stripThinking(acc).trim())
|
|
23081
23448
|
});
|
|
23082
23449
|
currentSubRef.current = summarySub;
|
|
23450
|
+
timer = setTimeout(() => {
|
|
23451
|
+
debugLogger.warn("Summarization pass timed out; using inline tool output");
|
|
23452
|
+
try {
|
|
23453
|
+
summarySub.unsubscribe();
|
|
23454
|
+
} catch {
|
|
23455
|
+
}
|
|
23456
|
+
done("");
|
|
23457
|
+
}, 3e4);
|
|
23083
23458
|
});
|
|
23084
23459
|
if (summaryText.trim()) {
|
|
23085
|
-
|
|
23460
|
+
const sourcesMd = collectedSources.length ? `
|
|
23461
|
+
|
|
23462
|
+
**Sources**
|
|
23463
|
+
${collectedSources.slice(0, 6).map((s) => {
|
|
23464
|
+
let domain = s.url;
|
|
23465
|
+
try {
|
|
23466
|
+
domain = new URL(s.url).hostname.replace(/^www\./, "");
|
|
23467
|
+
} catch {
|
|
23468
|
+
}
|
|
23469
|
+
return `- [${s.title || domain}](${s.url}) \u2014 ${domain}`;
|
|
23470
|
+
}).join("\n")}` : "";
|
|
23471
|
+
enhancedMessage = summaryText + sourcesMd + (inlineImageBlocks.length ? `
|
|
23086
23472
|
|
|
23087
23473
|
${inlineImageBlocks.join("\n\n")}` : "");
|
|
23088
23474
|
}
|
|
@@ -23131,6 +23517,7 @@ ${inlineImageBlocks.join("\n\n")}` : "");
|
|
|
23131
23517
|
}
|
|
23132
23518
|
setInputValue("");
|
|
23133
23519
|
setPastedImages([]);
|
|
23520
|
+
telemetryEndTurn();
|
|
23134
23521
|
setTimeout(() => {
|
|
23135
23522
|
clearFlushTimer();
|
|
23136
23523
|
setPendingMessage(null);
|
|
@@ -23146,6 +23533,7 @@ ${inlineImageBlocks.join("\n\n")}` : "");
|
|
|
23146
23533
|
overrideComponentStatus("Idle");
|
|
23147
23534
|
setIsSubmitting(false);
|
|
23148
23535
|
setIsStreaming(false);
|
|
23536
|
+
telemetryEndTurn({ error: e instanceof Error ? e.message : String(e) });
|
|
23149
23537
|
}
|
|
23150
23538
|
}
|
|
23151
23539
|
});
|
|
@@ -26004,10 +26392,10 @@ var init_enhanced_mobile_conversations_modal = __esm({
|
|
|
26004
26392
|
(0, import_react52.useEffect)(() => {
|
|
26005
26393
|
setDeletedConversationIds((prev) => {
|
|
26006
26394
|
let changed = false;
|
|
26007
|
-
const
|
|
26395
|
+
const active2 = new Set(conversations.map((conv) => conv.id));
|
|
26008
26396
|
const next = /* @__PURE__ */ new Set();
|
|
26009
26397
|
prev.forEach((id) => {
|
|
26010
|
-
if (
|
|
26398
|
+
if (active2.has(id)) {
|
|
26011
26399
|
next.add(id);
|
|
26012
26400
|
} else {
|
|
26013
26401
|
changed = true;
|