docs: cut 1.3.2, and say plainly that the wheel did not change

tools/ and tests/ are not in the package, so this release ships library
code identical to 1.3.1 byte for byte. Anyone who only uses the library
can skip it. Saying so up front is better than letting someone diff the
wheel and wonder what they missed.

What is in it: the retention script can now be told which table it may
delete from, and the Postgres tests no longer touch the table three
migration projects also write to.

The --table entry documents the failure it prevents rather than just
the flag. search_path starts with "$user", so the same command run as a
different role can resolve to a different table, and the script's own
printout of what it resolved lands in the same run as the DELETE.
This commit is contained in:
2026-08-26 10:55:15 -04:00
parent ea9e5062e8
commit bc0fcc4719
4 changed files with 32 additions and 3 deletions
+27
View File
@@ -1,5 +1,32 @@
# Changelog
## 1.3.2(2026-08-26)
**本版不改库代码。** `tools/``tests/` 都不在 pip 包内(脚本随仓库分发,见 README),故 1.3.2 的 wheel 与 1.3.1 在库代码上**逐字节相同**。升级它不会改变任何库行为——本版的内容是运维脚本 `tools/telemetry_retention.py` 的一处契约扩展,以及测试隔离的重建。若你只用库本体,可以跳过本版。
### 运维脚本:`--table` 让删除目标不再由连接环境决定(issue #18)
`tools/telemetry_retention.py` 此前删哪张表,取决于连接的 `search_path`——它的首项是 `"$user"`,所以**换个角色跑同一条命令,目标可能就换了一张表**。脚本会把解析到的限定名打出来,但那行打印与 `DELETE` 在同一次运行里,中间没有人。
新增可选参数 `--table <schema>.llm_calls`:给了它,目标由参数精确解析(`to_regclass` 走引号限定名),绕开 `search_path`
| 情形 | 行为 |
|---|---|
| 不给 `--table` | **与 1.3.1 完全一致**,现有 cron 不受影响;但 `--apply` 时会多打印一行,提示目标是推断来的 |
| 表名段不是 `llm_calls` | 退出 **1**。本脚本只清理遥测表,不是通用清理器——一次 `--table audit.events` 的手误,会对一张恰好也有 `created_at` / `tenant_id` 的业务表跑同一套分批 DELETE |
| 显式指定的表不存在/不可见 | 退出 **2**,消息附一句"PG 中未加引号建的标识符在 catalog 里是小写"(大小写手误是这里的高频原因) |
| 显式指定的是分区表 | 仍退出 **3** 让路给 `DROP PARTITION`,语义未变 |
退出码契约未新增也未改动。**建议 cron 一律带上 `--table`**:那一行配置从此自己说明删的是哪张表。
### 测试隔离:从"事后观测共享表"改成"权限上做不到"
issue #18 报的是一条 PG 集成测试偶发红。查下来失败的断言并不在测被测脚本——它比对的是一张**三个迁移项目也在写**的表的前后行数,而报错时(61 行变 12 行)脚本本身被证明只动了自己的临时 schema。
行数快照承载不了它想守的属性:别人一写就假红,而外部插入恰好抵消掉一次误删时又会假绿——后一半守的正是"审计表被删空"。现在这条属性交给数据库强制:跑脚本的测试角色拥有自己的临时表、对共享表**没有任何授权**,`search_path` 万一落空就是 `permission denied` 而不是"但愿有断言发现"。共享表 `llm_calls` 至此不再被本仓库任何测试读写,killed 的测试也不会再往里留孤儿行。
对下游没有影响(测试不进包),列在这里是因为它解释了本版为何存在。
## 1.3.1(2026-08-26)
「这次调用到底推理没推理」从此是库的**一等返回值**(issue #16 + #17): `LLMResponse.thinking_observation` 三态如实作答,判不出来时说 `unknown` 而不是伪装成「没推理」,并与推理能力表持续对账。
+3 -1
View File
@@ -352,7 +352,7 @@ PGW_TELEMETRY_TEXT_CAP=2000 # 落库正文的字符上限;不设 = 存全
| 正文体量 | `PGW_TELEMETRY_TEXT_CAP=2000`(按需调);超出部分头部硬切并附 `…(略 N 字)` |
| 保留期 | 上面的分区模板 + `pg_partman``retention`,过期分区整块 `DROP` |
| 访问控制 | 上面的三角色 + `REVOKE UPDATE, DELETE` + `FORCE` RLS |
| 存量兜底 | 已经攒成一张大普通表、来不及改造分区时,用 `tools/telemetry_retention.py`(默认 dry-run,`--apply` 才动手;探测到分区表会直接退出让路给 `DROP PARTITION`) |
| 存量兜底 | 已经攒成一张大普通表、来不及改造分区时,用 `tools/telemetry_retention.py`(默认 dry-run,`--apply` 才动手;探测到分区表会直接退出让路给 `DROP PARTITION`;**`--table <schema>.llm_calls` 把目标钉死**,不给则由连接的 `search_path` 推断) |
**`PGW_TELEMETRY_TEXT_CAP` 的覆盖面必须说清,否则合规判断会出错。** cap 落在四处:`messages` 里每条消息的字符串 `content`、多模态 content 数组中 `type == "text"` 的 part 的 `text`,以及 `response``thinking` 两列。消息侧的这个面与缓存摘要函数 `digest_messages` 一致——**只碰 `content`**,消息里别的字段一概不碰。所以调用方自己塞进 `tool_calls.function.arguments``name` 等字段的内容**不在覆盖范围内**:开了 cap 不等于表里没有全文残留。另需知道:缺省是**不截断**(存全文),而截断之后遥测不再是可复现重放的证据。
@@ -362,6 +362,8 @@ SQLite 侧**不建议**对着一个大库文件跑 `DELETE` + `VACUUM`,而应**
`tools/telemetry_retention.py` 的 SQLite 分支是给**存量场景**兜底的——已经攒成一个大库、来不及改轮转时用它,不是推荐路径。
**`--apply` 之前先把目标钉死。** 不给 `--table` 时,脚本删哪张表取决于连接的 `search_path`——它的首项是 `"$user"`,故换个角色跑同一条命令,只要库里存在同名 schema 下的 `llm_calls`,删的就是另一张表。`--table <schema>.llm_calls` 让目标由参数精确解析、不再经 `search_path` 推断;表名段固定为 `llm_calls`(本脚本只清理遥测表,不是通用清理器),写别的名字会以退出码 1 被拒。cron 里跑 `--apply` 尤其该给它:那一行配置从此自己说明删的是哪张表。
该脚本**随仓库分发,不在 pip 包内**(它是运维工具而非库能力,库本体不 import 它,也不该拿到 `DELETE` 权限),请从仓库的 [`tools/telemetry_retention.py`](https://gitea.iomgaa.online/iomgaa/PolyGateway/src/branch/main/tools/telemetry_retention.py) 取,用维护角色跑。
## 错误模型(四分类)
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "polygateway"
version = "1.3.1"
version = "1.3.2"
description = "PolyGateway:实验室统一的大语言模型(LLM/VLM/OCR)调度与中转库——多源、限流、重试、熔断、缓存、遥测"
# registry 包页面的正文只认这一项:缺了页面就是一片空白(1.1.2 的教训,twine 会警告
# long_description missing 但不阻塞上传)。README 在打包时被固化进产物,发布后再改无效。
+1 -1
View File
@@ -42,7 +42,7 @@ from polygateway.types import (
ThinkingObservation,
)
__version__ = "1.3.1"
__version__ = "1.3.2"
__all__ = [
"DEFAULT_PROFILES",