fix: expose trace settings and preserve long prompt attributes

This commit is contained in:
Soulter
2026-10-13 20:30:51 +08:00
parent 6239fc5d45
commit 4d8d619ebf
9 changed files with 78 additions and 9 deletions
+6 -3
View File
@@ -30,7 +30,9 @@ def bounded_text(value: str, limit: int = MAX_BODY_BYTES) -> str:
)
def bounded_attributes(fields: dict[str, Any]) -> dict[str, Any]:
def bounded_attributes(
fields: dict[str, Any], *, max_bytes: int = MAX_ATTRIBUTE_BYTES
) -> dict[str, Any]:
"""Convert application fields to bounded OTel attribute values.
Nested objects are represented as JSON strings, not non-standard attribute
@@ -38,11 +40,12 @@ def bounded_attributes(fields: dict[str, Any]) -> dict[str, Any]:
Args:
fields: Application attributes with their final names.
max_bytes: Shared attribute budget, not a per-field text limit.
Returns:
Primitive attributes with an optional truncation marker.
"""
remaining = MAX_ATTRIBUTE_BYTES - 64
remaining = max_bytes - 64
truncated = False
result = {}
for key, value in fields.items():
@@ -95,7 +98,7 @@ def bounded_attributes(fields: dict[str, Any]) -> dict[str, Any]:
continue
if isinstance(value, str):
original = value
value = bounded_text(value, min(4096, max(16, remaining - len(key) - 16)))
value = bounded_text(value, max(16, remaining - len(key) - 16))
truncated |= value != original
try:
cost = len(
+4 -2
View File
@@ -193,7 +193,8 @@ class TraceSpan:
return
now = time.time_ns()
attrs = bounded_attributes(
{"astrbot." + key: value for key, value in fields.items()}
{"astrbot." + key: value for key, value in fields.items()},
max_bytes=MAX_TRACE_BYTES,
)
if attrs.get("astrbot.truncated"):
self._summary["truncated"] = True
@@ -306,7 +307,8 @@ class TraceSpan:
span["end_time_unix_nano"] = str(now)
if is_target:
span["attributes"] = bounded_attributes(
{**span["attributes"], **attrs}
{**span["attributes"], **attrs},
max_bytes=MAX_TRACE_BYTES,
)
span["status"] = {
"code": 2 if fields.get("status") == "error" else 1
+1 -1
View File
@@ -18,7 +18,7 @@ from astrbot.core.utils.astrbot_path import get_astrbot_data_path
logger = logging.getLogger("astrbot")
QUEUE_BYTES = 4 * 1024 * 1024
MAX_FRAME_BYTES = 64 * 1024
MAX_FRAME_BYTES = 1024 * 1024
class TraceStore:
+3
View File
@@ -10,6 +10,9 @@
## Trace
- Expose SQLite trace settings under Settings → General → Logs, replacing the obsolete JSONL settings whitelist.
- Remove the per-field 4 KB text truncation limit for trace attributes while retaining whole-trace, writer-queue, and storage budgets.
- Expand message traces to include plugin handlers and their categories, event hooks, Agent requests, response outlines, and completion status.
- Trace table with expandable message rows → full-width tracing list with an opt-in execution timeline inspector. Columns: status chip, platform logo, timestamp, live duration, message, UMO, sender, and Spans.
- Add a Filters sidebar with combinable Has errors / Has model calls / Has plugin calls conditions; counts refer to message traces, not internal steps.
+3 -3
View File
@@ -747,9 +747,9 @@ const systemConfigGroups = computed(() => {
'log_file_enable',
'log_file_path',
'log_file_max_mb',
'trace_log_enable',
'trace_log_path',
'trace_log_max_mb'
'trace_db_path',
'trace_db_max_mb',
'trace_retention_days'
]),
createGroup('tempStorage', [
'temp_dir_max_size'
+35
View File
@@ -0,0 +1,35 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import vm from "node:vm";
import ts from "typescript";
import { computed, ref } from "vue";
import { parse } from "vue/compiler-sfc";
const source = readFileSync(new URL("../src/views/Settings.vue", import.meta.url), "utf8");
const { descriptor } = parse(source);
const ast = ts.createSourceFile("settings.js", descriptor.scriptSetup.content, ts.ScriptTarget.Latest, true);
test("general log settings expose SQLite path, capacity, and retention without legacy JSONL keys", () => {
const keys = [
"log_level", "log_file_enable", "log_file_path", "log_file_max_mb",
"trace_db_path", "trace_db_max_mb", "trace_retention_days",
];
const items = Object.fromEntries(keys.map((key) => [key, { type: key === "trace_db_path" ? "string" : "int" }]));
const context = vm.createContext({
computed,
tm: (key) => key,
systemConfigMetadata: ref({ system_group: { metadata: { system: { items } } } }),
});
const names = new Set(["systemConfigGroups", "generalSystemConfigGroups"]);
const statements = ast.statements.filter((node) => ts.isVariableStatement(node) &&
names.has(node.declarationList.declarations[0]?.name.getText(ast)));
assert.equal(statements.length, names.size);
vm.runInContext(statements.map((node) => node.getText(ast)).join("\n"), context);
const groups = vm.runInContext("generalSystemConfigGroups.value", context);
const logs = groups.find((group) => group.key === "logs");
assert.deepEqual(Object.keys(logs.metadata.logs.items), keys);
for (const key of ["trace_log_enable", "trace_log_path", "trace_log_max_mb"]) {
assert.doesNotMatch(statements[0].getText(ast), new RegExp(key));
}
});
+2
View File
@@ -172,6 +172,8 @@ The toolbar contains search, fullscreen, font-size controls, and `Install Pip pa
### Trace
Configure the trace database path, capacity budget (MB), and retention days under Settings → General → Logs. Defaults are `logs/traces.db`, 80 MB, and 7 days; restart the backend after changing the path. Start/pause recording from the trace toolbar. Top-level text fields such as system prompts have no separate 4 KB truncation limit; whole-trace content and node budgets still apply.
The trace tab has no outer gray card; its toolbar and list sit directly on the page with only the necessary separators. The former hint and switch are replaced by a toolbar button showing Pause (enabled and connected), Connecting (enabled but disconnected), or Start (disabled). Filters, recording, and refresh use matching light-gray icon-and-text buttons sharing the input's background color, all 36px high. Search fills the remaining width, pushing actions to the right. Its placeholder includes the message count instead of a separate counter. Click to pause or resume. Updates show loading and prevent repeated clicks; failures preserve the previous state and display an error. Pausing retains the list and open details in both themes.
The `Trace` tab shows message execution chains: receipt, plugin handlers, Agent requests, responses, and completion. It opens as a full-width Vuetify datatable with columns ordered as status chip, platform logo, timestamp, duration, message, UMO, sender, and Spans. Headers, row height, and spacing use Vuetify defaults; only cell content such as status, platform icons, and messages is customized. The platform column has a minimum width and a non-wrapping header. Duration updates while execution is active. Click a row to open its timeline details on the right; use the close button or Escape to return to the list. History loading, incoming records, and filtering never automatically open another trace. Scroll horizontally on narrow screens. Spans counts actual spans including the message root, excluding span events; collapsing does not change the count. The search placeholder shows the total message traces matching the current filters. History is searched and filtered server-side in pages of 50; click Load more to fetch another page. The browser retains at most 300 summaries and loads complete detail only for the selected trace, releasing it on close. Open `Filters` next to search to select `Has errors`, `Has model calls`, and `Has plugin calls`. Multiple selections require all conditions and can be combined with search. No selection shows all traces; `Reset` clears selections, and closing the sidebar preserves them. This sidebar replaces the former `All / Errors / Agent / Plugins` single-select tabs. Use the toolbar recording button to enable or disable trace recording.
+2
View File
@@ -172,6 +172,8 @@ ChatUI 支持以下常用能力:
### 追踪
在“设置 → 常规 → 日志”中配置“追踪数据库路径”“追踪数据库容量预算 (MB)”和“追踪保留天数”。默认分别为 `logs/traces.db`、80 MB 和 7 天;更改数据库路径后重启后端。追踪记录的启动/暂停仍使用追踪页工具栏按钮。系统提示词等顶层文本字段没有单独的 4 KB 截断限制,但整条追踪的内容和节点预算仍然生效。
追踪标签页不使用外层灰色卡片,工具栏和列表直接铺在页面上,仅保留必要的分隔线。原来的顶部说明和独立记录开关已移除;工具栏按钮显示“暂停”(启用且已连接)、“连接中”(启用但未连接)或“启动”(停用)。过滤器、记录按钮、刷新均采用图标加文字的浅灰按钮,与搜索框共用背景色,统一为 36px 高;搜索框填满剩余宽度,将操作按钮靠右排列。记录数量并入占位文字:“在 X 条记录中搜索消息、会话、插件”,不单独显示。点击记录按钮可暂停或恢复,更新期间显示加载状态并禁用重复点击;失败时保持原状态并提示错误。暂停不会清除已有列表或关闭详情,明暗主题保持相同布局。
`追踪` 页面实时显示消息执行链:收到消息、触发插件处理器、Agent 相关请求、发送响应和完成。默认只展示全宽 Vuetify datatable 追踪列表,列顺序为状态标签(运行中/已完成/错误)、平台 logo、时间戳、时长、消息、UMO、发送人和 Spans;表头、行高和间距采用 Vuetify 默认样式,仅定制状态、平台图标及消息等单元格内容。平台列设有最小宽度,表头不会换行。时长在执行期间持续更新。点击某行后才打开右侧时间线详情,点击关闭按钮或按 Escape 返回列表;加载历史记录、新记录到达及筛选变化都不会自动打开其他记录的详情。窄屏列表可横向滚动。Spans 统计实际 Span 数(包括消息根 Span),不包含挂载在 Span 上的事件,折叠不改变计数;搜索占位文字显示满足当前条件的消息 trace 总数。历史记录在服务端搜索和筛选,每页 50 条,可点击“加载更多”;浏览器最多保留 300 条摘要,只有当前打开的 trace 加载完整详情,关闭后释放详情。点击搜索框左侧的“过滤器”,勾选“有错误”“有模型调用”“有插件调用”;多选需同时满足所有条件,并可叠加搜索。不勾选则显示全部,“重置”清除勾选,关闭侧栏仍保留条件。原先的“全部 / 错误 / Agent / 插件”单选标签已改为多选侧栏。工具栏记录按钮可启用或禁用追踪记录。
+22
View File
@@ -244,6 +244,28 @@ def test_database_pages_filters_details_and_restart(persisted, capture):
restarted.close()
@pytest.mark.parametrize("prompt", ["x" * 8192, "字" * 20000, "x" * 131072])
def test_long_prompt_is_preserved_in_spans_events_and_database(
persisted, capture, prompt
):
trace = TraceSpan("message")
with trace.context():
trace.record("event_received")
trace.record("agent_start", system_prompt=prompt)
trace.record("astr_agent_prepare", system_prompt=prompt)
trace.record("agent_end", status="success")
trace.record("pipeline_end", status="success")
assert capture[-1]["trace"]["truncated"] is False
for frame in capture:
assert persisted.append(frame)
assert persisted.flush()
detail = persisted.get_trace(trace.trace_id)
agent = next(span for span in detail["spans"] if span["parent_span_id"])
assert agent["attributes"]["astrbot.system_prompt"] == prompt
assert agent["events"][0]["attributes"]["astrbot.system_prompt"] == prompt
assert detail["trace"]["truncated"] is False
def test_retention_deletes_whole_traces(persisted, capture):
trace = TraceSpan("message")
trace.record("event_received")