mirror of
https://github.com/AstrBotDevs/AstrBot.git
synced 2026-10-16 15:06:04 +08:00
feat: show model persona and tool counts on Agent headings
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
# Unreleased
|
||||
|
||||
- Add model and persona ID chips to Agent execution headings, show provider IDs on model hover, and count actual tool calls separately for each Agent while folded or expanded.
|
||||
|
||||
- Record successful send_message_to_user deliveries as response events on the originating trace, including message-chain outlines and destination UMO. Do not record failed sends as delivered responses; leave cron Agent instrumentation unchanged.
|
||||
|
||||
- Present trace-detail UMO and sender on separate icon-and-value rows instead of a dot-separated line, preserving UMO alias tooltips.
|
||||
|
||||
@@ -246,10 +246,27 @@ const timelineGroups = computed(() => {
|
||||
const timelineRows = computed(() => {
|
||||
const rows = [];
|
||||
const agentErrors = new Map();
|
||||
const agentMetadata = new Map();
|
||||
for (const group of timelineGroups.value) {
|
||||
for (const id of group.ancestors) {
|
||||
agentErrors.set(id, (agentErrors.get(id) || 0) + group.records.filter((record) => record.fields.status === "error").length);
|
||||
}
|
||||
// Attribute metadata to the nearest Agent, including through plugin/tool spans.
|
||||
const owner = group.ancestors.at(-1);
|
||||
if (!owner) continue;
|
||||
if (!agentMetadata.has(owner)) agentMetadata.set(owner, { models: [], personas: [], toolCalls: 0 });
|
||||
const metadata = agentMetadata.get(owner);
|
||||
for (const record of group.records) {
|
||||
if (record.action === "astr_agent_prepare") {
|
||||
const provider = record.fields.chat_provider;
|
||||
if (provider?.model && !metadata.models.some((model) => model.id === provider.model && model.provider === provider.id)) {
|
||||
metadata.models.push({ id: provider.model, provider: provider.id || "" });
|
||||
}
|
||||
const persona = record.fields.persona_id;
|
||||
if (persona && !metadata.personas.includes(persona)) metadata.personas.push(persona);
|
||||
}
|
||||
if (record.action === "agent_tool_call") metadata.toolCalls += 1;
|
||||
}
|
||||
}
|
||||
for (const group of timelineGroups.value) {
|
||||
if (group.ancestors.some((id) => !expandedAgents.has(id))) continue;
|
||||
@@ -258,7 +275,8 @@ const timelineRows = computed(() => {
|
||||
if (group.records.length === 1) {
|
||||
const agentId = group.agentId;
|
||||
rows.push({ key: group.key, record: group.records[0], category: group.category, depth,
|
||||
agentId, expanded: agentId ? expandedAgents.has(agentId) : detailsExpanded,
|
||||
agentId, agentInfo: agentId ? agentMetadata.get(agentId) : null,
|
||||
expanded: agentId ? expandedAgents.has(agentId) : detailsExpanded,
|
||||
detailsExpanded, errors: agentId ? (agentErrors.get(agentId) || 0) + Number(group.records[0].fields.status === "error") : 0 });
|
||||
continue;
|
||||
}
|
||||
@@ -1021,6 +1039,17 @@ onBeforeUnmount(() => {
|
||||
: row.record.action === 'agent_start' ? tm('compact.agentExecution')
|
||||
: row.record.action === 'event_received' ? tm('actions.eventReceived')
|
||||
: recordLabel(row.record, Boolean(row.group)) }}</strong>
|
||||
<template v-if="row.agentInfo">
|
||||
<v-tooltip v-for="model in row.agentInfo.models" :key="model.id + ':' + model.provider" location="top" content-class="trace-tooltip">
|
||||
<template #activator="{ props }">
|
||||
<v-chip v-bind="props" class="timeline-metadata-chip" size="x-small" variant="tonal">{{ model.id }}</v-chip>
|
||||
</template>
|
||||
<div>{{ model.id }}</div>
|
||||
<div v-if="model.provider">{{ tm("chips.provider", { id: model.provider }) }}</div>
|
||||
</v-tooltip>
|
||||
<v-chip v-for="persona in row.agentInfo.personas" :key="persona" class="timeline-metadata-chip" size="x-small" variant="tonal" :title="tm('chips.persona', { id: persona })">{{ persona }}</v-chip>
|
||||
<v-chip v-if="row.agentInfo.toolCalls" size="x-small" variant="tonal">{{ tm("chips.toolCalls", { count: row.agentInfo.toolCalls }) }}</v-chip>
|
||||
</template>
|
||||
<v-chip v-if="row.category" size="x-small" variant="tonal">{{ tm(`handlerCategories.${row.category}`) }}</v-chip>
|
||||
<span v-if="row.group" class="timeline-group-count">{{ tm("groups.count", { count: row.group.records.length }) }}</span>
|
||||
<span v-if="row.record.action === 'agent_start'" class="timeline-duration">{{ formatDuration(row.record.completed ? row.record.fields.duration_ms || 0 : Math.max(0, (clockNow - row.record.time) * 1000)) }}</span>
|
||||
@@ -1108,6 +1137,7 @@ onBeforeUnmount(() => {
|
||||
.timeline-group-errors { color: rgb(var(--v-theme-error)); font-size: 11px; white-space: nowrap; }
|
||||
.timeline-group-summary { align-items: center; display: flex; flex-wrap: wrap; gap: 6px 10px; }
|
||||
.timeline-metadata-chip { max-width: 100%; vertical-align: middle; }
|
||||
.timeline-title .timeline-metadata-chip { max-width: 200px; }
|
||||
.timeline-metadata-chip :deep(.v-chip__content) { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.timeline-expansion-icon { color: var(--trace-muted); transition: transform 0.15s ease; }
|
||||
.timeline-expansion-icon.is-collapsed { transform: rotate(-90deg); }
|
||||
|
||||
@@ -86,6 +86,9 @@
|
||||
"spansHint": "Actual spans including the message root, excluding events. Collapsing does not change the count."
|
||||
},
|
||||
"chips": {
|
||||
"provider": "Provider: {id}",
|
||||
"persona": "Persona: {id}",
|
||||
"toolCalls": "{count} tool calls",
|
||||
"executionMode": "Execution mode: {name}",
|
||||
"systemSender": "Sent by system",
|
||||
"pluginSender": "Sent by plugin: {name}",
|
||||
|
||||
@@ -86,6 +86,9 @@
|
||||
"spansHint": "メッセージのルートを含む実際の Span 数。イベントは含まず、折りたたんでも変わりません。"
|
||||
},
|
||||
"chips": {
|
||||
"provider": "プロバイダー: {id}",
|
||||
"persona": "ペルソナ: {id}",
|
||||
"toolCalls": "ツール呼び出し {count} 回",
|
||||
"executionMode": "実行方式: {name}",
|
||||
"systemSender": "システムから送信",
|
||||
"pluginSender": "プラグインから送信: {name}",
|
||||
|
||||
@@ -86,6 +86,9 @@
|
||||
"spansHint": "Число Span, включая корневой Span сообщения, без событий. Сворачивание не меняет число."
|
||||
},
|
||||
"chips": {
|
||||
"provider": "Провайдер: {id}",
|
||||
"persona": "Персона: {id}",
|
||||
"toolCalls": "Вызовов инструментов: {count}",
|
||||
"executionMode": "Режим выполнения: {name}",
|
||||
"systemSender": "Отправлено системой",
|
||||
"pluginSender": "Отправлено плагином: {name}",
|
||||
|
||||
@@ -86,6 +86,9 @@
|
||||
"spansHint": "实际 Span 数(含消息根 Span),不包含事件;折叠不影响计数。"
|
||||
},
|
||||
"chips": {
|
||||
"provider": "提供商:{id}",
|
||||
"persona": "人格:{id}",
|
||||
"toolCalls": "{count} 次工具调用",
|
||||
"executionMode": "执行方式:{name}",
|
||||
"systemSender": "系统发送",
|
||||
"pluginSender": "插件发送:{name}",
|
||||
|
||||
@@ -64,6 +64,31 @@ function handler(key, category = "listener", fields = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
test("Agent title metadata remains visible when folded and stays isolated per Agent", (t) => {
|
||||
const state = setup(t, [
|
||||
{ key: "outer", action: "agent_start", operation_span_id: "outer", parent_span_id: "root", fields: {} },
|
||||
{ key: "prepare", action: "astr_agent_prepare", owner_span_id: "outer", fields: { chat_provider: { id: "provider-a", model: "model-a" }, persona_id: "persona-a", tools: ["unused", "search"] } },
|
||||
{ key: "prepare-again", action: "astr_agent_prepare", owner_span_id: "outer", fields: { chat_provider: { id: "provider-a", model: "model-a" }, persona_id: "persona-a" } },
|
||||
{ key: "call", action: "agent_tool_call", operation_span_id: "tool", parent_span_id: "outer", fields: {} },
|
||||
{ key: "result", action: "agent_tool_result", owner_span_id: "tool", fields: {} },
|
||||
{ key: "inner", action: "agent_start", operation_span_id: "inner", parent_span_id: "outer", fields: {} },
|
||||
{ key: "inner-prepare", action: "astr_agent_prepare", owner_span_id: "inner", fields: { chat_provider: { id: "provider-b", model: "model-b" }, persona_id: "persona-b" } },
|
||||
{ key: "inner-tool", action: "agent_tool_call", operation_span_id: "inner-tool", parent_span_id: "inner", fields: {} },
|
||||
{ key: "parallel", action: "agent_start", operation_span_id: "parallel", parent_span_id: "root", fields: {} },
|
||||
]);
|
||||
const outer = state.timelineRows.value.find((row) => row.agentId === "outer");
|
||||
assert.equal(outer.expanded, false);
|
||||
assert.deepEqual(JSON.parse(JSON.stringify(outer.agentInfo)), {
|
||||
models: [{ id: "model-a", provider: "provider-a" }], personas: ["persona-a"], toolCalls: 1,
|
||||
});
|
||||
assert.equal(state.timelineRows.value.find((row) => row.agentId === "parallel").agentInfo, undefined);
|
||||
state.expandedAgents.add("outer");
|
||||
const inner = state.timelineRows.value.find((row) => row.agentId === "inner");
|
||||
assert.equal(inner.agentInfo.models[0].id, "model-b");
|
||||
assert.equal(inner.agentInfo.personas[0], "persona-b");
|
||||
assert.equal(inner.agentInfo.toolCalls, 1);
|
||||
});
|
||||
|
||||
test("collapse adjacent handlers by category without reordering separated records", (t) => {
|
||||
const state = setup(t, [
|
||||
{ key: "received", action: "event_received", fields: { platform_id: "qq" } },
|
||||
|
||||
@@ -172,6 +172,8 @@ The toolbar contains search, fullscreen, font-size controls, and `Install Pip pa
|
||||
|
||||
### Trace
|
||||
|
||||
The Agent execution heading shows chips for recorded model and persona IDs, plus the number of actual tool calls when nonzero. Hover over a model chip to see its provider ID. These remain visible when the execution chain is collapsed; nested and parallel Agents are counted separately, and unavailable metadata is omitted.
|
||||
|
||||
Below the detail title, UMO and sender appear on separate icon-and-value rows. UMO aliases retain their hover tooltip showing the full identifier.
|
||||
|
||||
Trace text and tooltips use the system UI font rather than Outfit. Running status is blue; completed and error statuses remain green and red.
|
||||
|
||||
@@ -174,6 +174,8 @@ ChatUI 支持以下常用能力:
|
||||
|
||||
包含 Agent 调用时,详情统计区增加无边框的 Tokens 卡片,展示本轮输入(含缓存输入)与输出的累计总量,以两位小数的 k/M 格式显示,悬浮提示保留精确数量;悬停或键盘聚焦可查看输入、输出及缓存命中明细。同一 Agent 的累计快照只取最新一份,多个 Agent 分别累计。没有已上报用量时显示“—”,不按 0 处理。
|
||||
|
||||
“Agent 执行”标题后显示已记录的模型 ID、人格 ID,以及非零的实际工具调用次数。模型标签悬停可查看提供商 ID,执行链折叠后标签仍可见;嵌套或并行 Agent 分别统计,缺失的信息不显示。
|
||||
|
||||
详情标题下的 UMO 和发送人分别以“图标 + 值”独立成行,不再使用点分隔;UMO 别名仍可悬停查看完整标识。
|
||||
|
||||
追踪区域和悬停提示使用系统默认字体,不使用 Outfit。运行中状态为蓝色,已完成和错误仍分别为绿色和红色。
|
||||
|
||||
Reference in New Issue
Block a user