feat(ui): show the turn/step a context event contributed to
This commit is contained in:
1 parent
d8f9324693
commit
888bb23078
2 files changed
+39
-2
No files matched your search
+27
-2
@@ -153,7 +153,7 @@ const requireStateful = (spec) => {
|
||||
const m2 = { exports: {} }
|
||||
const pluginExports2 = factory(requireStateful, m2, globalThis.window, fakeDoc)
|
||||
|
||||
const DICT_FOR_TEST = { 'tab': 'Context', 'loading': '…', 'error': 'x', 'detail.step': 'Turn {t} · Step {s}', 'gran.step': 'Step', 'gran.turn': 'Turn', 'detail.turn': 'Turn {t} · {n} steps', 'detail.lastStep': 'last step', 'overview.actual': '· last actual prompt {n}' }
|
||||
const DICT_FOR_TEST = { 'tab': 'Context', 'loading': '…', 'error': 'x', 'detail.step': 'Turn {t} · Step {s}', 'gran.step': 'Step', 'gran.turn': 'Turn', 'detail.turn': 'Turn {t} · {n} steps', 'detail.lastStep': 'last step', 'overview.actual': '· last actual prompt {n}', 'events.at': 'Turn {t} · Step {s}' }
|
||||
let viewComponent = null
|
||||
const fakeCtx2 = {
|
||||
get: () => undefined,
|
||||
@@ -493,9 +493,34 @@ const fmtTimeLocal = (t) => {
|
||||
assert.equal(nodeTimes[0].args[2], fmtTimeLocal(65000), 'formatted time (65000ms)')
|
||||
assert.equal(nodeTimes[1].args[2], fmtTimeLocal(1000), 'formatted time (1000ms)')
|
||||
|
||||
// ---- context events show the turn/step they contributed to ----
|
||||
// The host sends events oldest->newest; the list reverses them. Each event
|
||||
// carries the request that follows it (host-stamped); the label renders
|
||||
// only when both turn and step are present (in-flight events stay bare).
|
||||
ctxSlots[0][1]({
|
||||
...snapshot,
|
||||
events: [
|
||||
{ seq: 7, kind: 'prune', time: 2000, tokens: 100, turn: 1, step: 0 },
|
||||
{ seq: 8, kind: 'model', time: 3000, from: 'a', to: 'b', turn: 1, step: 1 },
|
||||
{ seq: 9, kind: 'inject', time: 4000, tokens: 5, form: 'notice', turn: 2, step: 0 },
|
||||
{ seq: 10, kind: 'compaction', time: 5000, tokens: 5000, count: 4 }, // no request after -> unlabeled
|
||||
],
|
||||
})
|
||||
tr = renderView()
|
||||
const evRows = byClass(tr, 'lc-event')
|
||||
assert.equal(evRows.length, 4, 'event rows rendered newest first')
|
||||
const atLabels = byClass(tr, 'lc-event-at')
|
||||
assert.equal(atLabels.length, 3, 'only stamped events carry a turn/step label')
|
||||
assert.equal(atLabels[0].args[2], 'Turn 2 · Step 0', 'inject shows the request it fed')
|
||||
assert.equal(atLabels[1].args[2], 'Turn 1 · Step 1', 'model switch shows its request')
|
||||
assert.equal(atLabels[2].args[2], 'Turn 1 · Step 0', 'prune shows its request')
|
||||
ctxSlots[0][1](snapshot) // restore
|
||||
tr = renderView()
|
||||
assert.equal(byClass(tr, 'lc-event').length, 0, 'event list restored to the empty state')
|
||||
|
||||
// ---- overview shows the provider-reported actual prompt of the last request ----
|
||||
const overviewNum = byClass(tr, 'lc-overview-num')[0]
|
||||
assert.ok(overviewNum, 'overview number row present')
|
||||
assert.match(textOf(overviewNum), /· last actual prompt 83\.0k/, 'overview shows the last actual prompt alongside the estimate')
|
||||
|
||||
console.log('✔ chart render test passed (fixed-width bars, scroll container, turn ranges, hover linking, overview tooltip, turn strip, granularity toggle, edge fades, full history, right-anchored default, message times, overview actual)')
|
||||
console.log('✔ chart render test passed (fixed-width bars, scroll container, turn ranges, hover linking, overview tooltip, turn strip, granularity toggle, edge fades, full history, right-anchored default, message times, event turn/step labels, overview actual)')
|
||||
@@ -134,6 +134,9 @@ interface ContextEventRecord {
|
||||
name?: string
|
||||
from?: string
|
||||
to?: string
|
||||
/** Turn/step of the request this event contributed to (host-stamped). */
|
||||
turn?: number
|
||||
step?: number
|
||||
}
|
||||
|
||||
// ---- dictionaries ------------------------------------------------------------
|
||||
@@ -160,6 +163,7 @@ const DICT_ZH: Record<string, string> = {
|
||||
'detail.output': '输出 {n}',
|
||||
'events.title': '上下文事件',
|
||||
'events.empty': '暂无上下文事件(压缩、注入、模型切换会出现在这里)',
|
||||
'events.at': '第 {t} 轮 · 第 {s} 步',
|
||||
'nodes.title': '消息构成',
|
||||
'nodes.hint': '当前模型可见的消息,最新在前',
|
||||
'nodes.more': '… 更早的 {n} 条消息已省略',
|
||||
@@ -206,6 +210,7 @@ const DICT_EN: Record<string, string> = {
|
||||
'detail.output': 'output {n}',
|
||||
'events.title': 'Context events',
|
||||
'events.empty': 'No context events yet (compaction, injections, model switches appear here)',
|
||||
'events.at': 'Turn {t} · Step {s}',
|
||||
'nodes.title': 'Messages',
|
||||
'nodes.hint': 'currently model-visible, newest first',
|
||||
'nodes.more': '… {n} earlier messages omitted',
|
||||
@@ -622,6 +627,12 @@ function makeView(ctx: ClientCtx, t: Translate): (props: ContextViewProps) => Re
|
||||
return h('div', { key: ev.seq + '-' + i, className: 'lc-event' },
|
||||
h('span', { className: 'lc-event-icon lc-event-' + ev.kind }, EVENT_ICONS[ev.kind] || '•'),
|
||||
h('span', { className: 'lc-event-label', title: label }, label),
|
||||
// Which request this event contributed to (host-stamped: the first
|
||||
// request logged after the event). In-flight or trimmed-away
|
||||
// events have no turn/step and stay unlabeled.
|
||||
typeof ev.turn === 'number' && typeof ev.step === 'number'
|
||||
? h('span', { className: 'lc-event-at' }, t('events.at', { t: ev.turn, s: ev.step }))
|
||||
: null,
|
||||
ev.tokens ? h('span', { className: 'lc-event-tokens' + (ev.kind === 'inject' ? ' lc-up' : ' lc-down') },
|
||||
(ev.kind === 'inject' ? '+' : '−') + fmt(ev.tokens)) : null,
|
||||
h('span', { className: 'lc-event-time' }, fmtTime(ev.time)))
|
||||
@@ -881,6 +892,7 @@ const STYLES = [
|
||||
'.lc-event-icon.lc-event-inject { color: #a855f7; }',
|
||||
'.lc-event-icon.lc-event-model { color: var(--dsw-alias-brand-primary); }',
|
||||
'.lc-event-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }',
|
||||
'.lc-event-at { flex: none; color: var(--dsw-alias-label-secondary); font-size: 11px; white-space: nowrap; }',
|
||||
'.lc-event-tokens { color: var(--dsw-alias-state-success-primary); }',
|
||||
'.lc-event-tokens.lc-up { color: var(--dsw-alias-state-warn-primary); }',
|
||||
'.lc-event-time { color: var(--dsw-alias-label-secondary); font-size: 12px; }',
|
||||
|
||||
Reference in new issue
Block a user