@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
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
} from "./chunk-ONQMRE2G.mjs";
|
|
4
4
|
import {
|
|
5
5
|
StreamingMarkdown_default
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SRCCNBHF.mjs";
|
|
7
7
|
import {
|
|
8
8
|
useMCPToolsStore
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-6QTTNYF2.mjs";
|
|
10
10
|
import {
|
|
11
11
|
AddIcon,
|
|
12
12
|
ArrowDownwardIcon,
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
useNotificationService,
|
|
43
43
|
useTTS,
|
|
44
44
|
useVoiceStore
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-5WQMMCZQ.mjs";
|
|
46
46
|
import {
|
|
47
47
|
authenticationService,
|
|
48
48
|
brandingService_default,
|
|
@@ -67,13 +67,13 @@ import {
|
|
|
67
67
|
useMemoryStore,
|
|
68
68
|
useProjectStore,
|
|
69
69
|
useVectorStore
|
|
70
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-EUBVBTB3.mjs";
|
|
71
71
|
import {
|
|
72
72
|
indexedDBService_default,
|
|
73
73
|
useModelStore,
|
|
74
74
|
usePackageSettingsStore,
|
|
75
75
|
usePreferencesStore
|
|
76
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-IPMTNREZ.mjs";
|
|
77
77
|
import {
|
|
78
78
|
useAIProviderStore
|
|
79
79
|
} from "./chunk-H3BYFEIE.mjs";
|
|
@@ -1388,6 +1388,313 @@ var chat_input_default = ChatInput;
|
|
|
1388
1388
|
// src/chat/hooks/useAIProvider.tsx
|
|
1389
1389
|
import { useCallback, useRef as useRef3 } from "react";
|
|
1390
1390
|
|
|
1391
|
+
// src/services/telemetry/otlpExporter.ts
|
|
1392
|
+
function redactSecretsString(s) {
|
|
1393
|
+
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]");
|
|
1394
|
+
}
|
|
1395
|
+
function resolveTelemetryConfig(opts) {
|
|
1396
|
+
if (!opts.telemetry?.enabled) return null;
|
|
1397
|
+
const endpoint = (opts.telemetry.endpoint ?? "https://otlp.burtson.ai").replace(/\/+$/, "");
|
|
1398
|
+
const headers = { ...opts.telemetry.headers ?? {} };
|
|
1399
|
+
const hasAuth = Object.keys(headers).some((k) => k.toLowerCase() === "authorization");
|
|
1400
|
+
if (!hasAuth && opts.banditApiKey) {
|
|
1401
|
+
headers["Authorization"] = `Bearer ${opts.banditApiKey}`;
|
|
1402
|
+
}
|
|
1403
|
+
const mode = opts.telemetry.mode ?? "metrics+traces";
|
|
1404
|
+
return { endpoint, headers, mode, serviceName: opts.telemetry.serviceName ?? "bandit-web" };
|
|
1405
|
+
}
|
|
1406
|
+
function toAttrs(rec) {
|
|
1407
|
+
const out = [];
|
|
1408
|
+
for (const [key, v] of Object.entries(rec)) {
|
|
1409
|
+
if (v === void 0 || v === null || v === "") continue;
|
|
1410
|
+
if (typeof v === "boolean") out.push({ key, value: { boolValue: v } });
|
|
1411
|
+
else if (typeof v === "number")
|
|
1412
|
+
out.push({ key, value: Number.isInteger(v) ? { intValue: String(v) } : { doubleValue: v } });
|
|
1413
|
+
else out.push({ key, value: { stringValue: v } });
|
|
1414
|
+
}
|
|
1415
|
+
return out;
|
|
1416
|
+
}
|
|
1417
|
+
var webCrypto = globalThis.crypto;
|
|
1418
|
+
function hex(bytes) {
|
|
1419
|
+
const arr = new Uint8Array(bytes);
|
|
1420
|
+
if (webCrypto?.getRandomValues) webCrypto.getRandomValues(arr);
|
|
1421
|
+
return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
1422
|
+
}
|
|
1423
|
+
var nano = (ms) => String(Math.round(ms * 1e6));
|
|
1424
|
+
var TTFT_BUCKETS = [0.1, 0.25, 0.5, 1, 2, 5, 10, 30];
|
|
1425
|
+
var DURATION_BUCKETS = [0.5, 1, 2, 5, 10, 30, 60, 120, 300];
|
|
1426
|
+
function histogramPoint(value, bounds, attrs, startMs, endMs) {
|
|
1427
|
+
const counts = new Array(bounds.length + 1).fill(0);
|
|
1428
|
+
let idx = bounds.findIndex((b) => value <= b);
|
|
1429
|
+
if (idx === -1) idx = bounds.length;
|
|
1430
|
+
counts[idx] = 1;
|
|
1431
|
+
return {
|
|
1432
|
+
attributes: toAttrs(attrs),
|
|
1433
|
+
startTimeUnixNano: nano(startMs),
|
|
1434
|
+
timeUnixNano: nano(endMs),
|
|
1435
|
+
count: "1",
|
|
1436
|
+
sum: value,
|
|
1437
|
+
bucketCounts: counts.map(String),
|
|
1438
|
+
explicitBounds: bounds
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
function sumPoint(value, attrs, startMs, endMs) {
|
|
1442
|
+
return {
|
|
1443
|
+
attributes: toAttrs(attrs),
|
|
1444
|
+
startTimeUnixNano: nano(startMs),
|
|
1445
|
+
timeUnixNano: nano(endMs),
|
|
1446
|
+
asInt: String(Math.round(value))
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
var clip = (s, n = 120) => redactSecretsString(s.slice(0, n)).slice(0, n);
|
|
1450
|
+
var TelemetryExporter = class {
|
|
1451
|
+
cfg;
|
|
1452
|
+
now;
|
|
1453
|
+
traceId = "";
|
|
1454
|
+
turn = null;
|
|
1455
|
+
llm = null;
|
|
1456
|
+
llmFirstChunkMs = 0;
|
|
1457
|
+
openTools = [];
|
|
1458
|
+
completedSpans = [];
|
|
1459
|
+
model = "";
|
|
1460
|
+
turnChunkChars = 0;
|
|
1461
|
+
turnTokens = 0;
|
|
1462
|
+
ttftSeconds = null;
|
|
1463
|
+
constructor(cfg, opts) {
|
|
1464
|
+
this.cfg = cfg;
|
|
1465
|
+
this.now = opts?.now ?? (() => Date.now());
|
|
1466
|
+
}
|
|
1467
|
+
startTurn(goal, model) {
|
|
1468
|
+
this.traceId = hex(16);
|
|
1469
|
+
this.model = model;
|
|
1470
|
+
this.turnChunkChars = 0;
|
|
1471
|
+
this.turnTokens = 0;
|
|
1472
|
+
this.ttftSeconds = null;
|
|
1473
|
+
this.llm = null;
|
|
1474
|
+
this.llmFirstChunkMs = 0;
|
|
1475
|
+
this.openTools = [];
|
|
1476
|
+
this.completedSpans = [];
|
|
1477
|
+
this.turn = {
|
|
1478
|
+
spanId: hex(8),
|
|
1479
|
+
name: "agent.turn",
|
|
1480
|
+
startMs: this.now(),
|
|
1481
|
+
attrs: { "gen_ai.request.model": model, "bandit.turn.goal": clip(goal, 160) }
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
/** Fed from the chat turn lifecycle. Best-effort; swallows bad payloads. */
|
|
1485
|
+
onEvent(type, payload) {
|
|
1486
|
+
if (!this.turn) return;
|
|
1487
|
+
try {
|
|
1488
|
+
const p = payload ?? {};
|
|
1489
|
+
switch (type) {
|
|
1490
|
+
case "tool_loop:llm_start":
|
|
1491
|
+
this.llm = {
|
|
1492
|
+
spanId: hex(8),
|
|
1493
|
+
parentSpanId: this.turn.spanId,
|
|
1494
|
+
name: "llm.generate",
|
|
1495
|
+
startMs: this.now(),
|
|
1496
|
+
attrs: { "gen_ai.request.model": this.model }
|
|
1497
|
+
};
|
|
1498
|
+
this.llmFirstChunkMs = 0;
|
|
1499
|
+
break;
|
|
1500
|
+
case "tool_loop:llm_chunk": {
|
|
1501
|
+
const chunk = typeof p.chunk === "string" ? p.chunk : "";
|
|
1502
|
+
if (this.llm && this.llmFirstChunkMs === 0 && chunk.length > 0) {
|
|
1503
|
+
this.llmFirstChunkMs = this.now();
|
|
1504
|
+
const ttft = (this.llmFirstChunkMs - this.llm.startMs) / 1e3;
|
|
1505
|
+
if (this.ttftSeconds === null) this.ttftSeconds = ttft;
|
|
1506
|
+
this.llm.attrs["bandit.llm.ttft_seconds"] = ttft;
|
|
1507
|
+
}
|
|
1508
|
+
this.turnChunkChars += chunk.length;
|
|
1509
|
+
this.turnTokens = Math.floor(this.turnChunkChars / 4);
|
|
1510
|
+
break;
|
|
1511
|
+
}
|
|
1512
|
+
case "tool_loop:llm_response":
|
|
1513
|
+
if (this.llm) {
|
|
1514
|
+
this.llm.endMs = this.now();
|
|
1515
|
+
if (typeof p.responseLength === "number") this.llm.attrs["bandit.llm.response_chars"] = p.responseLength;
|
|
1516
|
+
this.completedSpans.push(this.llm);
|
|
1517
|
+
this.llm = null;
|
|
1518
|
+
}
|
|
1519
|
+
break;
|
|
1520
|
+
case "tool_loop:tool_execute": {
|
|
1521
|
+
const name = typeof p.name === "string" ? p.name : "tool";
|
|
1522
|
+
const params = p.params ?? {};
|
|
1523
|
+
const primary = params.query ?? params.url ?? params.prompt ?? params.topic ?? "";
|
|
1524
|
+
this.openTools.push({
|
|
1525
|
+
spanId: hex(8),
|
|
1526
|
+
parentSpanId: this.turn.spanId,
|
|
1527
|
+
name: `tool.${name}`,
|
|
1528
|
+
startMs: this.now(),
|
|
1529
|
+
attrs: { "bandit.tool.name": name, "bandit.tool.primary": primary ? clip(primary) : void 0 }
|
|
1530
|
+
});
|
|
1531
|
+
break;
|
|
1532
|
+
}
|
|
1533
|
+
case "tool_loop:tool_result":
|
|
1534
|
+
case "tool_loop:tool_error": {
|
|
1535
|
+
const name = typeof p.name === "string" ? p.name : void 0;
|
|
1536
|
+
const span = this.takeOpenTool(name);
|
|
1537
|
+
if (span) {
|
|
1538
|
+
span.endMs = this.now();
|
|
1539
|
+
if (type === "tool_loop:tool_error" || p.isError === true) span.error = "tool error";
|
|
1540
|
+
this.completedSpans.push(span);
|
|
1541
|
+
}
|
|
1542
|
+
break;
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
} catch {
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
takeOpenTool(name) {
|
|
1549
|
+
if (name) {
|
|
1550
|
+
for (let i = this.openTools.length - 1; i >= 0; i -= 1) {
|
|
1551
|
+
if (this.openTools[i].name === `tool.${name}`) return this.openTools.splice(i, 1)[0];
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
return this.openTools.shift();
|
|
1555
|
+
}
|
|
1556
|
+
/** Close the turn, build OTLP traces + metrics, and flush. Never rejects. */
|
|
1557
|
+
async endTurn(outcome) {
|
|
1558
|
+
const turn = this.turn;
|
|
1559
|
+
if (!turn) return;
|
|
1560
|
+
this.turn = null;
|
|
1561
|
+
const end = this.now();
|
|
1562
|
+
if (this.llm && !this.llm.endMs) {
|
|
1563
|
+
this.llm.endMs = end;
|
|
1564
|
+
this.completedSpans.push(this.llm);
|
|
1565
|
+
this.llm = null;
|
|
1566
|
+
}
|
|
1567
|
+
for (const t of this.openTools.splice(0)) {
|
|
1568
|
+
t.endMs = end;
|
|
1569
|
+
t.error = t.error ?? "incomplete";
|
|
1570
|
+
this.completedSpans.push(t);
|
|
1571
|
+
}
|
|
1572
|
+
turn.endMs = end;
|
|
1573
|
+
if (outcome?.error) turn.error = outcome.error;
|
|
1574
|
+
const traceId = this.traceId;
|
|
1575
|
+
const spans = [turn, ...this.completedSpans];
|
|
1576
|
+
const jobs = [];
|
|
1577
|
+
if (this.cfg.mode === "metrics+traces") jobs.push(this.post("/v1/traces", this.buildTraces(traceId, spans)));
|
|
1578
|
+
jobs.push(this.post("/v1/metrics", this.buildMetrics(turn)));
|
|
1579
|
+
try {
|
|
1580
|
+
await Promise.all(jobs);
|
|
1581
|
+
} catch {
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
buildTraces(traceId, spans) {
|
|
1585
|
+
return {
|
|
1586
|
+
resourceSpans: [
|
|
1587
|
+
{
|
|
1588
|
+
resource: { attributes: toAttrs({ "service.name": this.cfg.serviceName }) },
|
|
1589
|
+
scopeSpans: [
|
|
1590
|
+
{
|
|
1591
|
+
scope: { name: this.cfg.serviceName },
|
|
1592
|
+
spans: spans.map((s) => ({
|
|
1593
|
+
traceId,
|
|
1594
|
+
spanId: s.spanId,
|
|
1595
|
+
parentSpanId: s.parentSpanId,
|
|
1596
|
+
name: s.name,
|
|
1597
|
+
kind: 1,
|
|
1598
|
+
startTimeUnixNano: nano(s.startMs),
|
|
1599
|
+
endTimeUnixNano: nano(s.endMs ?? s.startMs),
|
|
1600
|
+
attributes: toAttrs(s.attrs),
|
|
1601
|
+
status: s.error ? { code: 2, message: s.error.slice(0, 200) } : { code: 1 }
|
|
1602
|
+
}))
|
|
1603
|
+
}
|
|
1604
|
+
]
|
|
1605
|
+
}
|
|
1606
|
+
]
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
buildMetrics(turn) {
|
|
1610
|
+
const start = turn.startMs;
|
|
1611
|
+
const end = turn.endMs ?? this.now();
|
|
1612
|
+
const metrics = [];
|
|
1613
|
+
if (this.turnTokens > 0) {
|
|
1614
|
+
metrics.push({
|
|
1615
|
+
name: "bandit.llm.tokens",
|
|
1616
|
+
sum: {
|
|
1617
|
+
aggregationTemporality: 1,
|
|
1618
|
+
isMonotonic: true,
|
|
1619
|
+
dataPoints: [sumPoint(this.turnTokens, { type: "output", "gen_ai.request.model": this.model }, start, end)]
|
|
1620
|
+
}
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1623
|
+
if (this.ttftSeconds !== null) {
|
|
1624
|
+
metrics.push({
|
|
1625
|
+
name: "bandit.llm.ttft",
|
|
1626
|
+
unit: "s",
|
|
1627
|
+
histogram: {
|
|
1628
|
+
aggregationTemporality: 1,
|
|
1629
|
+
dataPoints: [histogramPoint(this.ttftSeconds, TTFT_BUCKETS, { "gen_ai.request.model": this.model }, start, end)]
|
|
1630
|
+
}
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
metrics.push({
|
|
1634
|
+
name: "bandit.turn.duration",
|
|
1635
|
+
unit: "s",
|
|
1636
|
+
histogram: {
|
|
1637
|
+
aggregationTemporality: 1,
|
|
1638
|
+
dataPoints: [histogramPoint((end - start) / 1e3, DURATION_BUCKETS, { "gen_ai.request.model": this.model }, start, end)]
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1641
|
+
return {
|
|
1642
|
+
resourceMetrics: [
|
|
1643
|
+
{
|
|
1644
|
+
resource: { attributes: toAttrs({ "service.name": this.cfg.serviceName }) },
|
|
1645
|
+
scopeMetrics: [{ scope: { name: this.cfg.serviceName }, metrics }]
|
|
1646
|
+
}
|
|
1647
|
+
]
|
|
1648
|
+
};
|
|
1649
|
+
}
|
|
1650
|
+
async post(path, body) {
|
|
1651
|
+
const doFetch = globalThis.fetch;
|
|
1652
|
+
if (!doFetch) return;
|
|
1653
|
+
const ctrl = new AbortController();
|
|
1654
|
+
const timer = setTimeout(() => ctrl.abort(), 4e3);
|
|
1655
|
+
try {
|
|
1656
|
+
await doFetch(`${this.cfg.endpoint}${path}`, {
|
|
1657
|
+
method: "POST",
|
|
1658
|
+
headers: { "Content-Type": "application/json", ...this.cfg.headers },
|
|
1659
|
+
body: JSON.stringify(body),
|
|
1660
|
+
signal: ctrl.signal
|
|
1661
|
+
});
|
|
1662
|
+
} catch (e) {
|
|
1663
|
+
debugLogger.debug("[telemetry] OTLP post failed", {
|
|
1664
|
+
path,
|
|
1665
|
+
error: e instanceof Error ? e.message : String(e)
|
|
1666
|
+
});
|
|
1667
|
+
} finally {
|
|
1668
|
+
clearTimeout(timer);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
};
|
|
1672
|
+
|
|
1673
|
+
// src/services/telemetry/index.ts
|
|
1674
|
+
var active = null;
|
|
1675
|
+
function syncTelemetry() {
|
|
1676
|
+
try {
|
|
1677
|
+
const settings = usePackageSettingsStore.getState().settings;
|
|
1678
|
+
const cfg = resolveTelemetryConfig({
|
|
1679
|
+
telemetry: settings?.telemetry,
|
|
1680
|
+
banditApiKey: authenticationService.getToken() ?? void 0
|
|
1681
|
+
});
|
|
1682
|
+
active = cfg ? new TelemetryExporter(cfg) : null;
|
|
1683
|
+
} catch {
|
|
1684
|
+
active = null;
|
|
1685
|
+
}
|
|
1686
|
+
return active !== null;
|
|
1687
|
+
}
|
|
1688
|
+
function telemetryStartTurn(goal, model) {
|
|
1689
|
+
active?.startTurn(goal, model);
|
|
1690
|
+
}
|
|
1691
|
+
function telemetryEvent(type, payload) {
|
|
1692
|
+
active?.onEvent(type, payload);
|
|
1693
|
+
}
|
|
1694
|
+
function telemetryEndTurn(outcome) {
|
|
1695
|
+
void active?.endTurn(outcome);
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1391
1698
|
// src/chat/hooks/useMemoryEnhancer.tsx
|
|
1392
1699
|
import { lastValueFrom, map as map2 } from "rxjs";
|
|
1393
1700
|
var MEMORY_LIMIT = 100;
|
|
@@ -2021,7 +2328,14 @@ var executeMCPTool = async (toolCall) => {
|
|
|
2021
2328
|
requestOptions.body = JSON.stringify(toolCall.parameters);
|
|
2022
2329
|
}
|
|
2023
2330
|
}
|
|
2024
|
-
const
|
|
2331
|
+
const controller = new AbortController();
|
|
2332
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
2333
|
+
let response;
|
|
2334
|
+
try {
|
|
2335
|
+
response = await fetch(url, { ...requestOptions, signal: controller.signal });
|
|
2336
|
+
} finally {
|
|
2337
|
+
clearTimeout(timeoutId);
|
|
2338
|
+
}
|
|
2025
2339
|
const data = await response.json();
|
|
2026
2340
|
if (!response.ok) {
|
|
2027
2341
|
debugLogger.error("MCP tool execution failed", {
|
|
@@ -2828,6 +3142,9 @@ ${protocol}`;
|
|
|
2828
3142
|
setStreamBuffer(latestDisplayMessage);
|
|
2829
3143
|
}, delay);
|
|
2830
3144
|
};
|
|
3145
|
+
syncTelemetry();
|
|
3146
|
+
telemetryStartTurn(question, modelName);
|
|
3147
|
+
telemetryEvent("tool_loop:llm_start");
|
|
2831
3148
|
const stream = provider.chat(request);
|
|
2832
3149
|
const initialPlaceholderQuestion = lastEntry?.question;
|
|
2833
3150
|
lastPartialRef.current = { text: "", images: [...imageList], usedDocs, question };
|
|
@@ -2841,7 +3158,10 @@ ${protocol}`;
|
|
|
2841
3158
|
const sub = stream.subscribe({
|
|
2842
3159
|
next: (data) => {
|
|
2843
3160
|
if (!data?.message?.content && !data?.message?.tool_calls) return;
|
|
2844
|
-
if (data.message.content)
|
|
3161
|
+
if (data.message.content) {
|
|
3162
|
+
fullMessage += data.message.content;
|
|
3163
|
+
telemetryEvent("tool_loop:llm_chunk", { chunk: data.message.content });
|
|
3164
|
+
}
|
|
2845
3165
|
const inThinkBlock = /<think>/.test(fullMessage) && !/<think>[\s\S]*<\/think>/.test(fullMessage);
|
|
2846
3166
|
setIsThinking?.(inThinkBlock);
|
|
2847
3167
|
const visibleMessage = stripThinking(fullMessage);
|
|
@@ -2873,6 +3193,7 @@ ${protocol}`;
|
|
|
2873
3193
|
setIsThinking?.(false);
|
|
2874
3194
|
setPendingMessage(null);
|
|
2875
3195
|
setLogoVisible(false);
|
|
3196
|
+
telemetryEndTurn({ error: err?.message || "stream error" });
|
|
2876
3197
|
if (onError) {
|
|
2877
3198
|
onError(err);
|
|
2878
3199
|
}
|
|
@@ -2880,6 +3201,7 @@ ${protocol}`;
|
|
|
2880
3201
|
complete: async () => {
|
|
2881
3202
|
try {
|
|
2882
3203
|
setIsThinking?.(false);
|
|
3204
|
+
telemetryEvent("tool_loop:llm_response", { responseLength: fullMessage.length });
|
|
2883
3205
|
latestDisplayMessage = stripThinking(fullMessage);
|
|
2884
3206
|
if (!sawToolBlock) {
|
|
2885
3207
|
flushNow();
|
|
@@ -2888,6 +3210,7 @@ ${protocol}`;
|
|
|
2888
3210
|
let enhancedMessage = fullMessage;
|
|
2889
3211
|
const summarizableResults = [];
|
|
2890
3212
|
const inlineImageBlocks = [];
|
|
3213
|
+
const collectedSources = [];
|
|
2891
3214
|
if (toolCallMatches && toolCallMatches.length > 0 && mcpToolsAvailable) {
|
|
2892
3215
|
debugLogger.info("Detected tool calls in AI response", {
|
|
2893
3216
|
toolCallCount: toolCallMatches.length,
|
|
@@ -2922,10 +3245,21 @@ ${protocol}`;
|
|
|
2922
3245
|
});
|
|
2923
3246
|
const placeholderToken = `<<TOOL_LOADING_${functionName}_${Math.random().toString(36).slice(2)}>>`;
|
|
2924
3247
|
enhancedMessage = enhancedMessage.replace(match, placeholderToken);
|
|
3248
|
+
clearFlushTimer();
|
|
3249
|
+
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";
|
|
3250
|
+
const toolPreamble = stripToolBlocks(fullMessage).trim();
|
|
3251
|
+
setStreamBuffer(toolPreamble ? `${toolPreamble}
|
|
3252
|
+
|
|
3253
|
+
_${toolStatus}_` : `_${toolStatus}_`);
|
|
3254
|
+
telemetryEvent("tool_loop:tool_execute", { name: functionName, params: parsedParams });
|
|
2925
3255
|
const result = await executeMCPTool({
|
|
2926
3256
|
toolName: functionName,
|
|
2927
3257
|
parameters: parsedParams
|
|
2928
3258
|
});
|
|
3259
|
+
telemetryEvent(result.success ? "tool_loop:tool_result" : "tool_loop:tool_error", {
|
|
3260
|
+
name: functionName,
|
|
3261
|
+
isError: !result.success
|
|
3262
|
+
});
|
|
2929
3263
|
let resultText = "";
|
|
2930
3264
|
if (result.success) {
|
|
2931
3265
|
if (functionName === "web_search" || functionName === "web-search") {
|
|
@@ -2939,18 +3273,16 @@ ${protocol}`;
|
|
|
2939
3273
|
blocks.push(
|
|
2940
3274
|
items.slice(0, 6).map((item, index) => {
|
|
2941
3275
|
const title = item.title?.trim() || "Untitled";
|
|
2942
|
-
const url = item.url?.trim();
|
|
3276
|
+
const url = item.url?.trim() || "";
|
|
2943
3277
|
const snippet = item.content?.trim();
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
${url}`;
|
|
3278
|
+
if (url) collectedSources.push({ title, url });
|
|
3279
|
+
let line = url ? `${index + 1}. [${title}](${url})` : `${index + 1}. ${title}`;
|
|
2947
3280
|
if (snippet) {
|
|
2948
3281
|
const truncated = snippet.length > 300 ? `${snippet.slice(0, 300)}\u2026` : snippet;
|
|
2949
|
-
line += `
|
|
2950
|
-
${truncated}`;
|
|
3282
|
+
line += ` \u2014 ${truncated}`;
|
|
2951
3283
|
}
|
|
2952
3284
|
return line;
|
|
2953
|
-
}).join("\n
|
|
3285
|
+
}).join("\n")
|
|
2954
3286
|
);
|
|
2955
3287
|
}
|
|
2956
3288
|
resultText = blocks.length ? blocks.join("\n\n") : `No results found${search.query ? ` for "${search.query}"` : ""}.`;
|
|
@@ -3032,7 +3364,7 @@ ${r.output}`).join("\n\n");
|
|
|
3032
3364
|
|
|
3033
3365
|
${toolResultsText}
|
|
3034
3366
|
|
|
3035
|
-
Using these results together with your own knowledge, answer my original question concisely and in a natural, well-formatted way.
|
|
3367
|
+
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.`
|
|
3036
3368
|
}
|
|
3037
3369
|
];
|
|
3038
3370
|
const summaryRequest = {
|
|
@@ -3042,9 +3374,22 @@ Using these results together with your own knowledge, answer my original questio
|
|
|
3042
3374
|
options: { num_predict: tokenLimit + 250 }
|
|
3043
3375
|
};
|
|
3044
3376
|
clearFlushTimer();
|
|
3045
|
-
|
|
3377
|
+
const summaryPreamble = stripToolBlocks(fullMessage).trim();
|
|
3378
|
+
setStreamBuffer(
|
|
3379
|
+
summaryPreamble ? `${summaryPreamble}
|
|
3380
|
+
|
|
3381
|
+
_Writing the answer\u2026_` : "_Writing the answer\u2026_"
|
|
3382
|
+
);
|
|
3046
3383
|
const summaryText = await new Promise((resolve) => {
|
|
3047
3384
|
let acc = "";
|
|
3385
|
+
let settled = false;
|
|
3386
|
+
let timer;
|
|
3387
|
+
const done = (value) => {
|
|
3388
|
+
if (settled) return;
|
|
3389
|
+
settled = true;
|
|
3390
|
+
if (timer) clearTimeout(timer);
|
|
3391
|
+
resolve(value);
|
|
3392
|
+
};
|
|
3048
3393
|
const summarySub = provider.chat(summaryRequest).subscribe({
|
|
3049
3394
|
next: (data) => {
|
|
3050
3395
|
if (data?.message?.content) {
|
|
@@ -3059,14 +3404,33 @@ Using these results together with your own knowledge, answer my original questio
|
|
|
3059
3404
|
debugLogger.error("Summarization pass failed", {
|
|
3060
3405
|
error: summaryErr instanceof Error ? summaryErr.message : String(summaryErr)
|
|
3061
3406
|
});
|
|
3062
|
-
|
|
3407
|
+
done("");
|
|
3063
3408
|
},
|
|
3064
|
-
complete: () =>
|
|
3409
|
+
complete: () => done(stripThinking(acc).trim())
|
|
3065
3410
|
});
|
|
3066
3411
|
currentSubRef.current = summarySub;
|
|
3412
|
+
timer = setTimeout(() => {
|
|
3413
|
+
debugLogger.warn("Summarization pass timed out; using inline tool output");
|
|
3414
|
+
try {
|
|
3415
|
+
summarySub.unsubscribe();
|
|
3416
|
+
} catch {
|
|
3417
|
+
}
|
|
3418
|
+
done("");
|
|
3419
|
+
}, 3e4);
|
|
3067
3420
|
});
|
|
3068
3421
|
if (summaryText.trim()) {
|
|
3069
|
-
|
|
3422
|
+
const sourcesMd = collectedSources.length ? `
|
|
3423
|
+
|
|
3424
|
+
**Sources**
|
|
3425
|
+
${collectedSources.slice(0, 6).map((s) => {
|
|
3426
|
+
let domain = s.url;
|
|
3427
|
+
try {
|
|
3428
|
+
domain = new URL(s.url).hostname.replace(/^www\./, "");
|
|
3429
|
+
} catch {
|
|
3430
|
+
}
|
|
3431
|
+
return `- [${s.title || domain}](${s.url}) \u2014 ${domain}`;
|
|
3432
|
+
}).join("\n")}` : "";
|
|
3433
|
+
enhancedMessage = summaryText + sourcesMd + (inlineImageBlocks.length ? `
|
|
3070
3434
|
|
|
3071
3435
|
${inlineImageBlocks.join("\n\n")}` : "");
|
|
3072
3436
|
}
|
|
@@ -3115,6 +3479,7 @@ ${inlineImageBlocks.join("\n\n")}` : "");
|
|
|
3115
3479
|
}
|
|
3116
3480
|
setInputValue("");
|
|
3117
3481
|
setPastedImages([]);
|
|
3482
|
+
telemetryEndTurn();
|
|
3118
3483
|
setTimeout(() => {
|
|
3119
3484
|
clearFlushTimer();
|
|
3120
3485
|
setPendingMessage(null);
|
|
@@ -3130,6 +3495,7 @@ ${inlineImageBlocks.join("\n\n")}` : "");
|
|
|
3130
3495
|
overrideComponentStatus("Idle");
|
|
3131
3496
|
setIsSubmitting(false);
|
|
3132
3497
|
setIsStreaming(false);
|
|
3498
|
+
telemetryEndTurn({ error: e instanceof Error ? e.message : String(e) });
|
|
3133
3499
|
}
|
|
3134
3500
|
}
|
|
3135
3501
|
});
|
|
@@ -6031,10 +6397,10 @@ var EnhancedMobileConversationsModal = ({
|
|
|
6031
6397
|
useEffect10(() => {
|
|
6032
6398
|
setDeletedConversationIds((prev) => {
|
|
6033
6399
|
let changed = false;
|
|
6034
|
-
const
|
|
6400
|
+
const active2 = new Set(conversations.map((conv) => conv.id));
|
|
6035
6401
|
const next = /* @__PURE__ */ new Set();
|
|
6036
6402
|
prev.forEach((id) => {
|
|
6037
|
-
if (
|
|
6403
|
+
if (active2.has(id)) {
|
|
6038
6404
|
next.add(id);
|
|
6039
6405
|
} else {
|
|
6040
6406
|
changed = true;
|
|
@@ -9220,4 +9586,4 @@ var chat_default = Chat;
|
|
|
9220
9586
|
export {
|
|
9221
9587
|
chat_default
|
|
9222
9588
|
};
|
|
9223
|
-
//# sourceMappingURL=chunk-
|
|
9589
|
+
//# sourceMappingURL=chunk-MFDMM5MS.mjs.map
|