Files
PolyGateway/CHANGELOG.md
T
iomgaa abca723d3d chore: release 1.0.3 with the est_tokens decoupling
Patch level: no field or env key was removed or renamed, no port
signature moved, and the API stays backward compatible -- what changed
is the telemetry data contract, which the changelog spells out for
downstream cost rollups.
2026-07-30 12:14:35 -04:00

8.5 KiB

Changelog

1.0.3(2026-07-30)

est_tokens 解耦(issue #2):一个常量此前被派了两份对"保守"定义相反的差事——TPM 入场预扣(押多了只是慢,安全)与 usage 缺失时的用量兜底(按上界记账只会账单虚高)。本次把两者拆开。

行为收紧/变更(下游请读)

  • usage_source 新增第三个值 unavailable 值域由 measured/estimated 两态变三态:unavailable 表示用量信息不可得(usage 帧缺失、失败尝试、终态失败),estimated 收窄为"有实测数字但可信度降级"(只剩打捞路径这一个生产者:收到 usage 帧但流被截断)。历史库里既有的 estimated 行语义不变、读兼容;按 usage_source 分支的下游代码需要认识新值。OCR 成功行不受影响,仍是 measured(0 token 是事实而非未知)。
  • 用量不可得的行,cost 由数值变 NULL。 此前 usage 帧缺失时库拿 est_tokens(按定义是最坏情形上界)当实测值,又整块塞进 completion_tokens 换算——输出单价通常是输入的数倍,实测双重高估约 26 倍;est_tokens=0 时则算出 0.0,让"免费"与"未知"在数据上不可区分。现在这类行如实记 0/0 + unavailable + cost=NULLSUM(cost) 天然跳过 NULL,账目缺口用 WHERE usage_source = 'unavailable' AND cache_hit = false 量化(cache_hit 限定不可省:缓存命中行未产生新调用,cost 仍是事实上的 0.0,本无缺口)。成本汇总若此前依赖"cost 非空"的隐含假设,请复核。
  • est_tokens 由必填降为可选调优覆盖。 装配校验 tpm > 0 ⇒ est_tokens > 0 已删除——它把供应商配额(运维能从配额页抄到)与库的实现细节(预扣量,无人能正确取值)绑死。未填时库按 max(1, tpm // 60) 派生("一次调用约占一秒钟的配额份额",尺度无关:任何配额规模都收敛到约 60 个在途)。字段与 {SCOPE}__{PROVIDER}__{N}__EST_TOKENS 环境键保留不删不改名,显式填值仍然优先。此前为绕开该校验而把 tpm 限死为 0 的调用方,现可填真实 TPM。

1.0.2(2026-07-30)

1.0.1 的续作:那一版把三条跨字段守卫收进构造期后,独立验证发现 from_env 上还留着同一类的 15 条校验与 4 条规范化,一并收拢。

修复

  • 后端选择与条件必填项在任何构造路径上都校验。 以下此前只有 from_env 拦得住,from_settings() 与直接构造一律放行:limiter_backend/breaker_backend/cache_backend/telemetry_backend/selector/quota_full 六个字段的合法域;取 redis 的后端必须有 redis_url;启用缓存必须有 cache_namespace 与正 cache_ttl_s;telemetry_backendsqlite/postgres 时对应的路径/DSN 必填;structured_max_retries 非负;scope 非空。
  • client.py 五处断言的前提现在真的成立。 assert settings.redis_url is not None # 内部不变量: config 已校验 之类的注释此前在 from_settings 路上是假的:断言开启时抛不含任何字段信息的 AssertionError,python -O 下断言被移除、错误退化为 redis 库抛出的连接串解析异常。注释已改为点明由哪个校验方法保证。
  • 构造路补齐了 from_env 一直在做的规范化,两条装配路对同一输入产出同一个值:
    • scope 小写并去空白。它直接进 Redis key(pgw:limit:{scope}:…pgw:gate:{scope}:…),此前一个进程走 from_env("LLM") 拿到 llm、另一个直接构造传 "LLM",同一逻辑 scope 的限流与熔断状态会分裂到两套命名空间,各记各的配额与熔断状态,分布式治理静默失效且不报错。
    • redis_urlpricing_path 的空串归 None。留着空串会骗过 is None 判断,把错误推迟成 redis 客户端的连接串解析异常或 Is a directory: '.'
    • Postgres DSN 剥掉 SQLAlchemy 驱动后缀(postgresql+asyncpg://…+asyncpg asyncpg 不认)。这一条剥的时候会发一条 warning——库动了调用方给的值,不该静默;日志只出现 scheme 段,DSN 带密码,整串不进日志。经 from_env 装配的不受影响也不会有这条 warning(_load_pg_dsn 早就剥干净了)。
  • EmbeddingSettingsbatch_size / expected_dim 域校验也移入构造期,此前只有 EmbeddingSettings.from_env 校验,直接构造出 batch_size=-3 要到 EmbeddingClient 构造时才 fail-loud。

行为收紧(下游请读)

同 1.0.1:经 from_env() 装配的调用方不受影响。手工构造 GatewaySettings 或对它 dataclasses.replace 的调用方,若配置组合非法,现在会在构造期抛 ValueError 并点出字段名,而不是留到运行时表现为静默不建后端、裸 AssertionError 或第三方库的天书报错。

一处静默改值需要留意:此前手工构造传 scope="LLM"(非全小写)的调用方,升级后 scope 会被规范化为 llm,Redis key 随之从 pgw:limit:LLM:… 切到 pgw:limit:llm:…。这正是本次要修的问题——旧行为下这批 key 与 from_env 装配的进程根本不在同一命名空间;但切换发生的那一刻,旧键上的在途租约会被遗弃,靠 TTL 自愈。滚动升级期间建议留意限流配额短暂偏松。

1.0.1(2026-07-30)

修复

  • 装配守卫在任何构造路径上都生效,不再只在 from_env 上。 三条跨字段不变量(源 timeout_slease_ttl_sstall_window_s ≥ 最大源 TTFT、probe_ttl_s ≥ 最慢源 timeout_s + 5)原先只在 GatewaySettings.from_env 里校验,而装配有两条官方路——走 from_settings() 或直接构造能装出违反不变量的配置且不报错,故障留到运行时才表现为:租约先于请求过期使并发悄悄超出配额、正常慢首包被误判卡死掐断、半开探针在途即被接管。守卫已收进 GatewaySettings.__post_init__,与 types.py 各子配置一致,三个 client(Gateway/Ocr/Embedding)的全部工厂一并覆盖。
  • 新增 sources 非空校验。此前零源配置只在 from_env 路径被拦,直接构造可装出必然选源失败的 client。

行为收紧(下游请读)

直接构造 GatewaySettings 或对它做 dataclasses.replace 时,若上述组合非法,现在会在构造期抛 ValueError,而不是留到运行时。经 from_env() 装配的调用方不受影响——那条路本就跑这些守卫。手工拼配置(如从 YAML 读出后构造)的调用方若此前撞上过上述任一故障,升级后会在启动时立即得到点名字段的报错。

守卫报错文案的补救建议改为点字段名(lease_ttl_sbackpressure.stall_window_sbreaker.probe_ttl_s)。原文案已点出字段名,但建议部分给的是环境变量键(如"调大 PGW_LEASE_TTL_S"),而不走 env 的调用方从没设过那些键。键名映射见 .env.example 与 wiki 参考-配置键

1.0.0(2026-07-22)

首个正式版。统一 LLM/VLM/OCR/Embedding 调度与中转库,治理单位为一次模型调用;经 GovDoc-SaaS 与 CHSAnalyzer 两个真实项目全量迁移验收(ARCHITECTURE §11)。

  • M1 核心: types/errors/ports 内核、OpenAI 兼容 httpx transport(SSE + 非流式)、三层活性看门狗、自研重试(错误四分类驱动,换源/退避/Retry-After)、多源多账号 + 选源 + 源冷却、内存限流/熔断、Redis/内存响应缓存(key 含 namespace/salt/多模态摘要)、SQLite 遥测(18 字段必录)、结构化输出阶梯(json_repair/原生 schema + 有界重问)、provider 注册表、from_env 装配。
  • M2 分布式: Redis 六道闸限流(Lua,契约测试双后端共用)、跨进程熔断(单探针租约 + epoch fencing)、背压 stall 双条件判定、Postgres 遥测、pricing 成本、EmbeddingClient(分批/维度校验)。
  • M2.5 治理韧性: 双通道熔断(失败率窗 + 连败 + 健康证据抑制)、健康感知选源(EWMA×在途 P2C 缺省)、AIMD 自适应并发、429 免重试预算、健康门槛降权;故障混编 soak 同场景 58.1%→98.96%。
  • M3 OCR: OcrTextPort/OcrLayoutPort 端口族 + MonkeyOCR 双端点 transport(数值防御下沉)、OcrClient 独立治理循环、check_health() 逐源预检;OCR soak 1500 调用 99.73%。
  • M4 迁移验证: GovDoc 与 CHS 全量迁移(合计约 −6800 行项目治理代码由库继任),原测试全绿 + 真实冒烟 + 50 样本回归;Gitea PyPI 分发。

安装(实验室 Gitea PyPI):

pip install --index-url https://gitea.iomgaa.online/api/packages/iomgaa/pypi/simple/ \
    --extra-index-url https://pypi.org/simple/ "polygateway[redis,postgres,structured]==1.0.*"