@burtson-labs/bandit-engine 2.0.59 → 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-URKUD3OL.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-V5QRXIIO.mjs → chunk-MFDMM5MS.mjs} +357 -19
- 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 +374 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10 -10
- package/dist/management/management.js +374 -14
- 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-3J4GDGWW.mjs +0 -16
- package/dist/chunk-KM7FUWCM.mjs.map +0 -1
- package/dist/chunk-V5QRXIIO.mjs.map +0 -1
- /package/dist/{chat-3J4GDGWW.mjs.map → chat-NLBCURUN.mjs.map} +0 -0
- /package/dist/{chunk-URKUD3OL.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({
|
|
@@ -22087,6 +22415,7 @@ var init_useAIProvider = __esm({
|
|
|
22087
22415
|
import_react44 = require("react");
|
|
22088
22416
|
init_knowledgeStore();
|
|
22089
22417
|
init_aiProviderStore();
|
|
22418
|
+
init_telemetry();
|
|
22090
22419
|
init_conversationStore();
|
|
22091
22420
|
init_useMemoryEnhancer();
|
|
22092
22421
|
init_useVectorStore();
|
|
@@ -22851,6 +23180,9 @@ ${protocol}`;
|
|
|
22851
23180
|
setStreamBuffer(latestDisplayMessage);
|
|
22852
23181
|
}, delay);
|
|
22853
23182
|
};
|
|
23183
|
+
syncTelemetry();
|
|
23184
|
+
telemetryStartTurn(question, modelName);
|
|
23185
|
+
telemetryEvent("tool_loop:llm_start");
|
|
22854
23186
|
const stream = provider.chat(request);
|
|
22855
23187
|
const initialPlaceholderQuestion = lastEntry?.question;
|
|
22856
23188
|
lastPartialRef.current = { text: "", images: [...imageList], usedDocs, question };
|
|
@@ -22864,7 +23196,10 @@ ${protocol}`;
|
|
|
22864
23196
|
const sub = stream.subscribe({
|
|
22865
23197
|
next: (data) => {
|
|
22866
23198
|
if (!data?.message?.content && !data?.message?.tool_calls) return;
|
|
22867
|
-
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
|
+
}
|
|
22868
23203
|
const inThinkBlock = /<think>/.test(fullMessage) && !/<think>[\s\S]*<\/think>/.test(fullMessage);
|
|
22869
23204
|
setIsThinking?.(inThinkBlock);
|
|
22870
23205
|
const visibleMessage = stripThinking(fullMessage);
|
|
@@ -22896,6 +23231,7 @@ ${protocol}`;
|
|
|
22896
23231
|
setIsThinking?.(false);
|
|
22897
23232
|
setPendingMessage(null);
|
|
22898
23233
|
setLogoVisible(false);
|
|
23234
|
+
telemetryEndTurn({ error: err?.message || "stream error" });
|
|
22899
23235
|
if (onError) {
|
|
22900
23236
|
onError(err);
|
|
22901
23237
|
}
|
|
@@ -22903,6 +23239,7 @@ ${protocol}`;
|
|
|
22903
23239
|
complete: async () => {
|
|
22904
23240
|
try {
|
|
22905
23241
|
setIsThinking?.(false);
|
|
23242
|
+
telemetryEvent("tool_loop:llm_response", { responseLength: fullMessage.length });
|
|
22906
23243
|
latestDisplayMessage = stripThinking(fullMessage);
|
|
22907
23244
|
if (!sawToolBlock) {
|
|
22908
23245
|
flushNow();
|
|
@@ -22911,6 +23248,7 @@ ${protocol}`;
|
|
|
22911
23248
|
let enhancedMessage = fullMessage;
|
|
22912
23249
|
const summarizableResults = [];
|
|
22913
23250
|
const inlineImageBlocks = [];
|
|
23251
|
+
const collectedSources = [];
|
|
22914
23252
|
if (toolCallMatches && toolCallMatches.length > 0 && mcpToolsAvailable) {
|
|
22915
23253
|
debugLogger.info("Detected tool calls in AI response", {
|
|
22916
23254
|
toolCallCount: toolCallMatches.length,
|
|
@@ -22945,10 +23283,21 @@ ${protocol}`;
|
|
|
22945
23283
|
});
|
|
22946
23284
|
const placeholderToken = `<<TOOL_LOADING_${functionName}_${Math.random().toString(36).slice(2)}>>`;
|
|
22947
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 });
|
|
22948
23293
|
const result = await executeMCPTool({
|
|
22949
23294
|
toolName: functionName,
|
|
22950
23295
|
parameters: parsedParams
|
|
22951
23296
|
});
|
|
23297
|
+
telemetryEvent(result.success ? "tool_loop:tool_result" : "tool_loop:tool_error", {
|
|
23298
|
+
name: functionName,
|
|
23299
|
+
isError: !result.success
|
|
23300
|
+
});
|
|
22952
23301
|
let resultText = "";
|
|
22953
23302
|
if (result.success) {
|
|
22954
23303
|
if (functionName === "web_search" || functionName === "web-search") {
|
|
@@ -22962,18 +23311,16 @@ ${protocol}`;
|
|
|
22962
23311
|
blocks.push(
|
|
22963
23312
|
items.slice(0, 6).map((item, index) => {
|
|
22964
23313
|
const title = item.title?.trim() || "Untitled";
|
|
22965
|
-
const url = item.url?.trim();
|
|
23314
|
+
const url = item.url?.trim() || "";
|
|
22966
23315
|
const snippet = item.content?.trim();
|
|
22967
|
-
|
|
22968
|
-
|
|
22969
|
-
${url}`;
|
|
23316
|
+
if (url) collectedSources.push({ title, url });
|
|
23317
|
+
let line = url ? `${index + 1}. [${title}](${url})` : `${index + 1}. ${title}`;
|
|
22970
23318
|
if (snippet) {
|
|
22971
23319
|
const truncated = snippet.length > 300 ? `${snippet.slice(0, 300)}\u2026` : snippet;
|
|
22972
|
-
line += `
|
|
22973
|
-
${truncated}`;
|
|
23320
|
+
line += ` \u2014 ${truncated}`;
|
|
22974
23321
|
}
|
|
22975
23322
|
return line;
|
|
22976
|
-
}).join("\n
|
|
23323
|
+
}).join("\n")
|
|
22977
23324
|
);
|
|
22978
23325
|
}
|
|
22979
23326
|
resultText = blocks.length ? blocks.join("\n\n") : `No results found${search.query ? ` for "${search.query}"` : ""}.`;
|
|
@@ -23055,7 +23402,7 @@ ${r.output}`).join("\n\n");
|
|
|
23055
23402
|
|
|
23056
23403
|
${toolResultsText}
|
|
23057
23404
|
|
|
23058
|
-
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.`
|
|
23059
23406
|
}
|
|
23060
23407
|
];
|
|
23061
23408
|
const summaryRequest = {
|
|
@@ -23069,7 +23416,7 @@ Using these results together with your own knowledge, answer my original questio
|
|
|
23069
23416
|
setStreamBuffer(
|
|
23070
23417
|
summaryPreamble ? `${summaryPreamble}
|
|
23071
23418
|
|
|
23072
|
-
|
|
23419
|
+
_Writing the answer\u2026_` : "_Writing the answer\u2026_"
|
|
23073
23420
|
);
|
|
23074
23421
|
const summaryText = await new Promise((resolve) => {
|
|
23075
23422
|
let acc = "";
|
|
@@ -23110,7 +23457,18 @@ _Working on it\u2026_` : "_Working on it\u2026_"
|
|
|
23110
23457
|
}, 3e4);
|
|
23111
23458
|
});
|
|
23112
23459
|
if (summaryText.trim()) {
|
|
23113
|
-
|
|
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 ? `
|
|
23114
23472
|
|
|
23115
23473
|
${inlineImageBlocks.join("\n\n")}` : "");
|
|
23116
23474
|
}
|
|
@@ -23159,6 +23517,7 @@ ${inlineImageBlocks.join("\n\n")}` : "");
|
|
|
23159
23517
|
}
|
|
23160
23518
|
setInputValue("");
|
|
23161
23519
|
setPastedImages([]);
|
|
23520
|
+
telemetryEndTurn();
|
|
23162
23521
|
setTimeout(() => {
|
|
23163
23522
|
clearFlushTimer();
|
|
23164
23523
|
setPendingMessage(null);
|
|
@@ -23174,6 +23533,7 @@ ${inlineImageBlocks.join("\n\n")}` : "");
|
|
|
23174
23533
|
overrideComponentStatus("Idle");
|
|
23175
23534
|
setIsSubmitting(false);
|
|
23176
23535
|
setIsStreaming(false);
|
|
23536
|
+
telemetryEndTurn({ error: e instanceof Error ? e.message : String(e) });
|
|
23177
23537
|
}
|
|
23178
23538
|
}
|
|
23179
23539
|
});
|
|
@@ -26032,10 +26392,10 @@ var init_enhanced_mobile_conversations_modal = __esm({
|
|
|
26032
26392
|
(0, import_react52.useEffect)(() => {
|
|
26033
26393
|
setDeletedConversationIds((prev) => {
|
|
26034
26394
|
let changed = false;
|
|
26035
|
-
const
|
|
26395
|
+
const active2 = new Set(conversations.map((conv) => conv.id));
|
|
26036
26396
|
const next = /* @__PURE__ */ new Set();
|
|
26037
26397
|
prev.forEach((id) => {
|
|
26038
|
-
if (
|
|
26398
|
+
if (active2.has(id)) {
|
|
26039
26399
|
next.add(id);
|
|
26040
26400
|
} else {
|
|
26041
26401
|
changed = true;
|