docs: add the missing imports to the plan's key interfaces

Codex review: the code blocks reference httpx and PolyGatewayError, but
neither module imports them today. A zero-context implementer copying
them verbatim would stall on F821.
This commit is contained in:
2026-08-16 05:57:07 -04:00
parent c2dd4a1cf4
commit 1489aab95d
5 changed files with 74 additions and 3 deletions
+12
View File
@@ -155,6 +155,11 @@
"id": "design:issue10-error-body-retention", "id": "design:issue10-error-body-retention",
"label": "HTTP 错误响应体留存(Issue #10)", "label": "HTTP 错误响应体留存(Issue #10)",
"type": "design" "type": "design"
},
{
"id": "plan:issue10-error-body-retention-plan",
"label": "实现计划: HTTP 错误响应体留存(Issue #10)",
"type": "plan"
} }
], ],
"links": [ "links": [
@@ -276,6 +281,13 @@
"relation": "implements", "relation": "implements",
"evidence": "T1-T6 实施该设计,含 §3.6 订正", "evidence": "T1-T6 实施该设计,含 §3.6 订正",
"added": "2026-08-06T14:58:01.673693+00:00" "added": "2026-08-06T14:58:01.673693+00:00"
},
{
"source": "plan:issue10-error-body-retention-plan",
"target": "design:issue10-error-body-retention",
"relation": "implements",
"evidence": "7 任务覆盖设计 G1-G4 与 §7 全部验收用例",
"added": "2026-08-16T09:50:57.830855+00:00"
} }
] ]
} }
+4 -2
View File
@@ -1,6 +1,6 @@
# Research Wiki 索引 # Research Wiki 索引
> 自动生成,更新时间:2026-08-16 09:12 UTC > 自动生成,更新时间:2026-08-16 09:50 UTC
## design (28) ## design (28)
- [2026-07-20-m1-core-design](designs/2026-07-20-m1-core-design.md) `design:2026-07-20-m1-core-design` - [2026-07-20-m1-core-design](designs/2026-07-20-m1-core-design.md) `design:2026-07-20-m1-core-design`
@@ -46,7 +46,7 @@
- [P7 OCR soak 验收: 99.73% 与 13 不变量全 PASS](findings/p7-ocr-soak.md) `finding:p7-ocr-soak` - [P7 OCR soak 验收: 99.73% 与 13 不变量全 PASS](findings/p7-ocr-soak.md) `finding:p7-ocr-soak`
- [推理开关与 reasoning_tokens: 供应商实测与业界做法](findings/2026-08-02-thinking-switch-and-reasoning-tokens.md) `finding:2026-08-02-thinking-switch-and-reasoning-tokens` - [推理开关与 reasoning_tokens: 供应商实测与业界做法](findings/2026-08-02-thinking-switch-and-reasoning-tokens.md) `finding:2026-08-02-thinking-switch-and-reasoning-tokens`
## plan (21) ## plan (23)
- [2026-07-20-m1-core-plan](plans/2026-07-20-m1-core-plan.md) `plan:2026-07-20-m1-core-plan` - [2026-07-20-m1-core-plan](plans/2026-07-20-m1-core-plan.md) `plan:2026-07-20-m1-core-plan`
- [2026-07-20-m2-distributed-plan](plans/2026-07-20-m2-distributed-plan.md) `plan:2026-07-20-m2-distributed-plan` - [2026-07-20-m2-distributed-plan](plans/2026-07-20-m2-distributed-plan.md) `plan:2026-07-20-m2-distributed-plan`
- [2026-07-21-m25-resilience-plan](plans/2026-07-21-m25-resilience-plan.md) `plan:2026-07-21-m25-resilience-plan` - [2026-07-21-m25-resilience-plan](plans/2026-07-21-m25-resilience-plan.md) `plan:2026-07-21-m25-resilience-plan`
@@ -57,6 +57,7 @@
- [2026-07-31-sampling-params](plans/2026-07-31-sampling-params.md) `plan:2026-07-31-sampling-params` - [2026-07-31-sampling-params](plans/2026-07-31-sampling-params.md) `plan:2026-07-31-sampling-params`
- [2026-08-06-governance-backend-error-plan](plans/2026-08-06-governance-backend-error-plan.md) `plan:2026-08-06-governance-backend-error-plan` - [2026-08-06-governance-backend-error-plan](plans/2026-08-06-governance-backend-error-plan.md) `plan:2026-08-06-governance-backend-error-plan`
- [2026-08-06-issue8-stall-budget](plans/2026-08-06-issue8-stall-budget.md) `plan:2026-08-06-issue8-stall-budget` - [2026-08-06-issue8-stall-budget](plans/2026-08-06-issue8-stall-budget.md) `plan:2026-08-06-issue8-stall-budget`
- [2026-08-16-issue10-error-body-retention](plans/2026-08-16-issue10-error-body-retention.md) `plan:2026-08-16-issue10-error-body-retention`
- [est_tokens 解耦实施计划](plans/est-tokens-decoupling.md) `plan:est-tokens-decoupling` - [est_tokens 解耦实施计划](plans/est-tokens-decoupling.md) `plan:est-tokens-decoupling`
- [issue #8 实施计划: stall 非生产性等待口径](plans/issue8-stall-budget-plan.md) `plan:issue8-stall-budget-plan` - [issue #8 实施计划: stall 非生产性等待口径](plans/issue8-stall-budget-plan.md) `plan:issue8-stall-budget-plan`
- [M1 核心里程碑实现计划](plans/m1-core-plan.md) `plan:m1-core-plan` - [M1 核心里程碑实现计划](plans/m1-core-plan.md) `plan:m1-core-plan`
@@ -65,6 +66,7 @@
- [M3 OCR 实现计划](plans/m3-ocr.md) `plan:m3-ocr` - [M3 OCR 实现计划](plans/m3-ocr.md) `plan:m3-ocr`
- [M4 迁移实现计划(T0-T14)](plans/m4-migration.md) `plan:m4-migration` - [M4 迁移实现计划(T0-T14)](plans/m4-migration.md) `plan:m4-migration`
- [响应可观测字段扩展实现计划](plans/response-observability-fields.md) `plan:response-observability-fields` - [响应可观测字段扩展实现计划](plans/response-observability-fields.md) `plan:response-observability-fields`
- [实现计划: HTTP 错误响应体留存(Issue #10)](plans/issue10-error-body-retention-plan.md) `plan:issue10-error-body-retention-plan`
- [实现计划: 治理后端故障归位为 scope 级不可用(Issue #7)](plans/governance-backend-error.md) `plan:governance-backend-error` - [实现计划: 治理后端故障归位为 scope 级不可用(Issue #7)](plans/governance-backend-error.md) `plan:governance-backend-error`
- [推理开关能力建模与 reasoning_tokens 采集实施计划(issue #5 + #6)](plans/2026-08-02-thinking-capability.md) `plan:2026-08-02-thinking-capability` - [推理开关能力建模与 reasoning_tokens 采集实施计划(issue #5 + #6)](plans/2026-08-02-thinking-capability.md) `plan:2026-08-02-thinking-capability`
- [采样参数透传实现计划(issue #4)](plans/sampling-params-plan.md) `plan:sampling-params-plan` - [采样参数透传实现计划(issue #4)](plans/sampling-params-plan.md) `plan:sampling-params-plan`
+3
View File
@@ -99,3 +99,6 @@
- [2026-08-07 15:11 UTC] 重建索引: 62 篇页面 - [2026-08-07 15:11 UTC] 重建索引: 62 篇页面
- [2026-08-16 09:09 UTC] 新增 design: HTTP 错误响应体留存(Issue #10) (design:issue10-error-body-retention) - [2026-08-16 09:09 UTC] 新增 design: HTTP 错误响应体留存(Issue #10) (design:issue10-error-body-retention)
- [2026-08-16 09:12 UTC] 重建索引: 64 篇页面 - [2026-08-16 09:12 UTC] 重建索引: 64 篇页面
- [2026-08-16 09:50 UTC] 新增 plan: 实现计划: HTTP 错误响应体留存(Issue #10) (plan:issue10-error-body-retention-plan)
- [2026-08-16 09:50 UTC] 新增边: plan:issue10-error-body-retention-plan --implements--> design:issue10-error-body-retention
- [2026-08-16 09:50 UTC] 重建索引: 66 篇页面
@@ -26,6 +26,10 @@
```python ```python
# src/polygateway/transports/_http_errors.py # src/polygateway/transports/_http_errors.py
from __future__ import annotations
import httpx # response_body 的类型与 ResponseNotRead 都来自它
_ERROR_BODY_CAP = 2048 # 字符(非字节),含省略标记在内的最终总长上限 _ERROR_BODY_CAP = 2048 # 字符(非字节),含省略标记在内的最终总长上限
_HEAD_CHARS = 1400 _HEAD_CHARS = 1400
_TAIL_CHARS = 600 _TAIL_CHARS = 600
@@ -69,6 +73,18 @@ class PolyGatewayError(Exception):
```python ```python
# src/polygateway/transports/openai_compat.py # src/polygateway/transports/openai_compat.py
# 既有 errors 导入(:18-23)须补入 PolyGatewayError —— 当前只导了四个子类,
# 直接写 _classify 的返回注解会让 ruff 报 F821 未定义名。
from polygateway.errors import (
PolyGatewayError, # ← 新增
RequestRejectedError,
ResultInvalidError,
SourceDeadError,
TransientError,
)
from polygateway.transports._http_errors import compose_message, summarize_body
def _classify(status: int) -> tuple[type[PolyGatewayError], str]: def _classify(status: int) -> tuple[type[PolyGatewayError], str]:
"""状态码 → (错误类, message 标签);映射与 1.1.2 逐条相同。""" """状态码 → (错误类, message 标签);映射与 1.1.2 逐条相同。"""
if status in (401, 403): if status in (401, 403):
@@ -117,7 +133,7 @@ def _status_to_error(
### - [ ] Task 2: 共享摘要单元 ### - [ ] Task 2: 共享摘要单元
**新建**: `src/polygateway/transports/_http_errors.py`(按上文"关键接口"逐字实现,中文模块/函数 docstring 解释**为什么**折叠空白、为什么头尾保留、为什么 `response_body` 必须降级) **新建**: `src/polygateway/transports/_http_errors.py`(按上文"关键接口"逐字实现,**含其中的 `import httpx`**,加中文模块/函数 docstring 解释**为什么**折叠空白、为什么头尾保留、为什么 `response_body` 必须降级)
**新建测试**: `tests/unit/test_http_error_body.py` **新建测试**: `tests/unit/test_http_error_body.py`
@@ -155,6 +171,7 @@ resp = httpx.Response(400, stream=_Unread()) # 未 read → .text 抛 Response
- `_translate_429` 签名改为 `(source, body_text, headers, ctx)`,两支 message 各自追加 `compose_message` 后缀,构造改用 `**ctx`;**`json.loads` 仍读原文 `body_text`**。 - `_translate_429` 签名改为 `(source, body_text, headers, ctx)`,两支 message 各自追加 `compose_message` 后缀,构造改用 `**ctx`;**`json.loads` 仍读原文 `body_text`**。
- message 主体逐字保持 1.1.2 原样(`凭据失效/欠费: {status}` / `请求被拒: 400` / `瞬时错误: {status}` / `客户端错误: {status}` / `配额耗尽(insufficient_quota)` / `限速: 429`),只在末尾追加 ` | {摘要}` - message 主体逐字保持 1.1.2 原样(`凭据失效/欠费: {status}` / `请求被拒: 400` / `瞬时错误: {status}` / `客户端错误: {status}` / `配额耗尽(insufficient_quota)` / `限速: 429`),只在末尾追加 ` | {摘要}`
- 三个调用点(`:402` embed、`:417` stream、`:509` 非流式)签名不变,**不改动**。 - 三个调用点(`:402` embed、`:417` stream、`:509` 非流式)签名不变,**不改动**。
- **补 import**:`PolyGatewayError`(errors)与 `compose_message` / `summarize_body`(`._http_errors`),见上文关键接口——漏补则 `make lint` 报 F821(Codex 审查 2026-08-16 提出)。
- **不改** `operation` 硬编码 `"chat"`(设计 §5.4 有意留给独立 issue)。 - **不改** `operation` 硬编码 `"chat"`(设计 §5.4 有意留给独立 issue)。
**测试**(`tests/unit/test_openai_compat.py`,沿用既有 `_transport_for(handler)` + `httpx.MockTransport`): **测试**(`tests/unit/test_openai_compat.py`,沿用既有 `_transport_for(handler)` + `httpx.MockTransport`):
@@ -0,0 +1,37 @@
---
type: plan
node_id: plan:issue10-error-body-retention-plan
title: "实现计划: HTTP 错误响应体留存(Issue #10)"
date: 2026-08-16
---
# 实现计划: HTTP 错误响应体留存(Issue #10)
**全文**: `plans/2026-08-16-issue10-error-body-retention.md`|**实现**: [[design:issue10-error-body-retention]]|**分支**: `feat/issue-10-error-body-retention`
## 任务分解
| # | 任务 | 产出 |
|---|---|---|
| 1 | 内核字段 | `PolyGatewayError.body_text`(默认空串)+ 与 `raw_text` 的界限 docstring |
| 2 | 共享摘要单元 | 新建 `transports/_http_errors.py`:`summarize_body` / `compose_message` / `response_body` |
| 3 | openai_compat 收口 | `_status_to_error` 表驱动;429 判类型仍读原文 |
| 4 | monkey_ocr 收口 | `_classify_status` 同款,含 `ResponseNotRead` 降级 |
| 5 | **端到端验收** | 400 调用后 SQLite `error` 列含摘要——本计划的硬判据 |
| 6 | 文档与版本 | 1.2.0、README pin `>=1.2,<2`、CHANGELOG、ARCHITECTURE §6.2 |
| 7 | 合并前门 | lint + 全套件 + 全新上下文 verifier |
依赖:1‖2 → 3‖4 → 5 → 6 → 7。
## 计划期钉死的两条实现红线
1. **429 判 `insufficient_quota` 必须解析未截断原文**,不得改用摘要——摘要会破坏 JSON,超长体一旦改用摘要解析,配额耗尽的源将不再 `force_open`,把诊断改进变成治理 bug。Task 3.4 有专门回归用例。
2. **message 主体逐字保持 1.1.2 原样**,只在末尾追加摘要后缀;状态码→分类映射逐条不变,验收要求既有分类断言零改写。
## 保真校验
不涉及 `reference/` 迁移。错误分类映射不变,保真体现为"既有分类断言全部保留"。
## 执行期观察
Task 计划提交时 pre-commit 钩子的全套件跑出现一次 `tests/e2e/test_compat_projects.py::TestGovDocOnboarding::test_call_site_shape_runs_governed` 失败,单独跑与 e2e 全目录跑(7 passed / 23.30s,每例 2-3.5s)均通过,重跑全套件亦通过 → 判为真实网关抖动,非回归。该用例正是 [[design:issue8-stall-budget]] 当年记录的三个漂移用例之一,e2e 打真实网关的固有 flaky 面仍在。