elasticdash-test 0.1.16 → 0.1.17-alpha-2
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/cloud-client.d.ts +34 -0
- package/dist/cloud-client.d.ts.map +1 -0
- package/dist/cloud-client.js +103 -0
- package/dist/cloud-client.js.map +1 -0
- package/dist/dashboard-server.d.ts.map +1 -1
- package/dist/dashboard-server.js +425 -90
- package/dist/dashboard-server.js.map +1 -1
- package/dist/evaluators/determinism.d.ts +3 -0
- package/dist/evaluators/determinism.d.ts.map +1 -0
- package/dist/evaluators/determinism.js +116 -0
- package/dist/evaluators/determinism.js.map +1 -0
- package/dist/evaluators/index.d.ts +4 -0
- package/dist/evaluators/index.d.ts.map +1 -0
- package/dist/evaluators/index.js +61 -0
- package/dist/evaluators/index.js.map +1 -0
- package/dist/evaluators/latency-budget.d.ts +3 -0
- package/dist/evaluators/latency-budget.d.ts.map +1 -0
- package/dist/evaluators/latency-budget.js +45 -0
- package/dist/evaluators/latency-budget.js.map +1 -0
- package/dist/evaluators/llm-judge.d.ts +3 -0
- package/dist/evaluators/llm-judge.d.ts.map +1 -0
- package/dist/evaluators/llm-judge.js +122 -0
- package/dist/evaluators/llm-judge.js.map +1 -0
- package/dist/evaluators/output-contains.d.ts +3 -0
- package/dist/evaluators/output-contains.d.ts.map +1 -0
- package/dist/evaluators/output-contains.js +52 -0
- package/dist/evaluators/output-contains.js.map +1 -0
- package/dist/evaluators/output-schema.d.ts +3 -0
- package/dist/evaluators/output-schema.d.ts.map +1 -0
- package/dist/evaluators/output-schema.js +58 -0
- package/dist/evaluators/output-schema.js.map +1 -0
- package/dist/evaluators/token-budget.d.ts +3 -0
- package/dist/evaluators/token-budget.d.ts.map +1 -0
- package/dist/evaluators/token-budget.js +45 -0
- package/dist/evaluators/token-budget.js.map +1 -0
- package/dist/evaluators/types.d.ts +103 -0
- package/dist/evaluators/types.d.ts.map +1 -0
- package/dist/evaluators/types.js +6 -0
- package/dist/evaluators/types.js.map +1 -0
- package/dist/html/dashboard.html +359 -73
- package/dist/index.cjs +253 -14
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interceptors/telemetry-push.d.ts +40 -0
- package/dist/interceptors/telemetry-push.d.ts.map +1 -1
- package/dist/interceptors/telemetry-push.js +129 -6
- package/dist/interceptors/telemetry-push.js.map +1 -1
- package/dist/interceptors/tool.d.ts.map +1 -1
- package/dist/interceptors/tool.js +8 -1
- package/dist/interceptors/tool.js.map +1 -1
- package/dist/interceptors/workflow-ai.d.ts.map +1 -1
- package/dist/interceptors/workflow-ai.js +21 -6
- package/dist/interceptors/workflow-ai.js.map +1 -1
- package/dist/internals/mock-resolver.d.ts +70 -2
- package/dist/internals/mock-resolver.d.ts.map +1 -1
- package/dist/internals/mock-resolver.js +157 -6
- package/dist/internals/mock-resolver.js.map +1 -1
- package/dist/test-group/cli.d.ts +8 -0
- package/dist/test-group/cli.d.ts.map +1 -0
- package/dist/test-group/cli.js +162 -0
- package/dist/test-group/cli.js.map +1 -0
- package/dist/test-group/git-context.d.ts +3 -0
- package/dist/test-group/git-context.d.ts.map +1 -0
- package/dist/test-group/git-context.js +59 -0
- package/dist/test-group/git-context.js.map +1 -0
- package/dist/test-group/reporter.d.ts +4 -0
- package/dist/test-group/reporter.d.ts.map +1 -0
- package/dist/test-group/reporter.js +54 -0
- package/dist/test-group/reporter.js.map +1 -0
- package/dist/test-group/runner.d.ts +18 -0
- package/dist/test-group/runner.d.ts.map +1 -0
- package/dist/test-group/runner.js +234 -0
- package/dist/test-group/runner.js.map +1 -0
- package/dist/workflow-runner-worker.js +7 -1
- package/dist/workflow-runner-worker.js.map +1 -1
- package/package.json +1 -1
- package/src/dashboard-server.ts +101 -28
- package/src/html/dashboard.html +359 -73
- package/src/index.ts +2 -2
- package/src/interceptors/telemetry-push.ts +159 -7
- package/src/interceptors/tool.ts +9 -1
- package/src/interceptors/workflow-ai.ts +22 -6
- package/src/internals/mock-resolver.ts +190 -7
- package/src/workflow-runner-worker.ts +11 -3
package/dist/html/dashboard.html
CHANGED
|
@@ -5,6 +5,30 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>ElasticDash Dashboard</title>
|
|
7
7
|
<style>
|
|
8
|
+
/* Help icon tooltip */
|
|
9
|
+
.help-icon-wrap { position: relative; display: inline-flex; align-items: center; }
|
|
10
|
+
.help-icon {
|
|
11
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
12
|
+
width: 16px; height: 16px; border-radius: 50%;
|
|
13
|
+
background: #e0e0e0; color: #666; font-size: 11px; font-weight: bold;
|
|
14
|
+
cursor: help; vertical-align: middle; margin-left: 4px;
|
|
15
|
+
user-select: none;
|
|
16
|
+
}
|
|
17
|
+
.help-tooltip {
|
|
18
|
+
display: none; position: absolute; left: 50%; bottom: calc(100% + 8px);
|
|
19
|
+
transform: translateX(-50%);
|
|
20
|
+
background: #333; color: #fff; font-size: 12px; font-weight: 400;
|
|
21
|
+
line-height: 1.4; padding: 8px 12px; border-radius: 6px;
|
|
22
|
+
width: 280px; white-space: normal; z-index: 10000;
|
|
23
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.18);
|
|
24
|
+
pointer-events: auto;
|
|
25
|
+
}
|
|
26
|
+
.help-tooltip::after {
|
|
27
|
+
content: ''; position: absolute; top: 100%; left: 50%;
|
|
28
|
+
transform: translateX(-50%);
|
|
29
|
+
border: 6px solid transparent; border-top-color: #333;
|
|
30
|
+
}
|
|
31
|
+
.help-icon-wrap:hover .help-tooltip { display: block; }
|
|
8
32
|
/* Ensure first cell in observation-table never overflows parent */
|
|
9
33
|
.observation-table td:first-child {
|
|
10
34
|
max-width: 120px;
|
|
@@ -314,7 +338,7 @@
|
|
|
314
338
|
window._toolMockConfig = {}; // { toolName: { mode: 'live'|'mock-all'|'mock-specific', callIndices: [], mockData: {} } }
|
|
315
339
|
|
|
316
340
|
// ---- Prompt Mock Config State ----
|
|
317
|
-
// { [originalSystemPrompt]:
|
|
341
|
+
// { [originalSystemPrompt]: { mode, replacement, callIndices? } }
|
|
318
342
|
window._promptMockConfig = {};
|
|
319
343
|
|
|
320
344
|
function getToolsFromTrace() {
|
|
@@ -322,7 +346,7 @@
|
|
|
322
346
|
const toolCalls = {};
|
|
323
347
|
currentObservations.forEach(function(obs, i) {
|
|
324
348
|
const isToolByType = obs.type === 'TOOL';
|
|
325
|
-
const isToolByName = typeof obs.name === 'string' && obs.name.startsWith('tool-');
|
|
349
|
+
const isToolByName = typeof obs.name === 'string' && (obs.name.startsWith('tool-') || obs.name.startsWith('tool:'));
|
|
326
350
|
if (!isToolByType && !isToolByName) return;
|
|
327
351
|
const name = isToolByName && obs.type !== 'TOOL'
|
|
328
352
|
? obs.name.slice(5)
|
|
@@ -408,14 +432,15 @@
|
|
|
408
432
|
return '<div style="color:#999;font-size:13px;padding:6px 0;">No tools detected.</div>';
|
|
409
433
|
}
|
|
410
434
|
|
|
411
|
-
let html = '<div style="
|
|
412
|
-
html += '<table style="width:100%;border-collapse:collapse;font-size:13px;">';
|
|
435
|
+
let html = '<div style="border:1px solid #e0e0e0;border-radius:6px;height:100%;display:flex;flex-direction:column;overflow:hidden;">';
|
|
436
|
+
html += '<table style="width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed;">';
|
|
413
437
|
html += '<thead><tr style="background:#f5f5f5;">';
|
|
414
|
-
html += '<th style="padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">Tool</th>';
|
|
415
|
-
html += '<th style="padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">Calls
|
|
416
|
-
html += '<th style="padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">
|
|
417
|
-
html += '<th style="padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">Details</th>';
|
|
418
|
-
html += '</tr></thead
|
|
438
|
+
html += '<th style="width:30%;padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">Tool</th>';
|
|
439
|
+
html += '<th style="width:15%;padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">Calls</th>';
|
|
440
|
+
html += '<th style="width:20%;padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">Mode</th>';
|
|
441
|
+
html += '<th style="width:35%;padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">Details</th>';
|
|
442
|
+
html += '</tr></thead></table>';
|
|
443
|
+
html += '<div style="flex:1;overflow-y:auto;"><table style="width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed;"><tbody>';
|
|
419
444
|
|
|
420
445
|
toolNames.forEach(function(name) {
|
|
421
446
|
const calls = traceTools[name] || [];
|
|
@@ -424,10 +449,10 @@
|
|
|
424
449
|
const nameStyle = inTrace ? '' : 'color:#999;';
|
|
425
450
|
|
|
426
451
|
html += '<tr class="tool-mock-row" data-tool-name="' + esc(name) + '" style="border-bottom:1px solid #f0f0f0;">';
|
|
427
|
-
html += '<td style="padding:6px 10px;font-family:Monaco,monospace;' + nameStyle + '">' + esc(name) + (inTrace ? '' : ' <span style="font-size:10px;color:#aaa;">(not in trace)</span>') + '</td>';
|
|
428
|
-
html += '<td style="padding:6px 10px;">' + calls.length + '</td>';
|
|
429
|
-
html += '<td style="padding:6px 10px;">';
|
|
430
|
-
html += '<select class="tool-mock-mode" style="font-size:12px;padding:2px 4px;" onchange="window.onToolMockModeChange(\'' + esc(name) + '\', this.value)">';
|
|
452
|
+
html += '<td style="width:30%;padding:6px 10px;font-family:Monaco,monospace;overflow:hidden;text-overflow:ellipsis;' + nameStyle + '">' + esc(name) + (inTrace ? '' : ' <span style="font-size:10px;color:#aaa;">(not in trace)</span>') + '</td>';
|
|
453
|
+
html += '<td style="width:15%;padding:6px 10px;">' + calls.length + '</td>';
|
|
454
|
+
html += '<td style="width:20%;padding:6px 10px;">';
|
|
455
|
+
html += '<select class="tool-mock-mode" style="font-size:12px;padding:2px 4px;width: 100%;" onchange="window.onToolMockModeChange(\'' + esc(name) + '\', this.value)">';
|
|
431
456
|
html += '<option value="live"' + (existing.mode === 'live' ? ' selected' : '') + '>Live</option>';
|
|
432
457
|
html += '<option value="mock-all"' + (existing.mode === 'mock-all' ? ' selected' : '') + '>Mock All Calls</option>';
|
|
433
458
|
if (calls.length > 0) {
|
|
@@ -437,7 +462,7 @@
|
|
|
437
462
|
html += '</td>';
|
|
438
463
|
|
|
439
464
|
// Details column: per-call checkboxes + mock data inputs
|
|
440
|
-
html += '<td style="padding:6px 10px;">';
|
|
465
|
+
html += '<td style="width:35%;padding:6px 10px;">';
|
|
441
466
|
if (existing.mode === 'mock-all') {
|
|
442
467
|
let defaultData = (existing.mockData && existing.mockData[0] !== undefined) ? JSON.stringify(existing.mockData[0]) : (calls.length > 0 ? JSON.stringify(calls[0].output) : '');
|
|
443
468
|
defaultData = convert(defaultData);
|
|
@@ -468,7 +493,7 @@
|
|
|
468
493
|
html += '</tr>';
|
|
469
494
|
});
|
|
470
495
|
|
|
471
|
-
html += '</tbody></table></div>';
|
|
496
|
+
html += '</tbody></table></div></div>';
|
|
472
497
|
return html;
|
|
473
498
|
}
|
|
474
499
|
|
|
@@ -476,9 +501,10 @@
|
|
|
476
501
|
|
|
477
502
|
/** Extract the system prompt string from an LLM call input object or JSON string. */
|
|
478
503
|
function extractSystemPromptFromInput(input) {
|
|
479
|
-
// Input may arrive as a JSON-encoded string (e.g. from Langfuse traces)
|
|
480
|
-
|
|
481
|
-
|
|
504
|
+
// Input may arrive as a JSON-encoded string (e.g. from Langfuse traces).
|
|
505
|
+
// Unwrap until we get a non-string value.
|
|
506
|
+
while (typeof input === 'string') {
|
|
507
|
+
try { input = JSON.parse(input); } catch(e) { break; }
|
|
482
508
|
}
|
|
483
509
|
if (!input || typeof input !== 'object') return null;
|
|
484
510
|
// Anthropic style: { system: "...", messages: [...] }
|
|
@@ -500,19 +526,24 @@
|
|
|
500
526
|
|
|
501
527
|
/**
|
|
502
528
|
* Returns an array of unique system prompts observed across all GENERATION observations.
|
|
503
|
-
* Each entry: { systemPrompt, modelName, count, rowIndex }
|
|
529
|
+
* Each entry: { systemPrompt, modelName, calls: [{obsIndex, callNumber}], count, rowIndex }
|
|
530
|
+
* callNumber is the 1-indexed occurrence of this prompt across GENERATION observations.
|
|
504
531
|
*/
|
|
505
532
|
function getSystemPromptsFromTrace() {
|
|
506
|
-
var seen = []; // [{ systemPrompt, modelName, count }]
|
|
533
|
+
var seen = []; // [{ systemPrompt, modelName, calls, count }]
|
|
507
534
|
var seenMap = {}; // systemPrompt -> index in seen
|
|
508
|
-
|
|
535
|
+
var callCounters = {}; // systemPrompt -> running count
|
|
536
|
+
currentObservations.forEach(function(obs, obsIndex) {
|
|
509
537
|
if (obs.type !== 'GENERATION') return;
|
|
510
538
|
var sp = extractSystemPromptFromInput(obs.input);
|
|
511
539
|
if (!sp) return;
|
|
540
|
+
callCounters[sp] = (callCounters[sp] || 0) + 1;
|
|
541
|
+
var callNumber = callCounters[sp];
|
|
512
542
|
if (seenMap[sp] === undefined) {
|
|
513
543
|
seenMap[sp] = seen.length;
|
|
514
|
-
seen.push({ systemPrompt: sp, modelName: obs.model || obs.name || '(unknown)', count: 0 });
|
|
544
|
+
seen.push({ systemPrompt: sp, modelName: obs.model || obs.name || '(unknown)', calls: [], count: 0 });
|
|
515
545
|
}
|
|
546
|
+
seen[seenMap[sp]].calls.push({ obsIndex: obsIndex, callNumber: callNumber });
|
|
516
547
|
seen[seenMap[sp]].count++;
|
|
517
548
|
});
|
|
518
549
|
return seen.map(function(e, i) { return Object.assign({}, e, { rowIndex: i }); });
|
|
@@ -524,82 +555,145 @@
|
|
|
524
555
|
return '<div style="color:#999;font-size:13px;padding:6px 0;">No system prompts detected in trace. Only AI calls with a system prompt can be mocked here.</div>';
|
|
525
556
|
}
|
|
526
557
|
|
|
527
|
-
let html = '<div style="
|
|
528
|
-
html += '<table style="width:100%;border-collapse:collapse;font-size:13px;">';
|
|
558
|
+
let html = '<div style="border:1px solid #e0e0e0;border-radius:6px;height:100%;display:flex;flex-direction:column;overflow:hidden;">';
|
|
559
|
+
html += '<table style="width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed;">';
|
|
529
560
|
html += '<thead><tr style="background:#f5f5f5;">';
|
|
530
|
-
html += '<th style="padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;
|
|
531
|
-
html += '<th style="padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">
|
|
532
|
-
html += '<th style="padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">
|
|
533
|
-
html += '<th style="padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">
|
|
534
|
-
html += '</tr></thead
|
|
561
|
+
html += '<th style="width:25%;padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">System Prompt</th>';
|
|
562
|
+
html += '<th style="width:10%;padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">Calls</th>';
|
|
563
|
+
html += '<th style="width:20%;padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">Mode</th>';
|
|
564
|
+
html += '<th style="width:45%;padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">Replacement</th>';
|
|
565
|
+
html += '</tr></thead></table>';
|
|
566
|
+
html += '<div style="flex:1;overflow-y:auto;"><table style="width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed;"><tbody>';
|
|
535
567
|
|
|
536
568
|
prompts.forEach(function(row) {
|
|
537
569
|
const key = row.systemPrompt;
|
|
538
|
-
const
|
|
539
|
-
const
|
|
540
|
-
const preview = key.length > 80 ? key.slice(0, 80) + '…' : key;
|
|
570
|
+
const existing = window._promptMockConfig[key] || { mode: 'live', replacement: '' };
|
|
571
|
+
const preview = key.length > 60 ? key.slice(0, 60) + '…' : key;
|
|
541
572
|
|
|
542
573
|
html += '<tr class="prompt-mock-row" data-row-index="' + row.rowIndex + '" style="border-bottom:1px solid #f0f0f0;vertical-align:top;">';
|
|
543
574
|
|
|
544
|
-
//
|
|
545
|
-
html += '<td style="padding:
|
|
546
|
-
html += '<input type="checkbox" class="prompt-mock-enable" title="Override this system prompt"' + (isEnabled ? ' checked' : '') + ' onchange="window.onPromptMockToggle(' + row.rowIndex + ', this.checked)">';
|
|
547
|
-
html += '</td>';
|
|
575
|
+
// System prompt preview column
|
|
576
|
+
html += '<td style="width:25%;padding:6px 10px;overflow:hidden;text-overflow:ellipsis;"><div style="font-size:11px;color:#555;font-family:Monaco,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="' + esc(key) + '">' + esc(preview) + '</div></td>';
|
|
548
577
|
|
|
549
|
-
//
|
|
550
|
-
html += '<td style="padding:
|
|
578
|
+
// Calls count column
|
|
579
|
+
html += '<td style="width:10%;padding:6px 10px;color:#555;white-space:nowrap;">' + row.count + 'x</td>';
|
|
551
580
|
|
|
552
|
-
//
|
|
553
|
-
html += '<td style="padding:
|
|
581
|
+
// Mode column
|
|
582
|
+
html += '<td style="width:20%;padding:6px 10px;white-space:nowrap;">';
|
|
583
|
+
html += '<select class="prompt-mock-mode" style="font-size:12px;padding:2px 4px;width: 100%;" onchange="window.onSystemPromptMockModeChange(' + row.rowIndex + ', this.value)">';
|
|
584
|
+
html += '<option value="live"' + (existing.mode === 'live' ? ' selected' : '') + '>Live</option>';
|
|
585
|
+
html += '<option value="replace-all"' + (existing.mode === 'replace-all' ? ' selected' : '') + '>Replace All</option>';
|
|
586
|
+
if (row.count > 1) {
|
|
587
|
+
html += '<option value="replace-specific"' + (existing.mode === 'replace-specific' ? ' selected' : '') + '>Replace Specific</option>';
|
|
588
|
+
}
|
|
589
|
+
html += '</select>';
|
|
590
|
+
html += '</td>';
|
|
554
591
|
|
|
555
|
-
//
|
|
556
|
-
html += '<td style="padding:
|
|
557
|
-
if (
|
|
558
|
-
html += '<
|
|
592
|
+
// Replacement column
|
|
593
|
+
html += '<td style="width:45%;padding:6px 10px;">';
|
|
594
|
+
if (existing.mode === 'replace-all') {
|
|
595
|
+
html += '<textarea class="prompt-mock-input" data-row-index="' + row.rowIndex + '" style="width:100%;box-sizing:border-box;font-size:11px;font-family:Monaco,monospace;padding:4px;border:1px solid #ddd;border-radius:4px;min-height:48px;resize:vertical;" oninput="window.onPromptMockInput(' + row.rowIndex + ', this.value)">' + esc(existing.replacement || key) + '</textarea>';
|
|
596
|
+
} else if (existing.mode === 'replace-specific') {
|
|
597
|
+
html += '<div style="font-size:11px;color:#555;margin-bottom:4px;">Select calls to replace:</div>';
|
|
598
|
+
row.calls.forEach(function(call) {
|
|
599
|
+
const isChecked = existing.callIndices && existing.callIndices.indexOf(call.callNumber) !== -1;
|
|
600
|
+
html += '<div style="margin-bottom:4px;padding:4px;background:#fafafa;border-radius:4px;border:1px solid #eee;">';
|
|
601
|
+
html += '<label style="display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer;">';
|
|
602
|
+
html += '<input type="checkbox" class="prompt-call-checkbox" value="' + call.callNumber + '"' + (isChecked ? ' checked' : '') + ' onchange="window.onSystemPromptCallCheckChange(' + row.rowIndex + ',' + call.callNumber + ',this.checked)">';
|
|
603
|
+
html += '<span>Call #' + call.callNumber + '</span>';
|
|
604
|
+
html += '</label>';
|
|
605
|
+
html += '</div>';
|
|
606
|
+
});
|
|
607
|
+
html += '<textarea class="prompt-mock-input" data-row-index="' + row.rowIndex + '" style="width:100%;box-sizing:border-box;font-size:11px;font-family:Monaco,monospace;padding:4px;border:1px solid #ddd;border-radius:4px;min-height:48px;resize:vertical;margin-top:4px;" oninput="window.onPromptMockInput(' + row.rowIndex + ', this.value)">' + esc(existing.replacement || key) + '</textarea>';
|
|
559
608
|
} else {
|
|
560
|
-
html += '<
|
|
609
|
+
html += '<span style="color:#aaa;font-size:11px;">—</span>';
|
|
561
610
|
}
|
|
562
611
|
html += '</td>';
|
|
563
612
|
|
|
564
613
|
html += '</tr>';
|
|
565
614
|
});
|
|
566
615
|
|
|
567
|
-
html += '</tbody></table></div>';
|
|
616
|
+
html += '</tbody></table></div></div>';
|
|
568
617
|
return html;
|
|
569
618
|
}
|
|
570
619
|
|
|
571
620
|
function buildPromptMockConfigFromUI() {
|
|
572
|
-
// Return _promptMockConfig as-is (string key → string value), filtering out blank values
|
|
573
621
|
const config = {};
|
|
574
622
|
Object.keys(window._promptMockConfig).forEach(function(key) {
|
|
575
|
-
const
|
|
576
|
-
if (
|
|
623
|
+
const entry = window._promptMockConfig[key];
|
|
624
|
+
if (!entry || entry.mode === 'live') return;
|
|
625
|
+
config[key] = { mode: entry.mode, replacement: entry.replacement || '' };
|
|
626
|
+
if (entry.mode === 'replace-specific' && entry.callIndices) {
|
|
627
|
+
config[key].callIndices = entry.callIndices;
|
|
628
|
+
}
|
|
577
629
|
});
|
|
578
630
|
return config;
|
|
579
631
|
}
|
|
580
632
|
|
|
581
|
-
window.
|
|
633
|
+
window.onSystemPromptMockModeChange = function(rowIndex, mode) {
|
|
582
634
|
const prompts = getSystemPromptsFromTrace();
|
|
583
635
|
const row = prompts[rowIndex];
|
|
584
636
|
if (!row) return;
|
|
585
637
|
const key = row.systemPrompt;
|
|
586
|
-
if (
|
|
638
|
+
if (mode === 'live') {
|
|
587
639
|
delete window._promptMockConfig[key];
|
|
588
640
|
} else {
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
641
|
+
if (!window._promptMockConfig[key]) {
|
|
642
|
+
window._promptMockConfig[key] = { mode: mode, replacement: row.systemPrompt };
|
|
643
|
+
} else {
|
|
644
|
+
window._promptMockConfig[key].mode = mode;
|
|
645
|
+
}
|
|
646
|
+
if (mode === 'replace-specific' && !window._promptMockConfig[key].callIndices) {
|
|
647
|
+
window._promptMockConfig[key].callIndices = [];
|
|
592
648
|
}
|
|
593
649
|
}
|
|
594
650
|
const container = document.getElementById('promptMockContainer');
|
|
595
651
|
if (container) container.innerHTML = renderPromptMockSection();
|
|
596
652
|
};
|
|
597
653
|
|
|
654
|
+
window.onSystemPromptCallCheckChange = function(rowIndex, callNumber, checked) {
|
|
655
|
+
const prompts = getSystemPromptsFromTrace();
|
|
656
|
+
const row = prompts[rowIndex];
|
|
657
|
+
if (!row) return;
|
|
658
|
+
const key = row.systemPrompt;
|
|
659
|
+
if (!window._promptMockConfig[key]) return;
|
|
660
|
+
var indices = window._promptMockConfig[key].callIndices || [];
|
|
661
|
+
if (checked) {
|
|
662
|
+
if (indices.indexOf(callNumber) === -1) indices.push(callNumber);
|
|
663
|
+
} else {
|
|
664
|
+
var pos = indices.indexOf(callNumber);
|
|
665
|
+
if (pos >= 0) indices.splice(pos, 1);
|
|
666
|
+
}
|
|
667
|
+
window._promptMockConfig[key].callIndices = indices;
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
window.switchMockTab = function(tabName) {
|
|
671
|
+
const tabs = {
|
|
672
|
+
tool: document.getElementById('mockTabTool'),
|
|
673
|
+
prompt: document.getElementById('mockTabPrompt'),
|
|
674
|
+
userPrompt: document.getElementById('mockTabUserPrompt'),
|
|
675
|
+
};
|
|
676
|
+
const contents = {
|
|
677
|
+
tool: document.getElementById('mockTabToolContent'),
|
|
678
|
+
prompt: document.getElementById('mockTabPromptContent'),
|
|
679
|
+
userPrompt: document.getElementById('mockTabUserPromptContent'),
|
|
680
|
+
};
|
|
681
|
+
const toolExtra = document.getElementById('mockTabToolExtra');
|
|
682
|
+
Object.keys(tabs).forEach(function(key) {
|
|
683
|
+
const isActive = key === tabName;
|
|
684
|
+
if (tabs[key]) { tabs[key].style.borderBottomColor = isActive ? '#4f46e5' : 'transparent'; tabs[key].style.color = isActive ? '#4f46e5' : '#888'; }
|
|
685
|
+
if (contents[key]) contents[key].style.display = isActive ? '' : 'none';
|
|
686
|
+
});
|
|
687
|
+
if (toolExtra) toolExtra.style.display = tabName === 'tool' ? 'flex' : 'none';
|
|
688
|
+
};
|
|
689
|
+
|
|
598
690
|
window.onPromptMockInput = function(rowIndex, value) {
|
|
599
691
|
const prompts = getSystemPromptsFromTrace();
|
|
600
692
|
const row = prompts[rowIndex];
|
|
601
693
|
if (!row) return;
|
|
602
|
-
window._promptMockConfig[row.systemPrompt]
|
|
694
|
+
if (window._promptMockConfig[row.systemPrompt]) {
|
|
695
|
+
window._promptMockConfig[row.systemPrompt].replacement = value;
|
|
696
|
+
}
|
|
603
697
|
};
|
|
604
698
|
|
|
605
699
|
window.onToolMockModeChange = function(toolName, mode) {
|
|
@@ -630,10 +724,187 @@
|
|
|
630
724
|
if (container) container.innerHTML = renderToolMockSection(showAll && showAll.checked);
|
|
631
725
|
};
|
|
632
726
|
|
|
727
|
+
// ---- User Prompt Mock Helpers ----
|
|
728
|
+
|
|
729
|
+
/** Extract all user-role message content strings from an LLM call input. */
|
|
730
|
+
function extractUserPromptsFromInput(input) {
|
|
731
|
+
// Unwrap JSON-encoded strings until we get a non-string value.
|
|
732
|
+
while (typeof input === 'string') {
|
|
733
|
+
try { input = JSON.parse(input); } catch(e) { break; }
|
|
734
|
+
}
|
|
735
|
+
if (!input || typeof input !== 'object') return [];
|
|
736
|
+
var msgs = Array.isArray(input.messages) ? input.messages : (Array.isArray(input) ? input : null);
|
|
737
|
+
if (!msgs) return [];
|
|
738
|
+
var results = [];
|
|
739
|
+
for (var i = 0; i < msgs.length; i++) {
|
|
740
|
+
var m = msgs[i];
|
|
741
|
+
if (m && typeof m === 'object' && m.role === 'user' && typeof m.content === 'string') {
|
|
742
|
+
results.push(m.content);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return results;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Returns an array of unique user prompt texts observed across all GENERATION observations.
|
|
750
|
+
* Each entry: { userPrompt, modelName, calls: [{obsIndex, callNumber}], count, rowIndex }
|
|
751
|
+
* callNumber is the 1-indexed occurrence of this text across GENERATION observations.
|
|
752
|
+
*/
|
|
753
|
+
function getUserPromptsFromTrace() {
|
|
754
|
+
var seen = []; // [{ userPrompt, modelName, calls, count }]
|
|
755
|
+
var seenMap = {}; // userPrompt -> index in seen
|
|
756
|
+
var callCounters = {}; // userPrompt -> running count
|
|
757
|
+
currentObservations.forEach(function(obs, obsIndex) {
|
|
758
|
+
if (obs.type !== 'GENERATION') return;
|
|
759
|
+
var prompts = extractUserPromptsFromInput(obs.input);
|
|
760
|
+
var uniqueInCall = [];
|
|
761
|
+
prompts.forEach(function(p) { if (uniqueInCall.indexOf(p) === -1) uniqueInCall.push(p); });
|
|
762
|
+
uniqueInCall.forEach(function(p) {
|
|
763
|
+
callCounters[p] = (callCounters[p] || 0) + 1;
|
|
764
|
+
var callNumber = callCounters[p];
|
|
765
|
+
if (seenMap[p] === undefined) {
|
|
766
|
+
seenMap[p] = seen.length;
|
|
767
|
+
seen.push({ userPrompt: p, modelName: obs.model || obs.name || '(unknown)', calls: [], count: 0 });
|
|
768
|
+
}
|
|
769
|
+
seen[seenMap[p]].calls.push({ obsIndex: obsIndex, callNumber: callNumber });
|
|
770
|
+
seen[seenMap[p]].count++;
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
return seen.map(function(e, i) { return Object.assign({}, e, { rowIndex: i }); });
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function renderUserPromptMockSection() {
|
|
777
|
+
const prompts = getUserPromptsFromTrace();
|
|
778
|
+
if (prompts.length === 0) {
|
|
779
|
+
return '<div style="color:#999;font-size:13px;padding:6px 0;">No user prompts detected in trace. Only AI calls with user-role messages can be mocked here.</div>';
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
let html = '<div style="border:1px solid #e0e0e0;border-radius:6px;height:100%;display:flex;flex-direction:column;overflow:hidden;">';
|
|
783
|
+
html += '<table style="width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed;">';
|
|
784
|
+
html += '<thead><tr style="background:#f5f5f5;">';
|
|
785
|
+
html += '<th style="width:30%;padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">User Prompt</th>';
|
|
786
|
+
html += '<th style="width:10%;padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">Calls</th>';
|
|
787
|
+
html += '<th style="width:20%;padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">Mode</th>';
|
|
788
|
+
html += '<th style="width:40%;padding:6px 10px;text-align:left;border-bottom:1px solid #e0e0e0;">Replacement</th>';
|
|
789
|
+
html += '</tr></thead></table>';
|
|
790
|
+
html += '<div style="flex:1;overflow-y:auto;"><table style="width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed;"><tbody>';
|
|
791
|
+
|
|
792
|
+
prompts.forEach(function(row) {
|
|
793
|
+
const key = row.userPrompt;
|
|
794
|
+
const existing = window._userPromptMockConfig[key] || { mode: 'live', replacement: '' };
|
|
795
|
+
const preview = key.length > 60 ? key.slice(0, 60) + '…' : key;
|
|
796
|
+
|
|
797
|
+
html += '<tr class="user-prompt-mock-row" data-row-index="' + row.rowIndex + '" style="border-bottom:1px solid #f0f0f0;vertical-align:top;">';
|
|
798
|
+
|
|
799
|
+
// User prompt preview column
|
|
800
|
+
html += '<td style="width:30%;padding:6px 10px;overflow:hidden;text-overflow:ellipsis;"><div style="font-size:11px;color:#555;font-family:Monaco,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="' + esc(key) + '">' + esc(preview) + '</div></td>';
|
|
801
|
+
|
|
802
|
+
// Calls count column
|
|
803
|
+
html += '<td style="width:10%;padding:6px 10px;color:#555;white-space:nowrap;">' + row.count + 'x</td>';
|
|
804
|
+
|
|
805
|
+
// Mode column
|
|
806
|
+
html += '<td style="width:20%;padding:6px 10px;white-space:nowrap;">';
|
|
807
|
+
html += '<select class="user-prompt-mock-mode" style="font-size:12px;padding:2px 4px;width: 100%;" onchange="window.onUserPromptMockModeChange(' + row.rowIndex + ', this.value)">';
|
|
808
|
+
html += '<option value="live"' + (existing.mode === 'live' ? ' selected' : '') + '>Live</option>';
|
|
809
|
+
html += '<option value="replace-all"' + (existing.mode === 'replace-all' ? ' selected' : '') + '>Replace All</option>';
|
|
810
|
+
if (row.count > 1) {
|
|
811
|
+
html += '<option value="replace-specific"' + (existing.mode === 'replace-specific' ? ' selected' : '') + '>Replace Specific</option>';
|
|
812
|
+
}
|
|
813
|
+
html += '</select>';
|
|
814
|
+
html += '</td>';
|
|
815
|
+
|
|
816
|
+
// Replacement column
|
|
817
|
+
html += '<td style="width:40%;padding:6px 10px;">';
|
|
818
|
+
if (existing.mode === 'replace-all') {
|
|
819
|
+
html += '<textarea class="user-prompt-mock-input" data-row-index="' + row.rowIndex + '" style="width:100%;box-sizing:border-box;font-size:11px;font-family:Monaco,monospace;padding:4px;border:1px solid #ddd;border-radius:4px;min-height:48px;resize:vertical;" oninput="window.onUserPromptMockInput(' + row.rowIndex + ', this.value)">' + esc(existing.replacement || key) + '</textarea>';
|
|
820
|
+
} else if (existing.mode === 'replace-specific') {
|
|
821
|
+
html += '<div style="font-size:11px;color:#555;margin-bottom:4px;">Select calls to replace:</div>';
|
|
822
|
+
row.calls.forEach(function(call) {
|
|
823
|
+
const isChecked = existing.callIndices && existing.callIndices.indexOf(call.callNumber) !== -1;
|
|
824
|
+
html += '<div style="margin-bottom:4px;padding:4px;background:#fafafa;border-radius:4px;border:1px solid #eee;">';
|
|
825
|
+
html += '<label style="display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer;">';
|
|
826
|
+
html += '<input type="checkbox" class="user-prompt-call-checkbox" value="' + call.callNumber + '"' + (isChecked ? ' checked' : '') + ' onchange="window.onUserPromptCallCheckChange(' + row.rowIndex + ',' + call.callNumber + ',this.checked)">';
|
|
827
|
+
html += '<span>Call #' + call.callNumber + '</span>';
|
|
828
|
+
html += '</label>';
|
|
829
|
+
html += '</div>';
|
|
830
|
+
});
|
|
831
|
+
html += '<textarea class="user-prompt-mock-input" data-row-index="' + row.rowIndex + '" style="width:100%;box-sizing:border-box;font-size:11px;font-family:Monaco,monospace;padding:4px;border:1px solid #ddd;border-radius:4px;min-height:48px;resize:vertical;margin-top:4px;" oninput="window.onUserPromptMockInput(' + row.rowIndex + ', this.value)">' + esc(existing.replacement || key) + '</textarea>';
|
|
832
|
+
} else {
|
|
833
|
+
html += '<span style="color:#aaa;font-size:11px;">—</span>';
|
|
834
|
+
}
|
|
835
|
+
html += '</td>';
|
|
836
|
+
|
|
837
|
+
html += '</tr>';
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
html += '</tbody></table></div></div>';
|
|
841
|
+
return html;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function buildUserPromptMockConfigFromUI() {
|
|
845
|
+
const config = {};
|
|
846
|
+
Object.keys(window._userPromptMockConfig).forEach(function(key) {
|
|
847
|
+
const entry = window._userPromptMockConfig[key];
|
|
848
|
+
if (!entry || entry.mode === 'live') return;
|
|
849
|
+
config[key] = { mode: entry.mode, replacement: entry.replacement || '' };
|
|
850
|
+
if (entry.mode === 'replace-specific' && entry.callIndices) {
|
|
851
|
+
config[key].callIndices = entry.callIndices;
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
return config;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
window.onUserPromptMockModeChange = function(rowIndex, mode) {
|
|
858
|
+
const prompts = getUserPromptsFromTrace();
|
|
859
|
+
const row = prompts[rowIndex];
|
|
860
|
+
if (!row) return;
|
|
861
|
+
const key = row.userPrompt;
|
|
862
|
+
if (mode === 'live') {
|
|
863
|
+
delete window._userPromptMockConfig[key];
|
|
864
|
+
} else {
|
|
865
|
+
if (!window._userPromptMockConfig[key]) {
|
|
866
|
+
window._userPromptMockConfig[key] = { mode: mode, replacement: row.userPrompt };
|
|
867
|
+
} else {
|
|
868
|
+
window._userPromptMockConfig[key].mode = mode;
|
|
869
|
+
}
|
|
870
|
+
if (mode === 'replace-specific' && !window._userPromptMockConfig[key].callIndices) {
|
|
871
|
+
window._userPromptMockConfig[key].callIndices = [];
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
const container = document.getElementById('userPromptMockContainer');
|
|
875
|
+
if (container) container.innerHTML = renderUserPromptMockSection();
|
|
876
|
+
};
|
|
877
|
+
|
|
878
|
+
window.onUserPromptMockInput = function(rowIndex, value) {
|
|
879
|
+
const prompts = getUserPromptsFromTrace();
|
|
880
|
+
const row = prompts[rowIndex];
|
|
881
|
+
if (!row) return;
|
|
882
|
+
if (window._userPromptMockConfig[row.userPrompt]) {
|
|
883
|
+
window._userPromptMockConfig[row.userPrompt].replacement = value;
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
window.onUserPromptCallCheckChange = function(rowIndex, callNumber, checked) {
|
|
888
|
+
const prompts = getUserPromptsFromTrace();
|
|
889
|
+
const row = prompts[rowIndex];
|
|
890
|
+
if (!row) return;
|
|
891
|
+
const key = row.userPrompt;
|
|
892
|
+
if (!window._userPromptMockConfig[key]) return;
|
|
893
|
+
var indices = window._userPromptMockConfig[key].callIndices || [];
|
|
894
|
+
if (checked) {
|
|
895
|
+
if (indices.indexOf(callNumber) === -1) indices.push(callNumber);
|
|
896
|
+
} else {
|
|
897
|
+
var pos = indices.indexOf(callNumber);
|
|
898
|
+
if (pos >= 0) indices.splice(pos, 1);
|
|
899
|
+
}
|
|
900
|
+
window._userPromptMockConfig[key].callIndices = indices;
|
|
901
|
+
};
|
|
902
|
+
|
|
633
903
|
window.openLiveValidationDialog = function() {
|
|
634
904
|
if (window.liveValidationDialog) return;
|
|
635
905
|
window._toolMockConfig = {}; // Reset mock configs each time dialog opens
|
|
636
906
|
window._promptMockConfig = {};
|
|
907
|
+
window._userPromptMockConfig = {};
|
|
637
908
|
|
|
638
909
|
const hasTraceTools = currentObservations.some(function(o) { return o.type === 'TOOL'; });
|
|
639
910
|
const hasRegisteredTools = codeIndex.tools && codeIndex.tools.length > 0;
|
|
@@ -642,7 +913,7 @@
|
|
|
642
913
|
window.liveValidationDialog.id = 'liveValidationDialog';
|
|
643
914
|
window.liveValidationDialog.style = 'position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(0,0,0,0.25);display:flex;align-items:center;justify-content:center;z-index:9999;';
|
|
644
915
|
window.liveValidationDialog.innerHTML = `
|
|
645
|
-
<div style="background:white;padding:32px 28px;border-radius:12px;box-shadow:0 2px 24px #0002;
|
|
916
|
+
<div style="background:white;padding:32px 28px;border-radius:12px;box-shadow:0 2px 24px #0002;width:680px;max-width:90vw;max-height:90vh;overflow-y:auto;">
|
|
646
917
|
<h3 style="margin-top:0;margin-bottom:18px;font-size:20px;">Validate Updated Flow with Live Data</h3>
|
|
647
918
|
<label style="font-size:15px;display:block;margin-bottom:8px;">How many times do you want to run the flow with live data?</label>
|
|
648
919
|
<input id="liveValidationCount" type="number" min="1" value="1" style="width:100%;font-size:16px;padding:6px 10px;margin-bottom:18px;" />
|
|
@@ -650,23 +921,29 @@
|
|
|
650
921
|
<input id="liveValidationSequential" type="checkbox" />
|
|
651
922
|
Run in sequence instead of parallel
|
|
652
923
|
</label>
|
|
653
|
-
${(hasTraceTools || hasRegisteredTools) ? `
|
|
654
924
|
<div style="border-top:1px solid #eee;padding-top:16px;margin-bottom:16px;">
|
|
655
|
-
<div style="display:flex;
|
|
656
|
-
|
|
657
|
-
<
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
925
|
+
<div style="display:flex;align-items:center;gap:0;border-bottom:2px solid #e0e0e0;margin-bottom:0;">
|
|
926
|
+
${(hasTraceTools || hasRegisteredTools) ? `<button id="mockTabTool" class="mock-tab-btn" onclick="window.switchMockTab('tool')" style="padding:8px 18px;font-size:14px;font-weight:600;border:none;background:none;cursor:pointer;border-bottom:2px solid #4f46e5;color:#4f46e5;margin-bottom:-2px;">Tool Mocking <span class="help-icon-wrap"><span class="help-icon">?</span><span class="help-tooltip">Control which tools use real calls vs mock data. ‘Live’ runs the real tool, ‘Mock All Calls’ returns mock data for every call, and ‘Mock Specific Calls’ lets you pick which call indices to mock.</span></span></button>` : ''}
|
|
927
|
+
<button id="mockTabPrompt" class="mock-tab-btn" onclick="window.switchMockTab('prompt')" style="padding:8px 18px;font-size:14px;font-weight:600;border:none;background:none;cursor:pointer;border-bottom:2px solid ${(hasTraceTools || hasRegisteredTools) ? 'transparent' : '#4f46e5'};color:${(hasTraceTools || hasRegisteredTools) ? '#888' : '#4f46e5'};margin-bottom:-2px;">System Prompt <span class="help-icon-wrap"><span class="help-icon">?</span><span class="help-tooltip">Override the system prompt for LLM calls. Check a row to enable editing — the replacement applies to all LLM calls that use that exact system prompt text.</span></span></button>
|
|
928
|
+
<button id="mockTabUserPrompt" class="mock-tab-btn" onclick="window.switchMockTab('userPrompt')" style="padding:8px 18px;font-size:14px;font-weight:600;border:none;background:none;cursor:pointer;border-bottom:2px solid transparent;color:#888;margin-bottom:-2px;">User Prompt <span class="help-icon-wrap"><span class="help-icon">?</span><span class="help-tooltip">Override user-role messages in LLM calls. Each unique user message text is listed. Choose ‘Replace All’ to replace it in every LLM call where it appears, or ‘Replace Specific’ to only replace it in selected call occurrences.</span></span></button>
|
|
929
|
+
<div id="mockTabToolExtra" style="margin-left:auto;display:flex;align-items:center;">
|
|
930
|
+
${(hasTraceTools || hasRegisteredTools) ? `<label style="display:flex;align-items:center;gap:6px;font-size:13px;cursor:pointer;">
|
|
931
|
+
<input id="showAllToolsToggle" type="checkbox" onchange="document.getElementById('toolMockContainer').innerHTML = renderToolMockSection(this.checked);" />
|
|
932
|
+
Show all tools
|
|
933
|
+
</label>` : ''}
|
|
934
|
+
</div>
|
|
661
935
|
</div>
|
|
662
|
-
<div
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
<div style="
|
|
667
|
-
|
|
936
|
+
<div style="height:300px;position:relative;">
|
|
937
|
+
<div id="mockTabToolContent" style="height:100%;overflow:hidden;${(hasTraceTools || hasRegisteredTools) ? '' : 'display:none;'}">
|
|
938
|
+
<div id="toolMockContainer" style="height:100%;"></div>
|
|
939
|
+
</div>
|
|
940
|
+
<div id="mockTabPromptContent" style="height:100%;overflow:hidden;${(hasTraceTools || hasRegisteredTools) ? 'display:none;' : ''}">
|
|
941
|
+
<div id="promptMockContainer" style="height:100%;"></div>
|
|
942
|
+
</div>
|
|
943
|
+
<div id="mockTabUserPromptContent" style="height:100%;overflow:hidden;display:none;">
|
|
944
|
+
<div id="userPromptMockContainer" style="height:100%;"></div>
|
|
945
|
+
</div>
|
|
668
946
|
</div>
|
|
669
|
-
<div id="promptMockContainer" style="max-height:160px;overflow-y:auto;"></div>
|
|
670
947
|
</div>
|
|
671
948
|
<div style="display:flex;gap:12px;justify-content:space-between;align-items:center;">
|
|
672
949
|
<span id="liveValidationProgress" style="font-size:14px;color:#555;"></span>
|
|
@@ -687,6 +964,10 @@
|
|
|
687
964
|
if (promptMockContainer) {
|
|
688
965
|
promptMockContainer.innerHTML = renderPromptMockSection();
|
|
689
966
|
}
|
|
967
|
+
const userPromptMockContainer = document.getElementById('userPromptMockContainer');
|
|
968
|
+
if (userPromptMockContainer) {
|
|
969
|
+
userPromptMockContainer.innerHTML = renderUserPromptMockSection();
|
|
970
|
+
}
|
|
690
971
|
document.getElementById('cancelLiveValidation').onclick = function() {
|
|
691
972
|
window.liveValidationDialog.remove();
|
|
692
973
|
window.liveValidationDialog = null;
|
|
@@ -700,6 +981,8 @@
|
|
|
700
981
|
window._toolMockConfig = toolMockConfig;
|
|
701
982
|
const promptMockConfig = buildPromptMockConfigFromUI();
|
|
702
983
|
window._promptMockConfig = promptMockConfig;
|
|
984
|
+
const userPromptMockConfig = buildUserPromptMockConfigFromUI();
|
|
985
|
+
window._userPromptMockConfig = userPromptMockConfig;
|
|
703
986
|
const submitBtn = document.getElementById('submitLiveValidation');
|
|
704
987
|
submitBtn.disabled = true;
|
|
705
988
|
submitBtn.textContent = 'Validating...';
|
|
@@ -736,7 +1019,7 @@
|
|
|
736
1019
|
const collectedTraces = [];
|
|
737
1020
|
let fatalError = null;
|
|
738
1021
|
for (let i = 0; i < count; i++) {
|
|
739
|
-
const singlePayload = { workflowName: selectedWorkflow?.name, runCount: 1, sequential: false, observations: currentObservations, toolMockConfig, promptMockConfig };
|
|
1022
|
+
const singlePayload = { workflowName: selectedWorkflow?.name, runCount: 1, sequential: false, observations: currentObservations, toolMockConfig, promptMockConfig, userPromptMockConfig };
|
|
740
1023
|
try {
|
|
741
1024
|
const response = await fetch('/api/validate-workflow', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(singlePayload) });
|
|
742
1025
|
const data = await response.json();
|
|
@@ -755,7 +1038,7 @@
|
|
|
755
1038
|
} else {
|
|
756
1039
|
// Parallel mode: single bulk request
|
|
757
1040
|
if (progressEl) progressEl.textContent = `Running ${count} workflow run${count !== 1 ? 's' : ''} in parallel…`;
|
|
758
|
-
const payload = { workflowName: selectedWorkflow?.name, runCount: count, sequential: false, observations: currentObservations, toolMockConfig, promptMockConfig };
|
|
1041
|
+
const payload = { workflowName: selectedWorkflow?.name, runCount: count, sequential: false, observations: currentObservations, toolMockConfig, promptMockConfig, userPromptMockConfig };
|
|
759
1042
|
try {
|
|
760
1043
|
const response = await fetch('/api/validate-workflow', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
|
|
761
1044
|
const data = await response.json();
|
|
@@ -947,6 +1230,7 @@
|
|
|
947
1230
|
}
|
|
948
1231
|
}
|
|
949
1232
|
|
|
1233
|
+
|
|
950
1234
|
function renderObservationTable() {
|
|
951
1235
|
// Capture scroll positions for step 5 columns before rebuilding DOM
|
|
952
1236
|
let _step5Col1Scroll = 0, _step5Col2Scroll = 0;
|
|
@@ -1460,6 +1744,7 @@
|
|
|
1460
1744
|
observations: currentObservations,
|
|
1461
1745
|
toolMockConfig: window._toolMockConfig || {},
|
|
1462
1746
|
promptMockConfig: window._promptMockConfig || {},
|
|
1747
|
+
userPromptMockConfig: window._userPromptMockConfig || {},
|
|
1463
1748
|
};
|
|
1464
1749
|
const response = await fetch('/api/run-from-breakpoint', {
|
|
1465
1750
|
method: 'POST',
|
|
@@ -1491,7 +1776,7 @@
|
|
|
1491
1776
|
step5RerunInFlight = true;
|
|
1492
1777
|
renderObservationTable();
|
|
1493
1778
|
try {
|
|
1494
|
-
const payload = { workflowName: selectedWorkflow?.name, runCount: 1, sequential: false, observations: currentObservations, toolMockConfig: window._toolMockConfig || {}, promptMockConfig: window._promptMockConfig || {} };
|
|
1779
|
+
const payload = { workflowName: selectedWorkflow?.name, runCount: 1, sequential: false, observations: currentObservations, toolMockConfig: window._toolMockConfig || {}, promptMockConfig: window._promptMockConfig || {}, userPromptMockConfig: window._userPromptMockConfig || {} };
|
|
1495
1780
|
const response = await fetch('/api/validate-workflow', {
|
|
1496
1781
|
method: 'POST',
|
|
1497
1782
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -1543,6 +1828,7 @@
|
|
|
1543
1828
|
snapshotId: liveTrace.snapshotId,
|
|
1544
1829
|
toolMockConfig: window._toolMockConfig || {},
|
|
1545
1830
|
promptMockConfig: window._promptMockConfig || {},
|
|
1831
|
+
userPromptMockConfig: window._userPromptMockConfig || {},
|
|
1546
1832
|
};
|
|
1547
1833
|
const response = await fetch('/api/resume-agent-from-task', {
|
|
1548
1834
|
method: 'POST',
|