feat: record the reasoning verdict in telemetry

This issue surfaced only because someone ran a slow suite that is
excluded by default and had not been run for eighteen days. As a column
it becomes a query: which model stopped being observable, and when.

The emitter unwraps the enum to a plain str at the single _record exit.
asyncpg makes no promise about encoding a str subclass, and a telemetry
write that fails is downgraded to one warning — it would not crash, it
would just quietly cost the Postgres path a column. Normalising at the
emitter follows what tenant_id, meta and sampling already do.

The column is appended last in COLUMNS and in both DDLs. An existing
table can only take ALTER at the end, so putting it anywhere else
forks the physical column order between a freshly built database and a
backfilled one.
This commit is contained in:
2026-08-26 00:29:26 -04:00
parent ab1c47ebcc
commit 56acb8f3ac
7 changed files with 146 additions and 30 deletions
+10 -5
View File
@@ -52,6 +52,7 @@ _EXPECTED_COLUMNS = [
"reasoning_tokens",
"tenant_id",
"meta",
"thinking_observation",
]
# run 级前缀: 同库并存的其他运行(迁移批跑/另一开发机)互不可见
@@ -125,6 +126,8 @@ async def _record_minimal(
# 到达 recorder 时已由 emitter 归一化: None → '',空 dict → '{}'
"tenant_id": "",
"meta": "{}",
# 同样已由 emitter 归一化: 枚举取 .value 后才下沉,recorder 只见裸 str
"thinking_observation": "unknown",
}
fields.update(overrides)
await recorder.record_llm_call(**fields)
@@ -602,10 +605,12 @@ _PRE_TENANT_INSERT = (
)
# `_PRE_TENANT_DDL` 的物理列(23 个): 由 `_EXPECTED_COLUMNS` 去掉 issue #11 的两个新维度
# `_PRE_TENANT_DDL` 的物理列(23 个): 由 `_EXPECTED_COLUMNS` 去掉此后新增的三列
# 派生而非另抄一份——两份常量必然漂移,而漂移的表现是"manual 档没补列"这条断言假绿。
# 去掉后的顺序与 DDL 逐字一致(tenant_id/meta 在 DDL 里本就排在末尾)。
_PRE_TENANT_COLUMNS = [c for c in _EXPECTED_COLUMNS if c not in ("tenant_id", "meta")]
# 去掉后的顺序与 DDL 逐字一致(这三列在 DDL 里本就排在末尾)。
_PRE_TENANT_COLUMNS = [
c for c in _EXPECTED_COLUMNS if c not in ("tenant_id", "meta", "thinking_observation")
]
# 回读要逐列比对的字段: 物理列去掉库从不显式写的 created_at,恰好 22 个
_PRE_TENANT_WRITTEN_COLUMNS = [c for c in _PRE_TENANT_COLUMNS if c != "created_at"]
@@ -761,7 +766,7 @@ class TestCallerDimensionsAcceptance:
"WHERE table_schema = $1 AND table_name = 'llm_calls' ORDER BY ordinal_position",
schema,
)
# 22 → 24 个 recorder 字段(加 created_at 共 25 个物理列),且新列追加在末尾
# 22 → 25 个 recorder 字段(加 created_at 共 26 个物理列),且新列追加在末尾
assert [r["column_name"] for r in cols] == _EXPECTED_COLUMNS
rows = await _fetch(
schema_dsn,
@@ -1060,7 +1065,7 @@ class TestPublishedSchemaScript:
await _execute_script(fresh_dsn, script)
actual = [r["column_name"] for r in await _fetch(fresh_dsn, _PHYSICAL_COLUMNS_SQL, schema)]
# 物理列 = 24 个 INSERT 字段 + 库从不显式写的 created_at;对着库常量比,不另抄一份
# 物理列 = 25 个 INSERT 字段 + 库从不显式写的 created_at;对着库常量比,不另抄一份
assert set(actual) == set(COLUMNS) | {"created_at"}
# 列序也不许漂: 新列必须排在 created_at 之后,否则新建库与 ALTER 升级的列序分叉
assert actual == _EXPECTED_COLUMNS