@lvce-editor/chat-debug-view 5.3.0 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chatDebugViewWorkerMain.js +569 -395
- package/package.json +1 -1
|
@@ -1171,14 +1171,414 @@ const diff2 = uid => {
|
|
|
1171
1171
|
return diff(oldState, newState);
|
|
1172
1172
|
};
|
|
1173
1173
|
|
|
1174
|
+
const handleCloseDetails = state => {
|
|
1175
|
+
return {
|
|
1176
|
+
...state,
|
|
1177
|
+
selectedEvent: null,
|
|
1178
|
+
selectedEventId: null,
|
|
1179
|
+
selectedEventIndex: null
|
|
1180
|
+
};
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1174
1183
|
const handleDetailsContextMenu = state => {
|
|
1175
1184
|
return state;
|
|
1176
1185
|
};
|
|
1177
1186
|
|
|
1187
|
+
const handleDetailTab = (state, value) => {
|
|
1188
|
+
if (!isDetailTab(value)) {
|
|
1189
|
+
return state;
|
|
1190
|
+
}
|
|
1191
|
+
return {
|
|
1192
|
+
...state,
|
|
1193
|
+
selectedDetailTab: value
|
|
1194
|
+
};
|
|
1195
|
+
};
|
|
1196
|
+
|
|
1197
|
+
const hasMatchingToolName$1 = (startedEvent, finishedEvent) => {
|
|
1198
|
+
if (typeof startedEvent.toolName === 'string' && typeof finishedEvent.toolName === 'string') {
|
|
1199
|
+
return startedEvent.toolName === finishedEvent.toolName;
|
|
1200
|
+
}
|
|
1201
|
+
return true;
|
|
1202
|
+
};
|
|
1203
|
+
|
|
1204
|
+
const isMatchingToolExecutionPair = (startedEvent, finishedEvent) => {
|
|
1205
|
+
return startedEvent.sessionId === finishedEvent.sessionId && hasMatchingToolName$1(startedEvent, finishedEvent);
|
|
1206
|
+
};
|
|
1207
|
+
|
|
1208
|
+
const startedEventType$1 = 'tool-execution-started';
|
|
1209
|
+
const finishedEventType$1 = 'tool-execution-finished';
|
|
1210
|
+
const mergedEventType = 'tool-execution';
|
|
1211
|
+
|
|
1212
|
+
const isToolExecutionFinishedEvent = event => {
|
|
1213
|
+
return event.type === finishedEventType$1;
|
|
1214
|
+
};
|
|
1215
|
+
|
|
1216
|
+
const isToolExecutionStartedEvent = event => {
|
|
1217
|
+
return event.type === startedEventType$1;
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
const getTimestamp$1 = value => {
|
|
1221
|
+
return typeof value === 'string' || typeof value === 'number' ? value : undefined;
|
|
1222
|
+
};
|
|
1223
|
+
|
|
1224
|
+
const getEndedTimestamp = event => {
|
|
1225
|
+
return getTimestamp$1(event.ended) ?? getTimestamp$1(event.endTime) ?? getTimestamp$1(event.endTimestamp) ?? getTimestamp$1(event.timestamp);
|
|
1226
|
+
};
|
|
1227
|
+
|
|
1228
|
+
const eventStableIds = new WeakMap();
|
|
1229
|
+
const eventStableIdState = {
|
|
1230
|
+
nextStableEventId: 1
|
|
1231
|
+
};
|
|
1232
|
+
|
|
1233
|
+
const getOrCreateStableEventId = event => {
|
|
1234
|
+
const existingStableEventId = eventStableIds.get(event);
|
|
1235
|
+
if (existingStableEventId) {
|
|
1236
|
+
return existingStableEventId;
|
|
1237
|
+
}
|
|
1238
|
+
const stableEventId = `event-${eventStableIdState.nextStableEventId++}`;
|
|
1239
|
+
eventStableIds.set(event, stableEventId);
|
|
1240
|
+
return stableEventId;
|
|
1241
|
+
};
|
|
1242
|
+
|
|
1243
|
+
const getStartedTimestamp = event => {
|
|
1244
|
+
return getTimestamp$1(event.started) ?? getTimestamp$1(event.startTime) ?? getTimestamp$1(event.startTimestamp) ?? getTimestamp$1(event.timestamp);
|
|
1245
|
+
};
|
|
1246
|
+
|
|
1247
|
+
const setStableEventId = (event, stableEventId) => {
|
|
1248
|
+
eventStableIds.set(event, stableEventId);
|
|
1249
|
+
};
|
|
1250
|
+
|
|
1251
|
+
const mergeToolExecutionEvents$1 = (startedEvent, finishedEvent) => {
|
|
1252
|
+
const ended = getEndedTimestamp(finishedEvent);
|
|
1253
|
+
const {
|
|
1254
|
+
eventId
|
|
1255
|
+
} = startedEvent;
|
|
1256
|
+
const started = getStartedTimestamp(startedEvent);
|
|
1257
|
+
const mergedEvent = {
|
|
1258
|
+
...startedEvent,
|
|
1259
|
+
...finishedEvent,
|
|
1260
|
+
...(ended === undefined ? {} : {
|
|
1261
|
+
ended
|
|
1262
|
+
}),
|
|
1263
|
+
...(eventId === undefined ? {} : {
|
|
1264
|
+
eventId
|
|
1265
|
+
}),
|
|
1266
|
+
...(started === undefined ? {} : {
|
|
1267
|
+
started
|
|
1268
|
+
}),
|
|
1269
|
+
type: mergedEventType
|
|
1270
|
+
};
|
|
1271
|
+
const stableEventId = `${getOrCreateStableEventId(startedEvent)}:${getOrCreateStableEventId(finishedEvent)}`;
|
|
1272
|
+
setStableEventId(mergedEvent, stableEventId);
|
|
1273
|
+
return mergedEvent;
|
|
1274
|
+
};
|
|
1275
|
+
|
|
1276
|
+
const getStableEventId = event => {
|
|
1277
|
+
return getOrCreateStableEventId(event);
|
|
1278
|
+
};
|
|
1279
|
+
|
|
1280
|
+
const collapseToolExecutionEvents = events => {
|
|
1281
|
+
const collapsedEvents = [];
|
|
1282
|
+
for (let i = 0; i < events.length; i++) {
|
|
1283
|
+
const event = events[i];
|
|
1284
|
+
if (isToolExecutionStartedEvent(event)) {
|
|
1285
|
+
const nextEvent = events[i + 1];
|
|
1286
|
+
if (nextEvent && isToolExecutionFinishedEvent(nextEvent) && isMatchingToolExecutionPair(event, nextEvent)) {
|
|
1287
|
+
collapsedEvents.push(mergeToolExecutionEvents$1(event, nextEvent));
|
|
1288
|
+
i++;
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
collapsedEvents.push(event);
|
|
1293
|
+
}
|
|
1294
|
+
return collapsedEvents;
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1297
|
+
const getVisibleEvents = (events, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents) => {
|
|
1298
|
+
return events.filter(event => {
|
|
1299
|
+
if (!showInputEvents && event.type === 'handle-input') {
|
|
1300
|
+
return false;
|
|
1301
|
+
}
|
|
1302
|
+
if (!showResponsePartEvents && event.type === 'sse-response-part') {
|
|
1303
|
+
return false;
|
|
1304
|
+
}
|
|
1305
|
+
if (!showEventStreamFinishedEvents && event.type === 'event-stream-finished') {
|
|
1306
|
+
return false;
|
|
1307
|
+
}
|
|
1308
|
+
// hide session creation events by default — not useful in the debug view
|
|
1309
|
+
if (event.type === 'chat-session-created') {
|
|
1310
|
+
return false;
|
|
1311
|
+
}
|
|
1312
|
+
return true;
|
|
1313
|
+
});
|
|
1314
|
+
};
|
|
1315
|
+
|
|
1316
|
+
const isNetworkEvent = event => {
|
|
1317
|
+
const normalizedType = event.type.toLowerCase();
|
|
1318
|
+
return normalizedType === 'request' || normalizedType === 'response' || normalizedType === 'handle-response' || normalizedType.includes('fetch') || normalizedType.includes('xhr');
|
|
1319
|
+
};
|
|
1320
|
+
|
|
1321
|
+
const isStreamEvent = event => {
|
|
1322
|
+
return event.type === 'sse-response-part' || event.type === 'event-stream-finished';
|
|
1323
|
+
};
|
|
1324
|
+
|
|
1325
|
+
const toolEventTypePrefix = 'tool-execution';
|
|
1326
|
+
const isToolEvent = event => {
|
|
1327
|
+
return event.type.startsWith(toolEventTypePrefix);
|
|
1328
|
+
};
|
|
1329
|
+
|
|
1330
|
+
const isUiEvent = event => {
|
|
1331
|
+
return event.type.startsWith('handle-') && event.type !== 'handle-response';
|
|
1332
|
+
};
|
|
1333
|
+
|
|
1334
|
+
const matchesEventCategoryFilter = (event, eventCategoryFilter) => {
|
|
1335
|
+
switch (eventCategoryFilter) {
|
|
1336
|
+
case Network:
|
|
1337
|
+
return isNetworkEvent(event);
|
|
1338
|
+
case Stream:
|
|
1339
|
+
return isStreamEvent(event);
|
|
1340
|
+
case Tools:
|
|
1341
|
+
return isToolEvent(event);
|
|
1342
|
+
case Ui:
|
|
1343
|
+
return isUiEvent(event);
|
|
1344
|
+
default:
|
|
1345
|
+
return true;
|
|
1346
|
+
}
|
|
1347
|
+
};
|
|
1348
|
+
|
|
1349
|
+
const getFilteredEvents = (events, filterValue, eventCategoryFilter, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents) => {
|
|
1350
|
+
const visibleEvents = getVisibleEvents(events, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents);
|
|
1351
|
+
const collapsedEvents = collapseToolExecutionEvents(visibleEvents);
|
|
1352
|
+
const parsedFilter = parseFilterValue(filterValue);
|
|
1353
|
+
const activeEventCategoryFilter = parsedFilter.eventCategoryFilter === All ? eventCategoryFilter : parsedFilter.eventCategoryFilter;
|
|
1354
|
+
const filteredByCategory = collapsedEvents.filter(event => matchesEventCategoryFilter(event, activeEventCategoryFilter));
|
|
1355
|
+
const {
|
|
1356
|
+
filterText
|
|
1357
|
+
} = parsedFilter;
|
|
1358
|
+
if (!filterText) {
|
|
1359
|
+
return filteredByCategory;
|
|
1360
|
+
}
|
|
1361
|
+
return filteredByCategory.filter(event => JSON.stringify(event).toLowerCase().includes(filterText));
|
|
1362
|
+
};
|
|
1363
|
+
|
|
1364
|
+
const toTimeNumber = value => {
|
|
1365
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
1366
|
+
return value;
|
|
1367
|
+
}
|
|
1368
|
+
if (typeof value === 'string') {
|
|
1369
|
+
const timestamp = Date.parse(value);
|
|
1370
|
+
if (!Number.isNaN(timestamp)) {
|
|
1371
|
+
return timestamp;
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
return undefined;
|
|
1375
|
+
};
|
|
1376
|
+
|
|
1377
|
+
const getEventTime = event => {
|
|
1378
|
+
return toTimeNumber(event.started ?? event.startTime ?? event.startTimestamp ?? event.timestamp);
|
|
1379
|
+
};
|
|
1380
|
+
|
|
1381
|
+
const maxBarUnits = 8;
|
|
1382
|
+
const parseTimelineSeconds = value => {
|
|
1383
|
+
const trimmed = value.trim();
|
|
1384
|
+
if (!trimmed) {
|
|
1385
|
+
return undefined;
|
|
1386
|
+
}
|
|
1387
|
+
const parsed = Number.parseFloat(trimmed);
|
|
1388
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
1389
|
+
return undefined;
|
|
1390
|
+
}
|
|
1391
|
+
return parsed;
|
|
1392
|
+
};
|
|
1393
|
+
const roundSeconds = value => {
|
|
1394
|
+
return Number(value.toFixed(3));
|
|
1395
|
+
};
|
|
1396
|
+
const getEventsWithTime = events => {
|
|
1397
|
+
return events.flatMap(event => {
|
|
1398
|
+
const time = getEventTime(event);
|
|
1399
|
+
if (time === undefined) {
|
|
1400
|
+
return [];
|
|
1401
|
+
}
|
|
1402
|
+
return [{
|
|
1403
|
+
event,
|
|
1404
|
+
time
|
|
1405
|
+
}];
|
|
1406
|
+
});
|
|
1407
|
+
};
|
|
1408
|
+
const getTimelineDurationSeconds = events => {
|
|
1409
|
+
const eventsWithTime = getEventsWithTime(events);
|
|
1410
|
+
if (eventsWithTime.length === 0) {
|
|
1411
|
+
return 0;
|
|
1412
|
+
}
|
|
1413
|
+
const baseTime = eventsWithTime[0].time;
|
|
1414
|
+
const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
|
|
1415
|
+
return roundSeconds(Math.max(0, lastTime - baseTime) / 1000);
|
|
1416
|
+
};
|
|
1417
|
+
const getSelectionPercent = (value, durationSeconds) => {
|
|
1418
|
+
if (durationSeconds <= 0) {
|
|
1419
|
+
return 0;
|
|
1420
|
+
}
|
|
1421
|
+
return Number((value / durationSeconds * 100).toFixed(3));
|
|
1422
|
+
};
|
|
1423
|
+
const getNormalizedRange = (durationSeconds, startValue, endValue) => {
|
|
1424
|
+
const parsedStart = parseTimelineSeconds(startValue);
|
|
1425
|
+
const parsedEnd = parseTimelineSeconds(endValue);
|
|
1426
|
+
if (parsedStart === undefined && parsedEnd === undefined) {
|
|
1427
|
+
return {
|
|
1428
|
+
endSeconds: null,
|
|
1429
|
+
hasSelection: false,
|
|
1430
|
+
startSeconds: null
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
const rawStart = parsedStart ?? 0;
|
|
1434
|
+
const rawEnd = parsedEnd ?? durationSeconds;
|
|
1435
|
+
const normalizedStart = Math.max(0, Math.min(durationSeconds, Math.min(rawStart, rawEnd)));
|
|
1436
|
+
const normalizedEnd = Math.max(0, Math.min(durationSeconds, Math.max(rawStart, rawEnd)));
|
|
1437
|
+
return {
|
|
1438
|
+
endSeconds: roundSeconds(normalizedEnd),
|
|
1439
|
+
hasSelection: true,
|
|
1440
|
+
startSeconds: roundSeconds(normalizedStart)
|
|
1441
|
+
};
|
|
1442
|
+
};
|
|
1443
|
+
const filterEventsByTimelineRange = (events, startValue, endValue) => {
|
|
1444
|
+
const eventsWithTime = getEventsWithTime(events);
|
|
1445
|
+
if (eventsWithTime.length === 0) {
|
|
1446
|
+
return events;
|
|
1447
|
+
}
|
|
1448
|
+
const baseTime = eventsWithTime[0].time;
|
|
1449
|
+
const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
|
|
1450
|
+
const durationSeconds = roundSeconds(Math.max(0, lastTime - baseTime) / 1000);
|
|
1451
|
+
const range = getNormalizedRange(durationSeconds, startValue, endValue);
|
|
1452
|
+
if (!range.hasSelection || range.startSeconds === null || range.endSeconds === null) {
|
|
1453
|
+
return events;
|
|
1454
|
+
}
|
|
1455
|
+
const startTime = baseTime + range.startSeconds * 1000;
|
|
1456
|
+
const endTime = baseTime + range.endSeconds * 1000;
|
|
1457
|
+
return eventsWithTime.filter(item => item.time >= startTime && item.time <= endTime).map(item => item.event);
|
|
1458
|
+
};
|
|
1459
|
+
const getTimelineInfo = (events, startValue, endValue) => {
|
|
1460
|
+
const eventsWithTime = getEventsWithTime(events);
|
|
1461
|
+
if (eventsWithTime.length === 0) {
|
|
1462
|
+
return {
|
|
1463
|
+
buckets: [],
|
|
1464
|
+
durationSeconds: 0,
|
|
1465
|
+
endSeconds: null,
|
|
1466
|
+
hasSelection: false,
|
|
1467
|
+
selectionEndPercent: null,
|
|
1468
|
+
selectionStartPercent: null,
|
|
1469
|
+
startSeconds: null
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
const baseTime = eventsWithTime[0].time;
|
|
1473
|
+
const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
|
|
1474
|
+
const durationMs = Math.max(0, lastTime - baseTime);
|
|
1475
|
+
const durationSeconds = roundSeconds(durationMs / 1000);
|
|
1476
|
+
const range = getNormalizedRange(durationSeconds, startValue, endValue);
|
|
1477
|
+
const bucketCount = durationSeconds === 0 ? 1 : Math.max(12, Math.min(48, Math.ceil(durationSeconds)));
|
|
1478
|
+
const bucketDurationMs = durationMs === 0 ? 1000 : durationMs / bucketCount;
|
|
1479
|
+
const counts = Array.from({
|
|
1480
|
+
length: bucketCount
|
|
1481
|
+
}).fill(0);
|
|
1482
|
+
for (const item of eventsWithTime) {
|
|
1483
|
+
const offsetMs = item.time - baseTime;
|
|
1484
|
+
const index = durationMs === 0 ? 0 : Math.min(bucketCount - 1, Math.floor(offsetMs / durationMs * bucketCount));
|
|
1485
|
+
counts[index] += 1;
|
|
1486
|
+
}
|
|
1487
|
+
const maxCount = Math.max(...counts);
|
|
1488
|
+
const selectionStartPercent = range.hasSelection && range.startSeconds !== null ? getSelectionPercent(range.startSeconds, durationSeconds) : null;
|
|
1489
|
+
const selectionEndPercent = range.hasSelection && range.endSeconds !== null ? getSelectionPercent(range.endSeconds, durationSeconds) : null;
|
|
1490
|
+
const buckets = counts.map((count, index) => {
|
|
1491
|
+
const bucketStartMs = index * bucketDurationMs;
|
|
1492
|
+
const bucketEndMs = index === bucketCount - 1 ? durationMs : (index + 1) * bucketDurationMs;
|
|
1493
|
+
const hasSelection = range.hasSelection && range.startSeconds !== null && range.endSeconds !== null;
|
|
1494
|
+
const selectionStartMs = hasSelection ? range.startSeconds * 1000 : 0;
|
|
1495
|
+
const selectionEndMs = hasSelection ? range.endSeconds * 1000 : 0;
|
|
1496
|
+
return {
|
|
1497
|
+
count,
|
|
1498
|
+
endSeconds: roundSeconds(bucketEndMs / 1000),
|
|
1499
|
+
isSelected: hasSelection && bucketEndMs >= selectionStartMs && bucketStartMs <= selectionEndMs,
|
|
1500
|
+
startSeconds: roundSeconds(bucketStartMs / 1000),
|
|
1501
|
+
unitCount: count === 0 ? 0 : Math.max(1, Math.round(count / maxCount * maxBarUnits))
|
|
1502
|
+
};
|
|
1503
|
+
});
|
|
1504
|
+
return {
|
|
1505
|
+
buckets,
|
|
1506
|
+
durationSeconds,
|
|
1507
|
+
endSeconds: range.endSeconds,
|
|
1508
|
+
hasSelection: range.hasSelection,
|
|
1509
|
+
selectionEndPercent,
|
|
1510
|
+
selectionStartPercent,
|
|
1511
|
+
startSeconds: range.startSeconds
|
|
1512
|
+
};
|
|
1513
|
+
};
|
|
1514
|
+
|
|
1515
|
+
const getCurrentEvents$3 = state => {
|
|
1516
|
+
const filteredEvents = getFilteredEvents(state.events, state.filterValue, state.eventCategoryFilter, state.showInputEvents, state.showResponsePartEvents, state.showEventStreamFinishedEvents);
|
|
1517
|
+
return filterEventsByTimelineRange(filteredEvents, state.timelineStartSeconds, state.timelineEndSeconds);
|
|
1518
|
+
};
|
|
1519
|
+
const getEventIndexByStableId$1 = (events, event) => {
|
|
1520
|
+
const stableEventId = getStableEventId(event);
|
|
1521
|
+
return events.findIndex(candidate => getStableEventId(candidate) === stableEventId);
|
|
1522
|
+
};
|
|
1523
|
+
const getSelectedEventIndex$1 = state => {
|
|
1524
|
+
const {
|
|
1525
|
+
selectedEventIndex
|
|
1526
|
+
} = state;
|
|
1527
|
+
if (selectedEventIndex === null) {
|
|
1528
|
+
return null;
|
|
1529
|
+
}
|
|
1530
|
+
const filteredEvents = getCurrentEvents$3(state);
|
|
1531
|
+
const selectedEvent = filteredEvents[selectedEventIndex];
|
|
1532
|
+
if (!selectedEvent) {
|
|
1533
|
+
return null;
|
|
1534
|
+
}
|
|
1535
|
+
const newIndex = getEventIndexByStableId$1(filteredEvents, selectedEvent);
|
|
1536
|
+
if (newIndex === -1) {
|
|
1537
|
+
return null;
|
|
1538
|
+
}
|
|
1539
|
+
return newIndex;
|
|
1540
|
+
};
|
|
1541
|
+
const getPreservedSelectedEventIndex$1 = (oldState, newState) => {
|
|
1542
|
+
const {
|
|
1543
|
+
selectedEventIndex
|
|
1544
|
+
} = oldState;
|
|
1545
|
+
if (selectedEventIndex === null) {
|
|
1546
|
+
return null;
|
|
1547
|
+
}
|
|
1548
|
+
const oldFilteredEvents = getCurrentEvents$3(oldState);
|
|
1549
|
+
const selectedEvent = oldFilteredEvents[selectedEventIndex];
|
|
1550
|
+
if (!selectedEvent) {
|
|
1551
|
+
return null;
|
|
1552
|
+
}
|
|
1553
|
+
const newFilteredEvents = getCurrentEvents$3(newState);
|
|
1554
|
+
const newIndex = getEventIndexByStableId$1(newFilteredEvents, selectedEvent);
|
|
1555
|
+
if (newIndex === -1) {
|
|
1556
|
+
return null;
|
|
1557
|
+
}
|
|
1558
|
+
return newIndex;
|
|
1559
|
+
};
|
|
1560
|
+
const withPreservedSelection$1 = (state, nextState) => {
|
|
1561
|
+
const selectedEventIndex = getPreservedSelectedEventIndex$1(state, nextState);
|
|
1562
|
+
return {
|
|
1563
|
+
...nextState,
|
|
1564
|
+
selectedEvent: selectedEventIndex === null ? null : state.selectedEvent,
|
|
1565
|
+
selectedEventId: selectedEventIndex === null ? null : state.selectedEventId,
|
|
1566
|
+
selectedEventIndex
|
|
1567
|
+
};
|
|
1568
|
+
};
|
|
1569
|
+
|
|
1570
|
+
const handleEventCategoryFilter = (state, value) => {
|
|
1571
|
+
const nextState = {
|
|
1572
|
+
...state,
|
|
1573
|
+
eventCategoryFilter: value || All
|
|
1574
|
+
};
|
|
1575
|
+
return withPreservedSelection$1(state, nextState);
|
|
1576
|
+
};
|
|
1577
|
+
|
|
1178
1578
|
// cspell:ignore IDBP
|
|
1179
1579
|
|
|
1180
|
-
const startedEventType
|
|
1181
|
-
const finishedEventType
|
|
1580
|
+
const startedEventType = 'tool-execution-started';
|
|
1581
|
+
const finishedEventType = 'tool-execution-finished';
|
|
1182
1582
|
const getRawEventBySessionIdAndEventId = async (store, sessionId, sessionIdIndexName, eventId) => {
|
|
1183
1583
|
if (eventId < 1) {
|
|
1184
1584
|
return undefined;
|
|
@@ -1200,18 +1600,18 @@ const getRawEventBySessionIdAndEventId = async (store, sessionId, sessionIdIndex
|
|
|
1200
1600
|
const events = all.filter(event => event.sessionId === sessionId);
|
|
1201
1601
|
return events[eventId - 1];
|
|
1202
1602
|
};
|
|
1203
|
-
const getTimestamp
|
|
1603
|
+
const getTimestamp = value => {
|
|
1204
1604
|
return typeof value === 'string' || typeof value === 'number' ? value : undefined;
|
|
1205
1605
|
};
|
|
1206
|
-
const hasMatchingToolName
|
|
1606
|
+
const hasMatchingToolName = (startedEvent, finishedEvent) => {
|
|
1207
1607
|
if (typeof startedEvent.toolName === 'string' && typeof finishedEvent.toolName === 'string') {
|
|
1208
1608
|
return startedEvent.toolName === finishedEvent.toolName;
|
|
1209
1609
|
}
|
|
1210
1610
|
return true;
|
|
1211
1611
|
};
|
|
1212
|
-
const mergeToolExecutionEvents
|
|
1213
|
-
const ended = getTimestamp
|
|
1214
|
-
const started = getTimestamp
|
|
1612
|
+
const mergeToolExecutionEvents = (startedEvent, finishedEvent, eventId) => {
|
|
1613
|
+
const ended = getTimestamp(finishedEvent.ended) ?? getTimestamp(finishedEvent.endTime) ?? getTimestamp(finishedEvent.timestamp);
|
|
1614
|
+
const started = getTimestamp(startedEvent.started) ?? getTimestamp(startedEvent.startTime) ?? getTimestamp(startedEvent.timestamp);
|
|
1215
1615
|
return {
|
|
1216
1616
|
...startedEvent,
|
|
1217
1617
|
...finishedEvent,
|
|
@@ -1236,20 +1636,20 @@ const getEventDetailsBySessionIdAndEventId = async (store, sessionId, sessionIdI
|
|
|
1236
1636
|
eventId
|
|
1237
1637
|
};
|
|
1238
1638
|
}
|
|
1239
|
-
if (event.type !== startedEventType
|
|
1639
|
+
if (event.type !== startedEventType) {
|
|
1240
1640
|
return {
|
|
1241
1641
|
...event,
|
|
1242
1642
|
eventId
|
|
1243
1643
|
};
|
|
1244
1644
|
}
|
|
1245
1645
|
const nextEvent = await getRawEventBySessionIdAndEventId(store, sessionId, sessionIdIndexName, eventId + 1);
|
|
1246
|
-
if (!nextEvent || nextEvent.type !== finishedEventType
|
|
1646
|
+
if (!nextEvent || nextEvent.type !== finishedEventType || nextEvent.sessionId !== sessionId || !hasMatchingToolName(event, nextEvent)) {
|
|
1247
1647
|
return {
|
|
1248
1648
|
...event,
|
|
1249
1649
|
eventId
|
|
1250
1650
|
};
|
|
1251
1651
|
}
|
|
1252
|
-
return mergeToolExecutionEvents
|
|
1652
|
+
return mergeToolExecutionEvents(event, nextEvent, eventId);
|
|
1253
1653
|
};
|
|
1254
1654
|
|
|
1255
1655
|
const instanceOfAny = (object, constructors) => constructors.some(c => object instanceof c);
|
|
@@ -1473,381 +1873,63 @@ const cursorIteratorTraps = {
|
|
|
1473
1873
|
return cachedFunc;
|
|
1474
1874
|
}
|
|
1475
1875
|
};
|
|
1476
|
-
async function* iterate(...args) {
|
|
1477
|
-
// tslint:disable-next-line:no-this-assignment
|
|
1478
|
-
let cursor = this;
|
|
1479
|
-
if (!(cursor instanceof IDBCursor)) {
|
|
1480
|
-
cursor = await cursor.openCursor(...args);
|
|
1481
|
-
}
|
|
1482
|
-
if (!cursor) return;
|
|
1483
|
-
cursor = cursor;
|
|
1484
|
-
const proxiedCursor = new Proxy(cursor, cursorIteratorTraps);
|
|
1485
|
-
ittrProxiedCursorToOriginalProxy.set(proxiedCursor, cursor);
|
|
1486
|
-
// Map this double-proxy back to the original, so other cursor methods work.
|
|
1487
|
-
reverseTransformCache.set(proxiedCursor, unwrap(cursor));
|
|
1488
|
-
while (cursor) {
|
|
1489
|
-
yield proxiedCursor;
|
|
1490
|
-
// If one of the advancing methods was not called, call continue().
|
|
1491
|
-
cursor = await (advanceResults.get(proxiedCursor) || cursor.continue());
|
|
1492
|
-
advanceResults.delete(proxiedCursor);
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
function isIteratorProp(target, prop) {
|
|
1496
|
-
return prop === Symbol.asyncIterator && instanceOfAny(target, [IDBIndex, IDBObjectStore, IDBCursor]) || prop === 'iterate' && instanceOfAny(target, [IDBIndex, IDBObjectStore]);
|
|
1497
|
-
}
|
|
1498
|
-
replaceTraps(oldTraps => ({
|
|
1499
|
-
...oldTraps,
|
|
1500
|
-
get(target, prop, receiver) {
|
|
1501
|
-
if (isIteratorProp(target, prop)) return iterate;
|
|
1502
|
-
return oldTraps.get(target, prop, receiver);
|
|
1503
|
-
},
|
|
1504
|
-
has(target, prop) {
|
|
1505
|
-
return isIteratorProp(target, prop) || oldTraps.has(target, prop);
|
|
1506
|
-
}
|
|
1507
|
-
}));
|
|
1508
|
-
|
|
1509
|
-
const openDatabaseDependencies = {
|
|
1510
|
-
openDB: openDB
|
|
1511
|
-
};
|
|
1512
|
-
const openDatabase = async (databaseName, dataBaseVersion) => {
|
|
1513
|
-
return openDatabaseDependencies.openDB(databaseName, dataBaseVersion);
|
|
1514
|
-
};
|
|
1515
|
-
|
|
1516
|
-
const loadSelectedEventDependencies = {
|
|
1517
|
-
getEventDetailsBySessionIdAndEventId: getEventDetailsBySessionIdAndEventId,
|
|
1518
|
-
openDatabase: openDatabase
|
|
1519
|
-
};
|
|
1520
|
-
const loadSelectedEvent = async (databaseName, dataBaseVersion, eventStoreName, sessionId, sessionIdIndexName, eventId, type) => {
|
|
1521
|
-
const database = await loadSelectedEventDependencies.openDatabase(databaseName, dataBaseVersion);
|
|
1522
|
-
try {
|
|
1523
|
-
if (!database.objectStoreNames.contains(eventStoreName)) {
|
|
1524
|
-
return null;
|
|
1525
|
-
}
|
|
1526
|
-
const transaction = database.transaction(eventStoreName, 'readonly');
|
|
1527
|
-
const store = transaction.objectStore(eventStoreName);
|
|
1528
|
-
const event = await loadSelectedEventDependencies.getEventDetailsBySessionIdAndEventId(store, sessionId, sessionIdIndexName, eventId, type);
|
|
1529
|
-
return event ?? null;
|
|
1530
|
-
} finally {
|
|
1531
|
-
database.close();
|
|
1532
|
-
}
|
|
1533
|
-
};
|
|
1534
|
-
|
|
1535
|
-
const hasMatchingToolName = (startedEvent, finishedEvent) => {
|
|
1536
|
-
if (typeof startedEvent.toolName === 'string' && typeof finishedEvent.toolName === 'string') {
|
|
1537
|
-
return startedEvent.toolName === finishedEvent.toolName;
|
|
1538
|
-
}
|
|
1539
|
-
return true;
|
|
1540
|
-
};
|
|
1541
|
-
|
|
1542
|
-
const isMatchingToolExecutionPair = (startedEvent, finishedEvent) => {
|
|
1543
|
-
return startedEvent.sessionId === finishedEvent.sessionId && hasMatchingToolName(startedEvent, finishedEvent);
|
|
1544
|
-
};
|
|
1545
|
-
|
|
1546
|
-
const startedEventType = 'tool-execution-started';
|
|
1547
|
-
const finishedEventType = 'tool-execution-finished';
|
|
1548
|
-
const mergedEventType = 'tool-execution';
|
|
1549
|
-
|
|
1550
|
-
const isToolExecutionFinishedEvent = event => {
|
|
1551
|
-
return event.type === finishedEventType;
|
|
1552
|
-
};
|
|
1553
|
-
|
|
1554
|
-
const isToolExecutionStartedEvent = event => {
|
|
1555
|
-
return event.type === startedEventType;
|
|
1556
|
-
};
|
|
1557
|
-
|
|
1558
|
-
const getTimestamp = value => {
|
|
1559
|
-
return typeof value === 'string' || typeof value === 'number' ? value : undefined;
|
|
1560
|
-
};
|
|
1561
|
-
|
|
1562
|
-
const getEndedTimestamp = event => {
|
|
1563
|
-
return getTimestamp(event.ended) ?? getTimestamp(event.endTime) ?? getTimestamp(event.endTimestamp) ?? getTimestamp(event.timestamp);
|
|
1564
|
-
};
|
|
1565
|
-
|
|
1566
|
-
const eventStableIds = new WeakMap();
|
|
1567
|
-
const eventStableIdState = {
|
|
1568
|
-
nextStableEventId: 1
|
|
1569
|
-
};
|
|
1570
|
-
|
|
1571
|
-
const getOrCreateStableEventId = event => {
|
|
1572
|
-
const existingStableEventId = eventStableIds.get(event);
|
|
1573
|
-
if (existingStableEventId) {
|
|
1574
|
-
return existingStableEventId;
|
|
1575
|
-
}
|
|
1576
|
-
const stableEventId = `event-${eventStableIdState.nextStableEventId++}`;
|
|
1577
|
-
eventStableIds.set(event, stableEventId);
|
|
1578
|
-
return stableEventId;
|
|
1579
|
-
};
|
|
1580
|
-
|
|
1581
|
-
const getStartedTimestamp = event => {
|
|
1582
|
-
return getTimestamp(event.started) ?? getTimestamp(event.startTime) ?? getTimestamp(event.startTimestamp) ?? getTimestamp(event.timestamp);
|
|
1583
|
-
};
|
|
1584
|
-
|
|
1585
|
-
const setStableEventId = (event, stableEventId) => {
|
|
1586
|
-
eventStableIds.set(event, stableEventId);
|
|
1587
|
-
};
|
|
1588
|
-
|
|
1589
|
-
const mergeToolExecutionEvents = (startedEvent, finishedEvent) => {
|
|
1590
|
-
const ended = getEndedTimestamp(finishedEvent);
|
|
1591
|
-
const {
|
|
1592
|
-
eventId
|
|
1593
|
-
} = startedEvent;
|
|
1594
|
-
const started = getStartedTimestamp(startedEvent);
|
|
1595
|
-
const mergedEvent = {
|
|
1596
|
-
...startedEvent,
|
|
1597
|
-
...finishedEvent,
|
|
1598
|
-
...(ended === undefined ? {} : {
|
|
1599
|
-
ended
|
|
1600
|
-
}),
|
|
1601
|
-
...(eventId === undefined ? {} : {
|
|
1602
|
-
eventId
|
|
1603
|
-
}),
|
|
1604
|
-
...(started === undefined ? {} : {
|
|
1605
|
-
started
|
|
1606
|
-
}),
|
|
1607
|
-
type: mergedEventType
|
|
1608
|
-
};
|
|
1609
|
-
const stableEventId = `${getOrCreateStableEventId(startedEvent)}:${getOrCreateStableEventId(finishedEvent)}`;
|
|
1610
|
-
setStableEventId(mergedEvent, stableEventId);
|
|
1611
|
-
return mergedEvent;
|
|
1612
|
-
};
|
|
1613
|
-
|
|
1614
|
-
const getStableEventId = event => {
|
|
1615
|
-
return getOrCreateStableEventId(event);
|
|
1616
|
-
};
|
|
1617
|
-
|
|
1618
|
-
const collapseToolExecutionEvents = events => {
|
|
1619
|
-
const collapsedEvents = [];
|
|
1620
|
-
for (let i = 0; i < events.length; i++) {
|
|
1621
|
-
const event = events[i];
|
|
1622
|
-
if (isToolExecutionStartedEvent(event)) {
|
|
1623
|
-
const nextEvent = events[i + 1];
|
|
1624
|
-
if (nextEvent && isToolExecutionFinishedEvent(nextEvent) && isMatchingToolExecutionPair(event, nextEvent)) {
|
|
1625
|
-
collapsedEvents.push(mergeToolExecutionEvents(event, nextEvent));
|
|
1626
|
-
i++;
|
|
1627
|
-
continue;
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1630
|
-
collapsedEvents.push(event);
|
|
1631
|
-
}
|
|
1632
|
-
return collapsedEvents;
|
|
1633
|
-
};
|
|
1634
|
-
|
|
1635
|
-
const getVisibleEvents = (events, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents) => {
|
|
1636
|
-
return events.filter(event => {
|
|
1637
|
-
if (!showInputEvents && event.type === 'handle-input') {
|
|
1638
|
-
return false;
|
|
1639
|
-
}
|
|
1640
|
-
if (!showResponsePartEvents && event.type === 'sse-response-part') {
|
|
1641
|
-
return false;
|
|
1642
|
-
}
|
|
1643
|
-
if (!showEventStreamFinishedEvents && event.type === 'event-stream-finished') {
|
|
1644
|
-
return false;
|
|
1645
|
-
}
|
|
1646
|
-
// hide session creation events by default — not useful in the debug view
|
|
1647
|
-
if (event.type === 'chat-session-created') {
|
|
1648
|
-
return false;
|
|
1649
|
-
}
|
|
1650
|
-
return true;
|
|
1651
|
-
});
|
|
1652
|
-
};
|
|
1653
|
-
|
|
1654
|
-
const isNetworkEvent = event => {
|
|
1655
|
-
const normalizedType = event.type.toLowerCase();
|
|
1656
|
-
return normalizedType === 'request' || normalizedType === 'response' || normalizedType === 'handle-response' || normalizedType.includes('fetch') || normalizedType.includes('xhr');
|
|
1657
|
-
};
|
|
1658
|
-
|
|
1659
|
-
const isStreamEvent = event => {
|
|
1660
|
-
return event.type === 'sse-response-part' || event.type === 'event-stream-finished';
|
|
1661
|
-
};
|
|
1662
|
-
|
|
1663
|
-
const toolEventTypePrefix = 'tool-execution';
|
|
1664
|
-
const isToolEvent = event => {
|
|
1665
|
-
return event.type.startsWith(toolEventTypePrefix);
|
|
1666
|
-
};
|
|
1667
|
-
|
|
1668
|
-
const isUiEvent = event => {
|
|
1669
|
-
return event.type.startsWith('handle-') && event.type !== 'handle-response';
|
|
1670
|
-
};
|
|
1671
|
-
|
|
1672
|
-
const matchesEventCategoryFilter = (event, eventCategoryFilter) => {
|
|
1673
|
-
switch (eventCategoryFilter) {
|
|
1674
|
-
case Network:
|
|
1675
|
-
return isNetworkEvent(event);
|
|
1676
|
-
case Stream:
|
|
1677
|
-
return isStreamEvent(event);
|
|
1678
|
-
case Tools:
|
|
1679
|
-
return isToolEvent(event);
|
|
1680
|
-
case Ui:
|
|
1681
|
-
return isUiEvent(event);
|
|
1682
|
-
default:
|
|
1683
|
-
return true;
|
|
1684
|
-
}
|
|
1685
|
-
};
|
|
1686
|
-
|
|
1687
|
-
const getFilteredEvents = (events, filterValue, eventCategoryFilter, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents) => {
|
|
1688
|
-
const visibleEvents = getVisibleEvents(events, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents);
|
|
1689
|
-
const collapsedEvents = collapseToolExecutionEvents(visibleEvents);
|
|
1690
|
-
const parsedFilter = parseFilterValue(filterValue);
|
|
1691
|
-
const activeEventCategoryFilter = parsedFilter.eventCategoryFilter === All ? eventCategoryFilter : parsedFilter.eventCategoryFilter;
|
|
1692
|
-
const filteredByCategory = collapsedEvents.filter(event => matchesEventCategoryFilter(event, activeEventCategoryFilter));
|
|
1693
|
-
const {
|
|
1694
|
-
filterText
|
|
1695
|
-
} = parsedFilter;
|
|
1696
|
-
if (!filterText) {
|
|
1697
|
-
return filteredByCategory;
|
|
1876
|
+
async function* iterate(...args) {
|
|
1877
|
+
// tslint:disable-next-line:no-this-assignment
|
|
1878
|
+
let cursor = this;
|
|
1879
|
+
if (!(cursor instanceof IDBCursor)) {
|
|
1880
|
+
cursor = await cursor.openCursor(...args);
|
|
1698
1881
|
}
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1882
|
+
if (!cursor) return;
|
|
1883
|
+
cursor = cursor;
|
|
1884
|
+
const proxiedCursor = new Proxy(cursor, cursorIteratorTraps);
|
|
1885
|
+
ittrProxiedCursorToOriginalProxy.set(proxiedCursor, cursor);
|
|
1886
|
+
// Map this double-proxy back to the original, so other cursor methods work.
|
|
1887
|
+
reverseTransformCache.set(proxiedCursor, unwrap(cursor));
|
|
1888
|
+
while (cursor) {
|
|
1889
|
+
yield proxiedCursor;
|
|
1890
|
+
// If one of the advancing methods was not called, call continue().
|
|
1891
|
+
cursor = await (advanceResults.get(proxiedCursor) || cursor.continue());
|
|
1892
|
+
advanceResults.delete(proxiedCursor);
|
|
1705
1893
|
}
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1894
|
+
}
|
|
1895
|
+
function isIteratorProp(target, prop) {
|
|
1896
|
+
return prop === Symbol.asyncIterator && instanceOfAny(target, [IDBIndex, IDBObjectStore, IDBCursor]) || prop === 'iterate' && instanceOfAny(target, [IDBIndex, IDBObjectStore]);
|
|
1897
|
+
}
|
|
1898
|
+
replaceTraps(oldTraps => ({
|
|
1899
|
+
...oldTraps,
|
|
1900
|
+
get(target, prop, receiver) {
|
|
1901
|
+
if (isIteratorProp(target, prop)) return iterate;
|
|
1902
|
+
return oldTraps.get(target, prop, receiver);
|
|
1903
|
+
},
|
|
1904
|
+
has(target, prop) {
|
|
1905
|
+
return isIteratorProp(target, prop) || oldTraps.has(target, prop);
|
|
1711
1906
|
}
|
|
1712
|
-
|
|
1713
|
-
};
|
|
1907
|
+
}));
|
|
1714
1908
|
|
|
1715
|
-
const
|
|
1716
|
-
|
|
1909
|
+
const openDatabaseDependencies = {
|
|
1910
|
+
openDB: openDB
|
|
1717
1911
|
};
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
const parseTimelineSeconds = value => {
|
|
1721
|
-
const trimmed = value.trim();
|
|
1722
|
-
if (!trimmed) {
|
|
1723
|
-
return undefined;
|
|
1724
|
-
}
|
|
1725
|
-
const parsed = Number.parseFloat(trimmed);
|
|
1726
|
-
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
1727
|
-
return undefined;
|
|
1728
|
-
}
|
|
1729
|
-
return parsed;
|
|
1912
|
+
const openDatabase = async (databaseName, dataBaseVersion) => {
|
|
1913
|
+
return openDatabaseDependencies.openDB(databaseName, dataBaseVersion);
|
|
1730
1914
|
};
|
|
1731
|
-
|
|
1732
|
-
|
|
1915
|
+
|
|
1916
|
+
const loadSelectedEventDependencies = {
|
|
1917
|
+
getEventDetailsBySessionIdAndEventId: getEventDetailsBySessionIdAndEventId,
|
|
1918
|
+
openDatabase: openDatabase
|
|
1733
1919
|
};
|
|
1734
|
-
const
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
if (
|
|
1738
|
-
return
|
|
1920
|
+
const loadSelectedEvent = async (databaseName, dataBaseVersion, eventStoreName, sessionId, sessionIdIndexName, eventId, type) => {
|
|
1921
|
+
const database = await loadSelectedEventDependencies.openDatabase(databaseName, dataBaseVersion);
|
|
1922
|
+
try {
|
|
1923
|
+
if (!database.objectStoreNames.contains(eventStoreName)) {
|
|
1924
|
+
return null;
|
|
1739
1925
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
const getTimelineDurationSeconds = events => {
|
|
1747
|
-
const eventsWithTime = getEventsWithTime(events);
|
|
1748
|
-
if (eventsWithTime.length === 0) {
|
|
1749
|
-
return 0;
|
|
1750
|
-
}
|
|
1751
|
-
const baseTime = eventsWithTime[0].time;
|
|
1752
|
-
const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
|
|
1753
|
-
return roundSeconds(Math.max(0, lastTime - baseTime) / 1000);
|
|
1754
|
-
};
|
|
1755
|
-
const getSelectionPercent = (value, durationSeconds) => {
|
|
1756
|
-
if (durationSeconds <= 0) {
|
|
1757
|
-
return 0;
|
|
1758
|
-
}
|
|
1759
|
-
return Number((value / durationSeconds * 100).toFixed(3));
|
|
1760
|
-
};
|
|
1761
|
-
const getNormalizedRange = (durationSeconds, startValue, endValue) => {
|
|
1762
|
-
const parsedStart = parseTimelineSeconds(startValue);
|
|
1763
|
-
const parsedEnd = parseTimelineSeconds(endValue);
|
|
1764
|
-
if (parsedStart === undefined && parsedEnd === undefined) {
|
|
1765
|
-
return {
|
|
1766
|
-
endSeconds: null,
|
|
1767
|
-
hasSelection: false,
|
|
1768
|
-
startSeconds: null
|
|
1769
|
-
};
|
|
1770
|
-
}
|
|
1771
|
-
const rawStart = parsedStart ?? 0;
|
|
1772
|
-
const rawEnd = parsedEnd ?? durationSeconds;
|
|
1773
|
-
const normalizedStart = Math.max(0, Math.min(durationSeconds, Math.min(rawStart, rawEnd)));
|
|
1774
|
-
const normalizedEnd = Math.max(0, Math.min(durationSeconds, Math.max(rawStart, rawEnd)));
|
|
1775
|
-
return {
|
|
1776
|
-
endSeconds: roundSeconds(normalizedEnd),
|
|
1777
|
-
hasSelection: true,
|
|
1778
|
-
startSeconds: roundSeconds(normalizedStart)
|
|
1779
|
-
};
|
|
1780
|
-
};
|
|
1781
|
-
const filterEventsByTimelineRange = (events, startValue, endValue) => {
|
|
1782
|
-
const eventsWithTime = getEventsWithTime(events);
|
|
1783
|
-
if (eventsWithTime.length === 0) {
|
|
1784
|
-
return events;
|
|
1785
|
-
}
|
|
1786
|
-
const baseTime = eventsWithTime[0].time;
|
|
1787
|
-
const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
|
|
1788
|
-
const durationSeconds = roundSeconds(Math.max(0, lastTime - baseTime) / 1000);
|
|
1789
|
-
const range = getNormalizedRange(durationSeconds, startValue, endValue);
|
|
1790
|
-
if (!range.hasSelection || range.startSeconds === null || range.endSeconds === null) {
|
|
1791
|
-
return events;
|
|
1792
|
-
}
|
|
1793
|
-
const startTime = baseTime + range.startSeconds * 1000;
|
|
1794
|
-
const endTime = baseTime + range.endSeconds * 1000;
|
|
1795
|
-
return eventsWithTime.filter(item => item.time >= startTime && item.time <= endTime).map(item => item.event);
|
|
1796
|
-
};
|
|
1797
|
-
const getTimelineInfo = (events, startValue, endValue) => {
|
|
1798
|
-
const eventsWithTime = getEventsWithTime(events);
|
|
1799
|
-
if (eventsWithTime.length === 0) {
|
|
1800
|
-
return {
|
|
1801
|
-
buckets: [],
|
|
1802
|
-
durationSeconds: 0,
|
|
1803
|
-
endSeconds: null,
|
|
1804
|
-
hasSelection: false,
|
|
1805
|
-
selectionEndPercent: null,
|
|
1806
|
-
selectionStartPercent: null,
|
|
1807
|
-
startSeconds: null
|
|
1808
|
-
};
|
|
1809
|
-
}
|
|
1810
|
-
const baseTime = eventsWithTime[0].time;
|
|
1811
|
-
const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
|
|
1812
|
-
const durationMs = Math.max(0, lastTime - baseTime);
|
|
1813
|
-
const durationSeconds = roundSeconds(durationMs / 1000);
|
|
1814
|
-
const range = getNormalizedRange(durationSeconds, startValue, endValue);
|
|
1815
|
-
const bucketCount = durationSeconds === 0 ? 1 : Math.max(12, Math.min(48, Math.ceil(durationSeconds)));
|
|
1816
|
-
const bucketDurationMs = durationMs === 0 ? 1000 : durationMs / bucketCount;
|
|
1817
|
-
const counts = Array.from({
|
|
1818
|
-
length: bucketCount
|
|
1819
|
-
}).fill(0);
|
|
1820
|
-
for (const item of eventsWithTime) {
|
|
1821
|
-
const offsetMs = item.time - baseTime;
|
|
1822
|
-
const index = durationMs === 0 ? 0 : Math.min(bucketCount - 1, Math.floor(offsetMs / durationMs * bucketCount));
|
|
1823
|
-
counts[index] += 1;
|
|
1926
|
+
const transaction = database.transaction(eventStoreName, 'readonly');
|
|
1927
|
+
const store = transaction.objectStore(eventStoreName);
|
|
1928
|
+
const event = await loadSelectedEventDependencies.getEventDetailsBySessionIdAndEventId(store, sessionId, sessionIdIndexName, eventId, type);
|
|
1929
|
+
return event ?? null;
|
|
1930
|
+
} finally {
|
|
1931
|
+
database.close();
|
|
1824
1932
|
}
|
|
1825
|
-
const maxCount = Math.max(...counts);
|
|
1826
|
-
const selectionStartPercent = range.hasSelection && range.startSeconds !== null ? getSelectionPercent(range.startSeconds, durationSeconds) : null;
|
|
1827
|
-
const selectionEndPercent = range.hasSelection && range.endSeconds !== null ? getSelectionPercent(range.endSeconds, durationSeconds) : null;
|
|
1828
|
-
const buckets = counts.map((count, index) => {
|
|
1829
|
-
const bucketStartMs = index * bucketDurationMs;
|
|
1830
|
-
const bucketEndMs = index === bucketCount - 1 ? durationMs : (index + 1) * bucketDurationMs;
|
|
1831
|
-
const hasSelection = range.hasSelection && range.startSeconds !== null && range.endSeconds !== null;
|
|
1832
|
-
const selectionStartMs = hasSelection ? range.startSeconds * 1000 : 0;
|
|
1833
|
-
const selectionEndMs = hasSelection ? range.endSeconds * 1000 : 0;
|
|
1834
|
-
return {
|
|
1835
|
-
count,
|
|
1836
|
-
endSeconds: roundSeconds(bucketEndMs / 1000),
|
|
1837
|
-
isSelected: hasSelection && bucketEndMs >= selectionStartMs && bucketStartMs <= selectionEndMs,
|
|
1838
|
-
startSeconds: roundSeconds(bucketStartMs / 1000),
|
|
1839
|
-
unitCount: count === 0 ? 0 : Math.max(1, Math.round(count / maxCount * maxBarUnits))
|
|
1840
|
-
};
|
|
1841
|
-
});
|
|
1842
|
-
return {
|
|
1843
|
-
buckets,
|
|
1844
|
-
durationSeconds,
|
|
1845
|
-
endSeconds: range.endSeconds,
|
|
1846
|
-
hasSelection: range.hasSelection,
|
|
1847
|
-
selectionEndPercent,
|
|
1848
|
-
selectionStartPercent,
|
|
1849
|
-
startSeconds: range.startSeconds
|
|
1850
|
-
};
|
|
1851
1933
|
};
|
|
1852
1934
|
|
|
1853
1935
|
const getCurrentEvents$2 = state => {
|
|
@@ -1895,7 +1977,7 @@ const parseSelectedEventIndex$1 = value => {
|
|
|
1895
1977
|
}
|
|
1896
1978
|
return parsed;
|
|
1897
1979
|
};
|
|
1898
|
-
const handleEventRowClick = async (state, value, button) => {
|
|
1980
|
+
const handleEventRowClick = async (state, value, button = 0) => {
|
|
1899
1981
|
if (!isPrimaryButton(button)) {
|
|
1900
1982
|
return state;
|
|
1901
1983
|
}
|
|
@@ -1931,7 +2013,7 @@ const getCurrentEvents$1 = state => {
|
|
|
1931
2013
|
const filteredEvents = getFilteredEvents(state.events, state.filterValue, state.eventCategoryFilter, state.showInputEvents, state.showResponsePartEvents, state.showEventStreamFinishedEvents);
|
|
1932
2014
|
return filterEventsByTimelineRange(filteredEvents, state.timelineStartSeconds, state.timelineEndSeconds);
|
|
1933
2015
|
};
|
|
1934
|
-
const parseTimelineRangePreset = value => {
|
|
2016
|
+
const parseTimelineRangePreset$1 = value => {
|
|
1935
2017
|
if (!value) {
|
|
1936
2018
|
return {
|
|
1937
2019
|
timelineEndSeconds: '',
|
|
@@ -2074,7 +2156,7 @@ const handleInput = (state, name, value, checked) => {
|
|
|
2074
2156
|
if (name === TimelineRangePreset) {
|
|
2075
2157
|
const nextState = {
|
|
2076
2158
|
...state,
|
|
2077
|
-
...parseTimelineRangePreset(value)
|
|
2159
|
+
...parseTimelineRangePreset$1(value)
|
|
2078
2160
|
};
|
|
2079
2161
|
return withPreservedSelection(state, nextState);
|
|
2080
2162
|
}
|
|
@@ -2126,8 +2208,43 @@ const clearTimelineSelectionState = state => {
|
|
|
2126
2208
|
};
|
|
2127
2209
|
};
|
|
2128
2210
|
|
|
2211
|
+
const parseTimelineRangePreset = value => {
|
|
2212
|
+
if (!value) {
|
|
2213
|
+
return {
|
|
2214
|
+
timelineEndSeconds: '',
|
|
2215
|
+
timelineStartSeconds: ''
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
2218
|
+
const [timelineStartSeconds = '', timelineEndSeconds = ''] = value.split(':', 2);
|
|
2219
|
+
return {
|
|
2220
|
+
timelineEndSeconds,
|
|
2221
|
+
timelineStartSeconds
|
|
2222
|
+
};
|
|
2223
|
+
};
|
|
2224
|
+
const handleTimelineStartSeconds = (state, value) => {
|
|
2225
|
+
const nextState = {
|
|
2226
|
+
...state,
|
|
2227
|
+
timelineStartSeconds: value
|
|
2228
|
+
};
|
|
2229
|
+
return withPreservedSelection$1(state, nextState);
|
|
2230
|
+
};
|
|
2231
|
+
const handleTimelineEndSeconds = (state, value) => {
|
|
2232
|
+
const nextState = {
|
|
2233
|
+
...state,
|
|
2234
|
+
timelineEndSeconds: value
|
|
2235
|
+
};
|
|
2236
|
+
return withPreservedSelection$1(state, nextState);
|
|
2237
|
+
};
|
|
2238
|
+
const handleTimelineRangePreset = (state, value) => {
|
|
2239
|
+
const nextState = {
|
|
2240
|
+
...state,
|
|
2241
|
+
...parseTimelineRangePreset(value)
|
|
2242
|
+
};
|
|
2243
|
+
return withPreservedSelection$1(state, nextState);
|
|
2244
|
+
};
|
|
2245
|
+
|
|
2129
2246
|
const handleTimelineDoubleClick = state => {
|
|
2130
|
-
const nextState =
|
|
2247
|
+
const nextState = handleTimelineRangePreset(state, '');
|
|
2131
2248
|
return clearTimelineSelectionState(nextState);
|
|
2132
2249
|
};
|
|
2133
2250
|
|
|
@@ -2217,10 +2334,44 @@ const handleTimelinePointerUp = (state, eventX) => {
|
|
|
2217
2334
|
const focus = Number.parseFloat(focusSeconds);
|
|
2218
2335
|
const startSeconds = formatTimelinePresetValue(Math.min(anchor, focus));
|
|
2219
2336
|
const endSeconds = formatTimelinePresetValue(Math.max(anchor, focus));
|
|
2220
|
-
const nextState =
|
|
2337
|
+
const nextState = handleTimelineRangePreset(state, `${startSeconds}:${endSeconds}`);
|
|
2221
2338
|
return clearTimelineSelectionState(nextState);
|
|
2222
2339
|
};
|
|
2223
2340
|
|
|
2341
|
+
const handleUseDevtoolsLayout = (state, checked) => {
|
|
2342
|
+
const useDevtoolsLayout = getBoolean(checked);
|
|
2343
|
+
const selectedEventIndex = useDevtoolsLayout ? getSelectedEventIndex$1(state) : null;
|
|
2344
|
+
return {
|
|
2345
|
+
...state,
|
|
2346
|
+
selectedEvent: useDevtoolsLayout && selectedEventIndex !== null ? state.selectedEvent : null,
|
|
2347
|
+
selectedEventId: useDevtoolsLayout && selectedEventIndex !== null ? state.selectedEventId : null,
|
|
2348
|
+
selectedEventIndex,
|
|
2349
|
+
useDevtoolsLayout
|
|
2350
|
+
};
|
|
2351
|
+
};
|
|
2352
|
+
|
|
2353
|
+
const handleShowEventStreamFinishedEvents = (state, checked) => {
|
|
2354
|
+
const nextState = {
|
|
2355
|
+
...state,
|
|
2356
|
+
showEventStreamFinishedEvents: getBoolean(checked)
|
|
2357
|
+
};
|
|
2358
|
+
return withPreservedSelection$1(state, nextState);
|
|
2359
|
+
};
|
|
2360
|
+
const handleShowInputEvents = (state, checked) => {
|
|
2361
|
+
const nextState = {
|
|
2362
|
+
...state,
|
|
2363
|
+
showInputEvents: getBoolean(checked)
|
|
2364
|
+
};
|
|
2365
|
+
return withPreservedSelection$1(state, nextState);
|
|
2366
|
+
};
|
|
2367
|
+
const handleShowResponsePartEvents = (state, checked) => {
|
|
2368
|
+
const nextState = {
|
|
2369
|
+
...state,
|
|
2370
|
+
showResponsePartEvents: getBoolean(checked)
|
|
2371
|
+
};
|
|
2372
|
+
return withPreservedSelection$1(state, nextState);
|
|
2373
|
+
};
|
|
2374
|
+
|
|
2224
2375
|
const getFailedToLoadMessage = sessionId => {
|
|
2225
2376
|
return `Failed to load chat debug session "${sessionId}". Please try again.`;
|
|
2226
2377
|
};
|
|
@@ -2588,7 +2739,6 @@ const Reference = 100;
|
|
|
2588
2739
|
|
|
2589
2740
|
const ClientX = 'event.clientX';
|
|
2590
2741
|
const ClientY = 'event.clientY';
|
|
2591
|
-
const TargetChecked = 'event.target.checked';
|
|
2592
2742
|
const TargetName = 'event.target.name';
|
|
2593
2743
|
const TargetValue = 'event.target.value';
|
|
2594
2744
|
|
|
@@ -3715,9 +3865,9 @@ const getDebugErrorDom = errorMessage => {
|
|
|
3715
3865
|
}, text(errorMessage)];
|
|
3716
3866
|
};
|
|
3717
3867
|
|
|
3718
|
-
const
|
|
3868
|
+
const HandleEventCategoryFilter = 4;
|
|
3719
3869
|
const HandleFilterInput = 5;
|
|
3720
|
-
const
|
|
3870
|
+
const HandleDetailTab = 6;
|
|
3721
3871
|
const HandleEventRowClick = 7;
|
|
3722
3872
|
const HandleHeaderContextMenu = 8;
|
|
3723
3873
|
const HandleSashPointerDown = 9;
|
|
@@ -3730,6 +3880,8 @@ const HandleTimelinePointerMove = 15;
|
|
|
3730
3880
|
const HandleTimelinePointerUp = 16;
|
|
3731
3881
|
const HandleTimelineDoubleClick = 17;
|
|
3732
3882
|
const HandleTableKeyDown = 18;
|
|
3883
|
+
const HandleTimelineRangePreset = 19;
|
|
3884
|
+
const HandleCloseDetails = 20;
|
|
3733
3885
|
|
|
3734
3886
|
const getDebugViewTopDom = (filterValue, useDevtoolsLayout, quickFilterNodes) => {
|
|
3735
3887
|
if (useDevtoolsLayout) {
|
|
@@ -3857,8 +4009,8 @@ const getTabNodes = selectedDetailTab => {
|
|
|
3857
4009
|
className: joinClassNames(ChatDebugViewDetailsTab, isSelected && ChatDebugViewDetailsTabSelected),
|
|
3858
4010
|
id: getTabId(detailTab),
|
|
3859
4011
|
name: DetailTab,
|
|
3860
|
-
onChange:
|
|
3861
|
-
onClick:
|
|
4012
|
+
onChange: HandleDetailTab,
|
|
4013
|
+
onClick: HandleDetailTab,
|
|
3862
4014
|
role: 'tab',
|
|
3863
4015
|
tabIndex: isSelected ? 0 : -1,
|
|
3864
4016
|
type: Button,
|
|
@@ -3884,8 +4036,8 @@ const getDetailsDom = (previewEventNodes, responseEventNodes = previewEventNodes
|
|
|
3884
4036
|
childCount: 0,
|
|
3885
4037
|
className: ChatDebugViewDetailsClose,
|
|
3886
4038
|
name: CloseDetails,
|
|
3887
|
-
onChange:
|
|
3888
|
-
onClick:
|
|
4039
|
+
onChange: HandleCloseDetails,
|
|
4040
|
+
onClick: HandleCloseDetails,
|
|
3889
4041
|
type: Button,
|
|
3890
4042
|
value: 'close'
|
|
3891
4043
|
}, {
|
|
@@ -4316,7 +4468,7 @@ const getBucketDom = bucket => {
|
|
|
4316
4468
|
className: ChatDebugViewTimelinePresetInput,
|
|
4317
4469
|
inputType: 'radio',
|
|
4318
4470
|
name: TimelineRangePreset,
|
|
4319
|
-
onChange:
|
|
4471
|
+
onChange: HandleTimelineRangePreset,
|
|
4320
4472
|
type: Input,
|
|
4321
4473
|
value: presetValue
|
|
4322
4474
|
}, {
|
|
@@ -4466,7 +4618,7 @@ const getQuickFilterNodes = (eventCategoryFilter, eventCategoryFilterOptions) =>
|
|
|
4466
4618
|
className: ChatDebugViewQuickFilterInput,
|
|
4467
4619
|
inputType: 'radio',
|
|
4468
4620
|
name: EventCategoryFilter,
|
|
4469
|
-
onChange:
|
|
4621
|
+
onChange: HandleEventCategoryFilter,
|
|
4470
4622
|
type: Input,
|
|
4471
4623
|
value: option.value
|
|
4472
4624
|
}, text(option.label)];
|
|
@@ -4593,11 +4745,17 @@ const renderEventListeners = () => {
|
|
|
4593
4745
|
name: HandleFilterInput,
|
|
4594
4746
|
params: ['handleInput', TargetName, TargetValue]
|
|
4595
4747
|
}, {
|
|
4596
|
-
name:
|
|
4597
|
-
params: ['
|
|
4748
|
+
name: HandleEventCategoryFilter,
|
|
4749
|
+
params: ['handleEventCategoryFilter', TargetValue]
|
|
4598
4750
|
}, {
|
|
4599
|
-
name:
|
|
4600
|
-
params: ['
|
|
4751
|
+
name: HandleDetailTab,
|
|
4752
|
+
params: ['handleDetailTab', TargetValue]
|
|
4753
|
+
}, {
|
|
4754
|
+
name: HandleTimelineRangePreset,
|
|
4755
|
+
params: ['handleTimelineRangePreset', TargetValue]
|
|
4756
|
+
}, {
|
|
4757
|
+
name: HandleCloseDetails,
|
|
4758
|
+
params: ['handleCloseDetails']
|
|
4601
4759
|
}, {
|
|
4602
4760
|
name: HandleSashPointerDown,
|
|
4603
4761
|
params: ['handleSashPointerDown', ClientX, ClientY],
|
|
@@ -4694,8 +4852,11 @@ const setIndexedDbSupportForTest = supported => {
|
|
|
4694
4852
|
return setIndexedDbSupportOverride(supported);
|
|
4695
4853
|
};
|
|
4696
4854
|
|
|
4855
|
+
const setSessionIdDependencies = {
|
|
4856
|
+
listChatViewEvents: listChatViewEvents
|
|
4857
|
+
};
|
|
4697
4858
|
const setSessionId = async (state, sessionId) => {
|
|
4698
|
-
const result = await listChatViewEvents(sessionId, state.databaseName, state.dataBaseVersion, state.eventStoreName, state.sessionIdIndexName);
|
|
4859
|
+
const result = await setSessionIdDependencies.listChatViewEvents(sessionId, state.databaseName, state.dataBaseVersion, state.eventStoreName, state.sessionIdIndexName, state.indexedDbSupportOverride);
|
|
4699
4860
|
if (result.type === 'not-supported') {
|
|
4700
4861
|
return {
|
|
4701
4862
|
...state,
|
|
@@ -4704,6 +4865,7 @@ const setSessionId = async (state, sessionId) => {
|
|
|
4704
4865
|
initial: false,
|
|
4705
4866
|
selectedEvent: null,
|
|
4706
4867
|
selectedEventId: null,
|
|
4868
|
+
selectedEventIndex: null,
|
|
4707
4869
|
sessionId
|
|
4708
4870
|
};
|
|
4709
4871
|
}
|
|
@@ -4715,6 +4877,7 @@ const setSessionId = async (state, sessionId) => {
|
|
|
4715
4877
|
initial: false,
|
|
4716
4878
|
selectedEvent: null,
|
|
4717
4879
|
selectedEventId: null,
|
|
4880
|
+
selectedEventIndex: null,
|
|
4718
4881
|
sessionId
|
|
4719
4882
|
};
|
|
4720
4883
|
}
|
|
@@ -4728,6 +4891,7 @@ const setSessionId = async (state, sessionId) => {
|
|
|
4728
4891
|
initial: false,
|
|
4729
4892
|
selectedEvent: null,
|
|
4730
4893
|
selectedEventId: null,
|
|
4894
|
+
selectedEventIndex: null,
|
|
4731
4895
|
sessionId
|
|
4732
4896
|
};
|
|
4733
4897
|
};
|
|
@@ -4736,18 +4900,28 @@ const commandMap = {
|
|
|
4736
4900
|
'ChatDebug.create': create,
|
|
4737
4901
|
'ChatDebug.diff2': diff2,
|
|
4738
4902
|
'ChatDebug.getCommandIds': getCommandIds,
|
|
4903
|
+
'ChatDebug.handleCloseDetails': wrapCommand(handleCloseDetails),
|
|
4739
4904
|
'ChatDebug.handleDetailsContextMenu': wrapCommand(handleDetailsContextMenu),
|
|
4905
|
+
'ChatDebug.handleDetailTab': wrapCommand(handleDetailTab),
|
|
4906
|
+
'ChatDebug.handleEventCategoryFilter': wrapCommand(handleEventCategoryFilter),
|
|
4740
4907
|
'ChatDebug.handleEventRowClick': wrapCommand(handleEventRowClick),
|
|
4741
4908
|
'ChatDebug.handleHeaderContextMenu': wrapCommand(handleHeaderContextMenu),
|
|
4742
4909
|
'ChatDebug.handleInput': wrapCommand(handleInput),
|
|
4743
4910
|
'ChatDebug.handleSashPointerDown': wrapCommand(handleSashPointerDown),
|
|
4744
4911
|
'ChatDebug.handleSashPointerMove': wrapCommand(handleSashPointerMove),
|
|
4745
4912
|
'ChatDebug.handleSashPointerUp': wrapCommand(handleSashPointerUp),
|
|
4913
|
+
'ChatDebug.handleShowEventStreamFinishedEvents': wrapCommand(handleShowEventStreamFinishedEvents),
|
|
4914
|
+
'ChatDebug.handleShowInputEvents': wrapCommand(handleShowInputEvents),
|
|
4915
|
+
'ChatDebug.handleShowResponsePartEvents': wrapCommand(handleShowResponsePartEvents),
|
|
4746
4916
|
'ChatDebug.handleTableBodyContextMenu': wrapCommand(handleTableBodyContextMenu),
|
|
4747
4917
|
'ChatDebug.handleTimelineDoubleClick': wrapCommand(handleTimelineDoubleClick),
|
|
4918
|
+
'ChatDebug.handleTimelineEndSeconds': wrapCommand(handleTimelineEndSeconds),
|
|
4748
4919
|
'ChatDebug.handleTimelinePointerDown': wrapCommand(handleTimelinePointerDown),
|
|
4749
4920
|
'ChatDebug.handleTimelinePointerMove': wrapCommand(handleTimelinePointerMove),
|
|
4750
4921
|
'ChatDebug.handleTimelinePointerUp': wrapCommand(handleTimelinePointerUp),
|
|
4922
|
+
'ChatDebug.handleTimelineRangePreset': wrapCommand(handleTimelineRangePreset),
|
|
4923
|
+
'ChatDebug.handleTimelineStartSeconds': wrapCommand(handleTimelineStartSeconds),
|
|
4924
|
+
'ChatDebug.handleUseDevtoolsLayout': wrapCommand(handleUseDevtoolsLayout),
|
|
4751
4925
|
'ChatDebug.loadContent': wrapCommand(loadContent),
|
|
4752
4926
|
'ChatDebug.loadContent2': wrapCommand(loadContent),
|
|
4753
4927
|
'ChatDebug.refresh': wrapCommand(refresh),
|