fairlead 0.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. fairlead-0.10.0/LICENSE +21 -0
  2. fairlead-0.10.0/PKG-INFO +490 -0
  3. fairlead-0.10.0/README.md +455 -0
  4. fairlead-0.10.0/pyproject.toml +148 -0
  5. fairlead-0.10.0/src/fairlead/__init__.py +150 -0
  6. fairlead-0.10.0/src/fairlead/_context_digest.py +109 -0
  7. fairlead-0.10.0/src/fairlead/_operational_logging.py +676 -0
  8. fairlead-0.10.0/src/fairlead/_projection.py +340 -0
  9. fairlead-0.10.0/src/fairlead/adapters/__init__.py +5 -0
  10. fairlead-0.10.0/src/fairlead/adapters/memory.py +136 -0
  11. fairlead-0.10.0/src/fairlead/audit.py +110 -0
  12. fairlead-0.10.0/src/fairlead/context.py +210 -0
  13. fairlead-0.10.0/src/fairlead/contracts/__init__.py +117 -0
  14. fairlead-0.10.0/src/fairlead/contracts/_base.py +61 -0
  15. fairlead-0.10.0/src/fairlead/contracts/artifacts.py +106 -0
  16. fairlead-0.10.0/src/fairlead/contracts/audit.py +87 -0
  17. fairlead-0.10.0/src/fairlead/contracts/context.py +530 -0
  18. fairlead-0.10.0/src/fairlead/contracts/events.py +262 -0
  19. fairlead-0.10.0/src/fairlead/contracts/metering.py +299 -0
  20. fairlead-0.10.0/src/fairlead/contracts/models.py +95 -0
  21. fairlead-0.10.0/src/fairlead/contracts/probe.py +122 -0
  22. fairlead-0.10.0/src/fairlead/contracts/prompt.py +117 -0
  23. fairlead-0.10.0/src/fairlead/contracts/run.py +382 -0
  24. fairlead-0.10.0/src/fairlead/contracts/usage.py +102 -0
  25. fairlead-0.10.0/src/fairlead/errors.py +204 -0
  26. fairlead-0.10.0/src/fairlead/experimental/__init__.py +15 -0
  27. fairlead-0.10.0/src/fairlead/experimental/_synthetic_doctor.py +133 -0
  28. fairlead-0.10.0/src/fairlead/experimental/coordinator.py +1015 -0
  29. fairlead-0.10.0/src/fairlead/experimental/deepseek_anthropic_doctor.py +808 -0
  30. fairlead-0.10.0/src/fairlead/experimental/doctor_cli.py +665 -0
  31. fairlead-0.10.0/src/fairlead/experimental/glm_doctor.py +624 -0
  32. fairlead-0.10.0/src/fairlead/experimental/model_doctor.py +843 -0
  33. fairlead-0.10.0/src/fairlead/experimental/providers/__init__.py +39 -0
  34. fairlead-0.10.0/src/fairlead/experimental/providers/_env_file.py +73 -0
  35. fairlead-0.10.0/src/fairlead/experimental/providers/deepseek.py +210 -0
  36. fairlead-0.10.0/src/fairlead/experimental/providers/deepseek_anthropic.py +592 -0
  37. fairlead-0.10.0/src/fairlead/experimental/providers/deepseek_config.py +163 -0
  38. fairlead-0.10.0/src/fairlead/experimental/providers/zai.py +216 -0
  39. fairlead-0.10.0/src/fairlead/experimental/providers/zai_config.py +175 -0
  40. fairlead-0.10.0/src/fairlead/experimental/pydantic_ai/__init__.py +43 -0
  41. fairlead-0.10.0/src/fairlead/experimental/pydantic_ai/_errors.py +175 -0
  42. fairlead-0.10.0/src/fairlead/experimental/pydantic_ai/_model.py +225 -0
  43. fairlead-0.10.0/src/fairlead/experimental/pydantic_ai/_settings.py +33 -0
  44. fairlead-0.10.0/src/fairlead/experimental/pydantic_ai/_signals.py +136 -0
  45. fairlead-0.10.0/src/fairlead/experimental/pydantic_ai/_usage.py +102 -0
  46. fairlead-0.10.0/src/fairlead/experimental/pydantic_ai/assembly.py +250 -0
  47. fairlead-0.10.0/src/fairlead/experimental/pydantic_ai/hashing.py +186 -0
  48. fairlead-0.10.0/src/fairlead/experimental/pydantic_ai/plan.py +161 -0
  49. fairlead-0.10.0/src/fairlead/experimental/pydantic_ai/probe.py +517 -0
  50. fairlead-0.10.0/src/fairlead/experimental/pydantic_ai/runtime.py +770 -0
  51. fairlead-0.10.0/src/fairlead/journal.py +238 -0
  52. fairlead-0.10.0/src/fairlead/metering.py +129 -0
  53. fairlead-0.10.0/src/fairlead/ports.py +104 -0
  54. fairlead-0.10.0/src/fairlead/projection.py +5 -0
  55. fairlead-0.10.0/src/fairlead/py.typed +1 -0
  56. fairlead-0.10.0/src/fairlead/runtime.py +88 -0
  57. fairlead-0.10.0/src/fairlead/schemas/v1/artifact-ref.schema.json +77 -0
  58. fairlead-0.10.0/src/fairlead/schemas/v1/attempt-descriptor.schema.json +34 -0
  59. fairlead-0.10.0/src/fairlead/schemas/v1/attempt-event-payload.schema.json +329 -0
  60. fairlead-0.10.0/src/fairlead/schemas/v1/attempt-execution-context.schema.json +24 -0
  61. fairlead-0.10.0/src/fairlead/schemas/v1/attempt-metering-summary.schema.json +254 -0
  62. fairlead-0.10.0/src/fairlead/schemas/v1/context-bundle-ref.schema.json +80 -0
  63. fairlead-0.10.0/src/fairlead/schemas/v1/context-bundle.schema.json +502 -0
  64. fairlead-0.10.0/src/fairlead/schemas/v1/context-disposition.schema.json +146 -0
  65. fairlead-0.10.0/src/fairlead/schemas/v1/context-part.schema.json +171 -0
  66. fairlead-0.10.0/src/fairlead/schemas/v1/context-receipt-item.schema.json +263 -0
  67. fairlead-0.10.0/src/fairlead/schemas/v1/context-receipt-source.schema.json +93 -0
  68. fairlead-0.10.0/src/fairlead/schemas/v1/cost-estimate.schema.json +33 -0
  69. fairlead-0.10.0/src/fairlead/schemas/v1/create-run-result.schema.json +782 -0
  70. fairlead-0.10.0/src/fairlead/schemas/v1/metering-totals.schema.json +167 -0
  71. fairlead-0.10.0/src/fairlead/schemas/v1/model-attempt-record.schema.json +314 -0
  72. fairlead-0.10.0/src/fairlead/schemas/v1/model-defaults.schema.json +87 -0
  73. fairlead-0.10.0/src/fairlead/schemas/v1/model-target.schema.json +163 -0
  74. fairlead-0.10.0/src/fairlead/schemas/v1/probe-check.schema.json +104 -0
  75. fairlead-0.10.0/src/fairlead/schemas/v1/probe-report.schema.json +352 -0
  76. fairlead-0.10.0/src/fairlead/schemas/v1/prompt-contract-ref.schema.json +100 -0
  77. fairlead-0.10.0/src/fairlead/schemas/v1/prompt-contract.schema.json +108 -0
  78. fairlead-0.10.0/src/fairlead/schemas/v1/prompt-use-ref.schema.json +131 -0
  79. fairlead-0.10.0/src/fairlead/schemas/v1/prompt-use.schema.json +226 -0
  80. fairlead-0.10.0/src/fairlead/schemas/v1/run-audit-bundle.schema.json +1011 -0
  81. fairlead-0.10.0/src/fairlead/schemas/v1/run-error-payload.schema.json +70 -0
  82. fairlead-0.10.0/src/fairlead/schemas/v1/run-error.schema.json +55 -0
  83. fairlead-0.10.0/src/fairlead/schemas/v1/run-event.schema.json +526 -0
  84. fairlead-0.10.0/src/fairlead/schemas/v1/run-intent.schema.json +306 -0
  85. fairlead-0.10.0/src/fairlead/schemas/v1/run-metering-statement.schema.json +317 -0
  86. fairlead-0.10.0/src/fairlead/schemas/v1/run-record.schema.json +761 -0
  87. fairlead-0.10.0/src/fairlead/schemas/v1/run-spec.schema.json +335 -0
  88. fairlead-0.10.0/src/fairlead/schemas/v1/runtime-output-delta-signal.schema.json +149 -0
  89. fairlead-0.10.0/src/fairlead/schemas/v1/runtime-signal.schema.json +314 -0
  90. fairlead-0.10.0/src/fairlead/schemas/v1/runtime-tool-call-signal.schema.json +149 -0
  91. fairlead-0.10.0/src/fairlead/schemas/v1/runtime-tool-result-signal.schema.json +149 -0
  92. fairlead-0.10.0/src/fairlead/schemas/v1/token-usage.schema.json +57 -0
  93. fairlead-0.10.0/src/fairlead/schemas/v1/usage-receipt.schema.json +135 -0
  94. fairlead-0.10.0/src/fairlead/schemas/v1/usage-recorded-payload.schema.json +150 -0
  95. fairlead-0.10.0/src/fairlead/testing/__init__.py +31 -0
  96. fairlead-0.10.0/src/fairlead/testing/fakes.py +158 -0
  97. fairlead-0.10.0/src/fairlead/testing/journal.py +1406 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fairlead contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,490 @@
1
+ Metadata-Version: 2.4
2
+ Name: fairlead
3
+ Version: 0.10.0
4
+ Summary: A composable, auditable runtime foundation for Pydantic AI applications.
5
+ Keywords: ai,audit,llm,pydantic-ai,runtime
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Classifier: Typing :: Typed
17
+ Requires-Dist: pydantic>=2.12,<3
18
+ Requires-Dist: pydantic-ai-slim==2.35.3
19
+ Requires-Dist: pydantic-ai-slim[anthropic]==2.35.3 ; extra == 'anthropic'
20
+ Requires-Dist: pydantic-ai-slim[google]==2.35.3 ; extra == 'google'
21
+ Requires-Dist: pydantic-ai-harness>=0.26,<0.27 ; extra == 'harness'
22
+ Requires-Dist: pydantic-ai-slim[openai]==2.35.3 ; extra == 'openai-compatible'
23
+ Requires-Dist: pydantic-ai-slim[zai]==2.35.3 ; extra == 'zai'
24
+ Requires-Python: >=3.12
25
+ Project-URL: Changelog, https://github.com/gugia/fairlead/blob/main/CHANGELOG.md
26
+ Project-URL: Homepage, https://github.com/gugia/fairlead
27
+ Project-URL: Issues, https://github.com/gugia/fairlead/issues
28
+ Project-URL: Repository, https://github.com/gugia/fairlead.git
29
+ Provides-Extra: anthropic
30
+ Provides-Extra: google
31
+ Provides-Extra: harness
32
+ Provides-Extra: openai-compatible
33
+ Provides-Extra: zai
34
+ Description-Content-Type: text/markdown
35
+
36
+ # Fairlead
37
+
38
+ Fairlead 是面向 Pydantic AI 应用的薄型运行基础:它固定跨项目值得共享的模型目标、探测、提示契约、上下文收据、运行证据与事件信封,同时把数据库、队列、HTTP 框架和业务语义留给应用。
39
+
40
+ 当前统一 release train 版本为 `0.10.0`。首次公共 PyPI 发布只包含核心 distribution `fairlead`;
41
+ `fairlead-reference-postgres-host` 与 `fairlead-reference-answer` 继续是公开仓库源码模板,可作为
42
+ GitHub/private artifact 交付,但不会进入公共包索引。`v0.10.0` tag、GitHub Release 与 PyPI 首发尚未执行,
43
+ 因此本文的公共安装命令要在发布完成后使用。除 v0.7 的 PostgreSQL
44
+ Submission/Operation/Run/Result/HTTP/SSE 全链外,参考宿主现已提供固定 slot daemon、Outbox
45
+ dispatcher、进程内/PostgreSQL `NOTIFY`/Redis 可选 hint、T6 Artifact 清理与 legal hold、独立
46
+ Recovery selector/orphan 对账、operator API、严格 JWT/JWKS、不可变配置 bundle、外部密钥引用、
47
+ 健康检查、低基数指标、SLO/告警,以及 PostgreSQL 灾备和容量资格资产。配置 alias 只在服务端
48
+ Submission 时解析;Operation、Worker 与审计永久绑定同一 exact revision/canonical SHA-256。
49
+
50
+ `0.10.0` 私有发布列车继续协调三个 MIT distribution,并生成 hash、CycloneDX SBOM、基于受控 Linux
51
+ 安装元数据的第三方许可证证据和 GitHub artifact attestation;attestation 前必须绑定同一 tag commit 的成功
52
+ quality run。这里的“私有”只表示该 workflow 不上传公共 PyPI,GitHub Actions artifact 的下载权限仍由
53
+ 仓库 read 权限控制;它不是机密分发、加密封装或 secret 传输通道,reference 源码也保持公开。制品中不得
54
+ 放入凭据或客户敏感正文。发布 receipt 绑定 tag/commit、quality run ID/attempt、release workflow ref/SHA 与最终
55
+ `SHA256SUMS` 摘要,再由外层 `RELEASE-HASHES` 同时覆盖 receipt 和 `SHA256SUMS`。仓库内机制通过
56
+ 不能替代真实 IdP、Secret Manager、
57
+ 异地恢复、HA fencing、主机断电、生产容量与 on-call 路由演练;未执行的目标环境声明保持
58
+ `not-qualified`。provider exactly-once 仍不是承诺。
59
+
60
+ SBOM 是 uv 从 universal lock 导出的跨平台清单;许可证 JSON 只索引固定 Ubuntu 24.04、x86_64、
61
+ CPython 3.14 source-project 环境中 `all-extras/no-dev` 实际安装 distribution 的许可证元数据与文件摘要。
62
+ manifest 精确冻结 SBOM-only 的 name/version/marker,报告完整披露但不执行 marker 求值;跨平台许可证覆盖
63
+ 保持 `not-qualified`,也不构成法律合规结论或 wheel/sdist smoke 环境闭包。smoke 额外安装的
64
+ `psycopg-binary` 只是测试期依赖,不随发布声明。
65
+
66
+ 公共核心包使用独立 Trusted Publishing workflow:GitHub Release `published` 后,先在无 OIDC job 中构建并
67
+ 核对 exact tag/SHA/quality run,再经独立 transfer verification,最后才由 `pypi` Environment 审批的发布
68
+ job 取得 OIDC。公共候选目录物理上只能包含核心 wheel 与 sdist。设计边界见
69
+ [核心包 PyPI ADR](https://github.com/gugia/fairlead/blob/v0.10.0/docs/architecture/0017-core-pypi-trusted-publishing.md),
70
+ 首发严格顺序与部分上传处置见
71
+ [PyPI Trusted Publishing runbook](https://github.com/gugia/fairlead/blob/v0.10.0/docs/guides/pypi-trusted-publishing.md)。
72
+
73
+ 新项目的六角色装配、启动/停止顺序与渐进采用方式见
74
+ [reference production integration](https://github.com/gugia/fairlead/blob/v0.10.0/docs/guides/reference-production-integration.md)。
75
+ 灾备收据的 detached lint/gate 信任边界和完整命令见
76
+ [qualification runbook](https://github.com/gugia/fairlead/blob/v0.10.0/operations/qualification/README.md);三包私有
77
+ 发布必须按 [v0.10 tag-ref dispatch 命令](https://github.com/gugia/fairlead/blob/v0.10.0/docs/product/v0.10-scope.md#私有发布工程)
78
+ 启动,不能从 `main` dispatch 后只改输入 tag。
79
+
80
+ 稳定层继续以 contracts、纯投影、最小 ports 和中间件中立的读取服务为主;
81
+ `fairlead.experimental.RunCoordinator`、Pydantic AI
82
+ Runtime、进程内 Journal、provider composition root、model doctor 与 reference packages 用于验证
83
+ 边界,不代表已经提供生产运行平台。现有项目不需要迁移;Fairlead 首先服务未来项目,待契约在真实
84
+ 业务中稳定后再按需接入旧项目。
85
+
86
+ ## 设计边界
87
+
88
+ ```text
89
+ 业务应用 / Agent / API / Worker
90
+
91
+ Fairlead ports + contracts
92
+
93
+ Pydantic AI 与可选 Harness 能力
94
+
95
+ Provider / PostgreSQL / Redis / HTTP adapters
96
+ ```
97
+
98
+ - 核心不依赖 PostgreSQL、Redis、FastAPI、任务队列或前端框架。
99
+ - Pydantic AI Harness 是可选能力,不是 Fairlead 的基类。
100
+ - `RunJournal` 是恢复判断所依赖的权威事实端口;权威 `RunEvent` 先提交 Journal,再由 `EventPublisher` 尽力广播。
101
+ - 输出与工具的 `RuntimeSignal` 是独立实时通道,不进入 Journal,也不能替代权威事件。
102
+ - `ModelRuntime.prepare` 在 provider I/O 前解析实际目标;协调器持久化 `attempt.started` 后,才把同一 Attempt ID 与权威开始时间交给 `PreparedAttempt.execute(context, emit)`。
103
+ - `RunRecord.attempts` 按尝试序号保存每个 Attempt 的最新快照;所有 Journal Adapter 在创建时调用公开的 `validate_run_creation`,追加时调用 `project_run_event`,不得自行解释或弱化状态转换。
104
+ - `RunEvent.payload` 只允许类型化、深冻结的 Attempt、usage、错误载荷或 `null`,不再接受任意 JSON。Schema 中的 `oneOf` 只表达 `eventType`/payload 的结构关联;跨字段生命周期、时间、usage 与错误不变量仍以 Pydantic 校验器和公共投影函数为权威。
105
+ - 模型声明能力与探测到的能力分开记录,探测结果不会静默修改配置。
106
+ - `RunIntent` 保存不含时间与 Artifact 的 `PromptUseRef`,因此提示契约、运行时输入哈希和首次 provider-neutral `ModelRequest` 规范语义哈希都参与幂等意图比较;后续工具/校验重试历史不追溯性改变该值。
107
+ - 默认证据只保存哈希、引用、版本、计量和脱敏错误,不保存密钥、完整提示或客户原文。除 System Instructions 明确按原始 UTF-8 字节校验外,各类资产使用自己的版本化 canonicalizer;Context Bundle 由 Fairlead 已冻结的算法复算,不能把一个 digest 算法冒充为全局规范。
108
+ - 业务工具、业务输出 Schema、权限与人工确认规则仍由应用拥有。
109
+ - `RunAuditBundle` 把创建投影、当前投影和完整权威事件序列装入同一份可移植快照;`verify_run_audit_bundle` 复用公共 reducer 核对内部一致性,但不提供签名、公证或真实性保证。
110
+ - `read_run_audit_bundle` 用高水位分页和投影双读从最小 `RunJournal` 取得稳定 revision 的审计包;坏页、身份漂移和回退失败关闭,竞争与历史大小都有硬上限。
111
+ - `fairlead.testing` 提供 core 与 persistence Journal 资格套件;后者只证明测试环境中的独立多 handle、并发 CAS 和正常关闭后重开,不是生产耐久认证。
112
+ - `project_run_metering` 从任意合法 Run revision 生成逐 Attempt 计量投影;成本估算只在同一币种与同一价格目录 revision 内相加,收据数不会被冒充为 provider 请求数或账单数量。
113
+ - 实验性协调器只执行一次 Attempt,不自动发起 Fairlead 级完整 Agent run 重试或 fallback;成功输出不持久化,幂等回放返回 `output=None`。
114
+
115
+ 详细范围见 [v0.1 范围](https://github.com/gugia/fairlead/blob/v0.10.0/docs/product/v0.1-scope.md)、
116
+ [v0.2 provider 证据范围](https://github.com/gugia/fairlead/blob/v0.10.0/docs/product/v0.2-scope.md)、
117
+ [v0.3 上下文范围](https://github.com/gugia/fairlead/blob/v0.10.0/docs/product/v0.3-scope.md)、
118
+ [v0.4 审计与计量范围](https://github.com/gugia/fairlead/blob/v0.10.0/docs/product/v0.4-scope.md)、
119
+ [v0.5 Journal 资格范围](https://github.com/gugia/fairlead/blob/v0.10.0/docs/product/v0.5-scope.md)、
120
+ [v0.6 参考接入范围](https://github.com/gugia/fairlead/blob/v0.10.0/docs/product/v0.6-scope.md)、
121
+ [v0.7 PostgreSQL 参考宿主范围](https://github.com/gugia/fairlead/blob/v0.10.0/docs/product/v0.7-scope.md)、
122
+ [v0.8 常驻执行范围](https://github.com/gugia/fairlead/blob/v0.10.0/docs/product/v0.8-scope.md)、
123
+ [v0.9 生产控制面范围](https://github.com/gugia/fairlead/blob/v0.10.0/docs/product/v0.9-scope.md) 和
124
+ [v0.10 灾备/双通道发布范围](https://github.com/gugia/fairlead/blob/v0.10.0/docs/product/v0.10-scope.md)。架构入口从
125
+ [核心边界 ADR](https://github.com/gugia/fairlead/blob/v0.10.0/docs/architecture/0001-core-boundaries.md) 到
126
+ [核心包 PyPI ADR](https://github.com/gugia/fairlead/blob/v0.10.0/docs/architecture/0017-core-pypi-trusted-publishing.md)
127
+ 连续编号;最近五项分别冻结
128
+ [daemon/Outbox/hint](https://github.com/gugia/fairlead/blob/v0.10.0/docs/architecture/0013-daemon-outbox-and-hints.md)、
129
+ [保留/恢复/控制面](https://github.com/gugia/fairlead/blob/v0.10.0/docs/architecture/0014-retention-recovery-and-control-plane.md)、
130
+ [灾备/私有发布](https://github.com/gugia/fairlead/blob/v0.10.0/docs/architecture/0015-production-operations-and-private-release.md)、
131
+ [六角色组合/可观测接线](https://github.com/gugia/fairlead/blob/v0.10.0/docs/architecture/0016-reference-composition-and-observability.md) 与
132
+ [核心包 PyPI Trusted Publishing](https://github.com/gugia/fairlead/blob/v0.10.0/docs/architecture/0017-core-pypi-trusted-publishing.md)。
133
+
134
+ ## v0.8–v0.10 生产参考链与复用方式
135
+
136
+ 采用项目复用的是显式角色和契约,不是一个读取所有环境变量的全局容器:
137
+
138
+ | 角色 | 可复用实现 | 采用项目仍需提供 |
139
+ | --- | --- | --- |
140
+ | API | Submission、Operation/Result、SSE、鉴权/配置接线 | 业务请求模型、租户授权、TLS/Ingress |
141
+ | Worker | 固定 slot daemon、lease/fence、精确 program resolver | 业务 Prompt/Schema、provider resource factory |
142
+ | Dispatcher | 耐久 Outbox 与可选 Local/`NOTIFY`/Redis hint | 部署拓扑与 listener 生命周期 |
143
+ | Retention | T6 cutoff、清理收据、legal hold | 业务保留政策与强授权法务读取 |
144
+ | Recovery | case selector、人工命令、orphan 重读 | operator 角色映射与值班流程 |
145
+ | Operator API | 独立 `recovery:write` 权限与安全 wire | 真实 IdP/MFA/审计导出 |
146
+
147
+ 生产可以按角色分进程;开发可以同进程,但各角色仍使用独立停止信号、有界批次和失败收据。Redis、
148
+ HTTP、JWT 与 Prometheus 都通过可选 extra 显式安装;基础 host import 只依赖 Fairlead、Pydantic 与
149
+ Psycopg。关闭全部 hint 后,HTTP 提交、Worker 执行、Result 读取和 SSE 补读仍由 PostgreSQL 完成。
150
+
151
+ 配置和密钥在组合根保持分离:数据库只保存不可变 bundle 与 secret reference,worker 从 Operation
152
+ 读取 exact revision/hash,只有 provider resource scope 解析 secret value。现有项目可以只采用其中
153
+ 一个边界;新项目则应从六角色模板开始,再替换业务 program 与授权策略。
154
+
155
+ ## v0.7 PostgreSQL 参考宿主历史基线
156
+
157
+ v0.7 计划在 reference host 中使用 PostgreSQL 保存 Run Journal、Answer/Summary Result、Artifact、
158
+ Operation Notification 与 Outbox;Redis 和 PostgreSQL `NOTIFY` 只作为可选 hint。核心
159
+ `fairlead.contracts` / `fairlead.ports` 不增加数据库、Operation、Result、Artifact、Outbox 或租约
160
+ 端口,也不会把 PostgreSQL、Redis 或 HTTP 框架变成最小安装依赖。
161
+
162
+ 浏览器通过 `POST /v1/operations` 获得 `202 Accepted` 与 Operation ID,再以带 cursor 的 SSE 读取
163
+ Operation 内连续 Notification。一次 Operation 可以依次关联摘要 Run 和回答 Run;Worker 只从受控
164
+ Submission Artifact 和精确 configuration revision 重建进程内计划,不序列化 `PreparedAnswer`。
165
+ SSE、Redis 或 `pg_notify` 丢失不会丢事实,服务端始终从 PostgreSQL after-exclusive 补读。
166
+
167
+ Artifact 正文默认在主 ArtifactStore 中逻辑保留 30 天,元数据与哈希长期保留;v0.7 当时尚未交付、
168
+ 现由 v0.9 实现的 T6 让清理收据也长期保留。这不等于 ResultStore 中的业务正文、PostgreSQL
169
+ MVCC/WAL、备份、只读副本或
170
+ provider 侧数据也在同一时间物理删除。结果先保存、Run 终态后提交形成的 orphan 窗口仍显式投影为
171
+ `recovery_required`,不会因 Store 共用 PostgreSQL 就被虚称为原子消失。
172
+
173
+ 智能客服与智能问数尚无冻结评测集。v0.7 可以验证持久化、预算、Schema、传输和恢复机制,但不得
174
+ 据此声称摘要忠实、回答正确、问数可用或达到生产语义质量。完整实现与验收边界见
175
+ [v0.7 范围](https://github.com/gugia/fairlead/blob/v0.10.0/docs/product/v0.7-scope.md) 和
176
+ [ADR-0010](https://github.com/gugia/fairlead/blob/v0.10.0/docs/architecture/0010-postgres-operation-and-sse.md)。
177
+
178
+ `reference/postgres-host` 已交付耐久 Operation/Submission、连续类型化 Notification、HTTP 安全投影、
179
+ 严格 SSE cursor/wire、纯状态机、`PostgresRunJournal` 和 `PostgresResultStore`。T1 将请求 Artifact、
180
+ Operation、accepted Notification 与 Outbox 在同一 PostgreSQL 短事务提交;T3 将 Run 当前投影、连续
181
+ RunEvent 和同 revision Outbox 原子提交;T4 将 Answer/Summary Result 与其 output Artifact 原子提交。
182
+ 这些边界的并发、幂等冲突、证据损坏、回滚、锁/statement timeout、ACK 丢失恢复和稳定结构日志均在
183
+ 真实 PostgreSQL 资格验证。Result 长期保留精确业务 bytes;重复的 output Artifact 正文仍按 30 天
184
+ 策略管理,两个保留责任没有被混成同一承诺。
185
+
186
+ T2 Worker claim repository 也已交付。只有首次领取 `accepted` Operation 才形成 fresh execution;
187
+ lease 已过期的 `running` Operation 只能返回 reconciliation,不会被当作新工作再次执行。领取使用
188
+ 数据库时钟冻结 lease,并以 owner/generation fence 约束续租、release 与 Artifact 读取;重领会递增
189
+ generation,使旧持有者不能继续提交。单次调用最多返回一个 claim,但会有界锁定并扫描最多 16 个
190
+ 候选;只读证据损坏的 poison 候选在成功跳过并继续领取时会逐条记录 ERROR;扫描窗口耗尽或全部为
191
+ poison 时以 claim 级 ERROR 失败关闭,不宣称逐行 durable quarantine 或严格公平。领取后可通过活动
192
+ fence 在独立短读事务中复核 scope、Artifact ID、SHA-256、policy、retention、创建时间绑定、30 天到期
193
+ 时间与状态;正文仍可用时还会重算正文 hash 与精确大小。正文清除后的 tombstone 无法从现有事实重算
194
+ 原始大小;media type 和 sensitivity 也只校验为合法值并返回,因为它们未在 Submission 中冻结,不能
195
+ 据此证明另一合法值没有漂移。若已链接 Run 的最后 Attempt
196
+ 仍为 running,repository 会原子写入
197
+ `recovery_required`、连续 Notification 与同 revision Outbox。全部事务保持短小,Redis 关闭不影响
198
+ 领取,且该切片不创建或调用 provider;其他 expired 状态统一返回 `ReconciliationClaim`,T2 不读 Result,
199
+ 也不分类 terminal/orphan。release 不提供幂等重放,错误、二次或过期 token 返回 lease lost,commit ACK
200
+ 丢失报告 outcome unknown。renew 的 ACK 丢失同样是 outcome unknown;以仍活动的同 owner/generation
201
+ 重试只保证单调延租,不保证复现原到期时间。fresh claim 与 `load_artifact` 都只是短时 handoff,后续
202
+ 动作仍须验证或续租,不能当作长期 provider authority。fence 不覆盖现有 T3/T4 写入,也不能撤销已经在途的外部 I/O,因此
203
+ 不构成 provider exactly-once 保证。
204
+
205
+ 同一参考包现已提供可选 `http` extra 和显式装配的 FastAPI App。`POST /v1/operations` 接收调用方已经
206
+ 规范化的 raw Artifact bytes,认证解析器在读正文前注入私有 scope,服务端对精确字节计算摘要;
207
+ `Idempotency-Key`、`Fairlead-Configuration-Revision` 和无参数 `Content-Type` 必须各出现一次,正文
208
+ 有硬上限且只接受缺省或 `identity` 编码。新建与相同意图回放均返回 `202`、`Location` 和
209
+ `Cache-Control: no-store`。Operation 查询与 PostgreSQL Notification 快照页保持 scope 隔离;SSE
210
+ 按连续 sequence 补读,等待期间不占数据库连接,终态已追平返回 `204`,落后时先补齐再关闭。FastAPI
211
+ 模块不会从包根导入,因此数据库宿主的基础安装不被 HTTP 框架反向绑定。
212
+
213
+ T5 PostgreSQL durable Run-link、settle 与 terminal reconcile 已实现。宿主现提供一次最多领取一个
214
+ Operation 的 `PostgresOperationRunner.run_once()`:它加载受权 Submission Artifact,用单一 lease
215
+ session 串行 renew/link/authorize/settle,按精确 configuration revision 解析应用程序,并把受控
216
+ preflight failure、临时 unavailable、terminal 与 recovery 结果收敛为类型化 disposition。参考问答包
217
+ 则提供业务专用的 `StaticAnswerProgramResolver`、`AnswerOperationProgram` 和
218
+ `PostgresOperationRunExecutor`,用 Operation 私有身份重建 Answer/Summary 工作流、确定性阶段 Run ID
219
+ 与域分隔 Journal key,并在 provider I/O 前依次执行 T3 create、T5 link、未开始 Run 授权、
220
+ `run.started`、runtime prepare、`attempt.started` 和最终 `AttemptExecutionGate`。最终 gate 以数据库
221
+ 时钟再次复核活动 lease 与完整 lineage;失败会留下 running Attempt 供对账,provider/usage/Result
222
+ 保持为零。该路径是可由宿主驱动的一次执行能力,不会自行启动轮询 daemon,也不提供 provider
223
+ exactly-once 或 active Attempt 重放。
224
+
225
+ 结果路由会先完成认证和完整 Operation 历史校验:活动/恢复态
226
+ 返回明确 `409`,失败或取消
227
+ 返回不可用 `409`;只有 `succeeded` Operation 的 result ID、scope 与最终 Answer Run 全部匹配时才
228
+ 返回 `fairlead.reference.postgres-host.http-result.v1`。普通 HTTP App 不会自动调度
229
+ `run_once()`;采用方仍需显式装配认证、连接池、精确配置 factory 和进程监督/唤醒机制。因此“提交
230
+ HTTP”本身不等于模型已经启动或 Operation 会无人值守完成。T6 Artifact 清理、Outbox dispatcher、
231
+ 这些在 v0.7 仍是独立后续边界的能力,现分别由 v0.8 与 v0.9 交付,且没有反向进入稳定核心端口。
232
+ 真实 PostgreSQL 18、全量覆盖率与隔离 wheel 验证见
233
+ [v0.7 PostgreSQL HTTP/SSE 资格账本](https://github.com/gugia/fairlead/blob/v0.10.0/docs/evidence/2026-08-28-postgres-http-sse-qualification.md)。
234
+ T3 的事务、conformance 与损坏检测证据见
235
+ [v0.7 PostgreSQL RunJournal 资格账本](https://github.com/gugia/fairlead/blob/v0.10.0/docs/evidence/2026-08-28-postgres-run-journal-qualification.md)。
236
+ T4 的原子结果、权限、故障与 HTTP 资格见
237
+ [v0.7 PostgreSQL ResultStore 资格账本](https://github.com/gugia/fairlead/blob/v0.10.0/docs/evidence/2026-08-28-postgres-result-store-qualification.md)。
238
+ T2 的真实 PostgreSQL 18.4 矩阵、401 条测试、100% statement/branch、最小权限与隔离 wheel 证据见
239
+ [v0.7 PostgreSQL Worker claim 资格账本](https://github.com/gugia/fairlead/blob/v0.10.0/docs/evidence/2026-08-28-postgres-worker-claim-qualification.md);
240
+ 该 T2 账本本身不包含新增 runner 全链;后者由下述 2026-08-29 独立资格账本证明。
241
+ T5 的 durable link、settle、terminal reconcile 边界与资格矩阵见
242
+ [v0.7 PostgreSQL Operation projection 资格账本](https://github.com/gugia/fairlead/blob/v0.10.0/docs/evidence/2026-08-28-postgres-operation-projection-qualification.md)。
243
+ 新增 runner、Answer-only 与 Summary→Answer HTTP/Worker/SSE 完整链的 PostgreSQL 18.4 结果见
244
+ [v0.7 PostgreSQL reference runner 资格账本](https://github.com/gugia/fairlead/blob/v0.10.0/docs/evidence/2026-08-29-postgres-reference-runner-qualification.md)。
245
+
246
+ ## v0.6 参考接入与可审计压缩
247
+
248
+ `reference/answer-service` 用一个“带来源回答”用例串起预算分区、确定性纳入/裁剪/丢弃、独立
249
+ 结构化摘要 Run、主回答 Run、结果先持久化、内容寻址 Artifact、审计读取与计量投影。模型摘要
250
+ 不是主 Run 内的隐藏 processor:它拥有自己的 target、PromptUse、Context、Attempt、usage 和
251
+ StoredSummary;只有精确 producer 成功且产物、引用和预算重新校验后,主链才会编译带 producer
252
+ lineage 的 child Bundle。
253
+
254
+ 该参考包用真实 OpenAI Responses 与 Anthropic Messages SDK/provider/model 加 MockTransport,把
255
+ 同一个 `AnswerService` 经过 `PydanticAIModelRuntime -> RunCoordinator -> Journal -> ResultStore ->
256
+ Audit/Metering` 跑通;两条 target 的 provider 身份分别为 `openai` 与 `anthropic`。这证明业务层的
257
+ 多供应商抽象在两套不同 SDK/profile/wire 上离线成立,不证明任一真实账户当前可达,也不证明供应商
258
+ 故障隔离、账单或生产运营资格。
259
+
260
+ 另有两条 provider 均为 `deepseek` 的 Responses 与 Anthropic-compatible 路径,用于检验同一供应商
261
+ 跨协议接线。生产 tokenizer、授权检索、耐久 Journal/结果/Artifact 存储、HTTP、orphan 恢复和摘要
262
+ 语义评测仍由采用项目决定。完整运行方式与真实性边界见
263
+ [`reference/answer-service`](https://github.com/gugia/fairlead/blob/v0.10.0/reference/answer-service/README.md)。
264
+
265
+ 2026-08-28 的两次 doctor、首次可审计截断失败、预算校准和三 Run 成功结果见
266
+ [v0.6 live 验证账本](https://github.com/gugia/fairlead/blob/v0.10.0/docs/evidence/2026-08-28-v0.6-live-verification.md)。实际使用 9/10 次授权
267
+ 预算,无自动重试;该证据不外推为第二供应商实网资格。
268
+
269
+ 随后增加国内 GLM/ZAI composition 与独立 doctor,并让 DeepSeek Responses 与 GLM Chat
270
+ Completions 对同一个 synthetic 请求分别完整执行 Summary + Answer。2026-08-28 的 GLM doctor 与
271
+ 四 Run 同链均通过,压缩 parent/producer/严格减量证据在两侧全部成立,见
272
+ [v0.7 provider live 验证账本](https://github.com/gugia/fairlead/blob/v0.10.0/docs/evidence/2026-08-28-v0.7-provider-live-verification.md) 和
273
+ [ADR-0011](https://github.com/gugia/fairlead/blob/v0.10.0/docs/architecture/0011-glm-zai-provider.md)。这证明两个真实供应商在该时点的接线与机械
274
+ 契约,不证明持续健康、fallback、语义质量或账单准确。
275
+
276
+ DeepSeek Responses 默认 thinking 的候选失败、v3 显式 `reasoning.effort=none` 修正,以及最终
277
+ DeepSeek 2/2 + GLM 2/2 的一次性复测见
278
+ [2026-08-29 follow-up](https://github.com/gugia/fairlead/blob/v0.10.0/docs/evidence/2026-08-29-v0.7-provider-live-verification-followup.md)。该轮两侧
279
+ 压缩 lineage 均完整,执行后两供应商各剩 6 次授权 Run;这仍只是时点机械资格。
280
+
281
+ ## v0.5 Journal 适配资格与一致审计读取
282
+
283
+ 应用不再需要自己拼接非原子的 `get + read_events`。`read_run_audit_bundle` 先固定当前 revision 高水位,只读取严格连续的 `1..H`,再要求第二次投影逐字段全等,最后仍调用公共审计 reducer 完整重放。活动 Run 的同 Spec revision 增长可在有限次数内整轮重试;回退、同 revision 改写、终态增长、Run 消失、空页、跳号、错 Run、非法模型或超长页立即以稳定安全错误失败。
284
+
285
+ `page_size`、`consistency_attempts` 和 `max_events` 都是有界严格整数。普通 Adapter 读取异常被映射为不携带原始异常链的 `AuditSnapshotReadError`;取消原样传播。成功只证明某个两次投影观测间稳定 revision 的包内一致性,不证明返回时最新、数据库事务快照、真实性、防篡改或存储耐久。
286
+
287
+ 未来项目实现自己的 Adapter 后,可以复用:
288
+
289
+ ```python
290
+ from fairlead.testing import (
291
+ run_journal_core_conformance,
292
+ run_journal_persistence_conformance,
293
+ )
294
+ ```
295
+
296
+ core 套件检查创建、幂等、CAS、事件冲突、原子性、分页、并发和审计重放。persistence 套件要求调用方提供一个隔离 `RunJournalTestStore`,实际打开两个不同 handle,再在关闭后打开第三个 handle 核对记录、事件与索引。通过不能外推到 fsync、进程崩溃、断电、备份、HA 或生产容量。范围和测试生命周期见 ADR-0007。
297
+
298
+ ## v0.4 可验证运行审计与计量投影
299
+
300
+ `build_run_audit_bundle(current_record, events)` 会从当前记录的冻结 `RunSpec` 构造 revision 1 创建投影,并把它与当前投影、从 `run.created` 开始的完整事件序列组成 `RunAuditBundle`。`verify_run_audit_bundle` 重新校验创建元组、事件身份与顺序、唯一 event ID、每一步状态转换和最终全等关系。截断读取、乱序、重复 ID、错误 usage 归属或与当前投影不一致都会失败;应用从非原子 Journal 读取记录与事件时,必须在一致快照中读取或在验证失败后重新读取。
301
+
302
+ 该快照只证明所携带事实彼此一致。攻击者若能同步改写全部字段,仍可生成另一份自洽快照;签名、外部时间戳和不可变存储属于应用或后续独立能力。`ModelTarget`、Prompt、Context、价格目录等版本化资产继续由应用按快照中的引用长期保留,Fairlead 不新增资产仓库端口。
303
+
304
+ `project_run_metering(record)` 对 queued、running 和全部终态使用同一套纯函数规则,生成 `RunMeteringStatement`:
305
+
306
+ - 每个 Attempt 保留状态、请求目标、实际 provider/model 与计量小计;失败或取消前已经确认的 usage 仍会计入;
307
+ - provider 报告 token 的收据与本地估算 token 的收据分开计数;空收据只表示没有已确认 usage 证据,不能推断没有请求或没有费用;
308
+ - priced/unpriced 收据显式计数,成本覆盖率区分 not applicable、unavailable、partial 与 complete;
309
+ - 估算成本只按 `(currency, catalogRevision)` 分组使用 `Decimal` 精确相加,不做汇率换算、舍入、税费或供应商账单对账。
310
+
311
+ 这两个入口不执行 I/O、不发日志,也不增加数据库、队列、HTTP 或 provider 依赖。范围与失败边界见 ADR-0006。
312
+
313
+ ## v0.3 上下文谱系与输入装配
314
+
315
+ Fairlead 现在提供一个不依赖数据库、队列或 provider SDK 的纯 Context 编译边界:
316
+
317
+ - 应用提供候选 `ContextPart`、最终 `ContextPart` 和明确的 `ContextDisposition`;Fairlead 派生来源快照、转换收据、token 对账与可复算 Bundle digest;
318
+ - `included`、`dropped`、`redacted`、`truncated` 和 N:1 `summarized` 都有严格合法矩阵;模型摘要必须引用先前独立的 producer Run/Attempt;
319
+ - `ContextBundleRef` 同时固定 canonicalizer、token estimator 与业务 policy revision;`estimatedTokens` 只表示最终 context part 内容,不等于完整 provider 请求 token;
320
+ - 初始 Bundle 和基于父 Bundle 的子 Bundle 使用同一套验证规则,转换不会静默覆盖父快照;
321
+ - Fairlead 不决定 RAG 排序、权限、业务优先级、脱敏正文或摘要内容,这些仍由应用拥有。
322
+
323
+ 实验性 Pydantic AI adapter 的 input assembly v1 让应用通过既有 `ExecutionPlanSource` 提供已授权解析的最终正文。`prepare` 会验证 Bundle 引用、digest、part 顺序和逐段正文哈希,再把 `document` / `summary` 与类型化 `runtimeInput` 渲染为唯一 canonical JSON envelope 并冻结;`execute` 只使用冻结文本。该路径不新增 Artifact resolver 稳定端口,不调用摘要模型,也不调用 provider token counting。
324
+
325
+ 若 prepare 失败,`RunCoordinator` 先提交权威 `run.failed`,再最多投影一条
326
+ `fairlead.run.prepare_failed` 结构化日志;日志只含 run ID、固定 phase、稳定错误类别/代码和
327
+ `retryable`,安全可重试外部错误为 WARNING、其余为 ERROR,不包含 Context 内部 ID、digest、
328
+ URI、正文或异常图。
329
+
330
+ ## 实验性 Pydantic AI 接线
331
+
332
+ `fairlead.experimental.pydantic_ai` 已提供一个不包含真实供应商 SDK 的离线可测 adapter:
333
+
334
+ - `PydanticAITargetBinding` / `ExactTargetResolver` 把精确 target revision 绑定到应用已经配置的 `Model`;
335
+ - `ExecutionPlanSource` 让应用继续拥有 Agent、deps、业务工具、授权正文读取、输入与输出类型;每个 plan 必须提供完整 Context Bundle、同序已解析正文,以及带显式正整数 `request_limit` 和 `tool_calls_limit`、关闭 `count_tokens_before_request` 的 `UsageLimits`,prepare 会复制该预算;
336
+ - Agent 是当前 Attempt 独占的可变对象,不能跨 plan 共享或在 prepare 后并发修改;binding 中未包装的 Model 可按其公开并发契约复用,但身份和 settings 在活跃 Attempt 期间不得漂移;
337
+ - `PydanticAIModelRuntime` 只接受 `agent.instrument is False` 和 Pydantic AI 自动装配的默认 capability tree,拒绝应用 capability hook 与预包装的任何 `WrapperModel`;prepare、execute 及紧邻真实请求的 Model 边界都会复核身份和策略;
338
+ - Runtime 按持久化的 `canonicalizerRevision` 与 `inputAssemblyRevision` 选择可信实现,execution plan 不能注入验证算法或任意 user prompt;公共 Model 请求边界核对 target-derived `ModelSettings`、静态 `instruction_parts` 以及 tools/structured/thinking 声明能力,这不等同于 provider profile、provider SDK 或最终 HTTP payload;
339
+ - `PydanticAIContractHasherV1` 固定 JSON 输入、单文本首请求、静态 function tools、output object/output tools 与输出模式的 adapter-local 摘要;它拒绝 native/deferred tools、动态 capability、image output、位置错误的 tool kind、预先解析的 thinking、多模态输入和历史消息;
340
+ - 每个 `PreparedAttempt` 只能原子执行一次;外部取消只结算取消前已经完整返回的 `ModelResponse` usage,随后仍重抛 `CancelledError`;
341
+ - `usage_source` 由 composition root 必填;generic adapter 不把响应 `model_name` 用作请求身份或去重,也不持久化该字段,provider response ID 按 provider 命名空间幂等去重且在证据冲突时失败;显式 response provider 矛盾会在 Agent 看见响应、执行工具之前失败;
342
+ - 流式工具信号只暴露工具名与调用 ID 的稳定 SHA-256 脱敏键,以及参数/结果摘要,不透传模型或 provider 可控标识原文;
343
+ - Probe 用独立 text-only run 证明 TEXT,并用受显式 request/tool limits 约束的 capability run 验证 tools/structured output;流式安全信号、逐响应 usage 与错误分类均可用 `TestModel` / `FunctionModel` 无网络验证。
344
+
345
+ 离线门禁包含实际 Pydantic AI V1 structured-output + function-tool Agent loop 的硬编码 golden,分别固定首请求、工具和输出摘要;测试不会调用待测摘要器生成自己的期望值,这仍不是实时 provider 证据。
346
+
347
+ 该 generic 路径刻意不从顶层导出,当前只在锁定的 `pydantic-ai-slim==2.35.3` 上验证。Execution plan 必须设置 `agent.instrument = False`;仅关闭内容字段仍不能证明异常事件已脱敏。它自身不创建 provider client、不加载密钥、不自动 retry/fallback,也不承诺多模态、历史消息或 native/deferred tools;离线测试和单次 synthetic Probe 更不能证明任何真实供应商持续可用。v0.2 的 DeepSeek-specific 证据位于下面的独立边界,不能反向扩大 generic adapter 的结论。具体装配和限制见 ADR-0003。
348
+
349
+ ## v0.2 DeepSeek 证据里程碑
350
+
351
+ 当前实现提供一个窄范围、显式启用的真实供应商证据路径:
352
+
353
+ - provider-specific experimental composition root 显式加载 `DEEPSEEK_API_KEY`,创建
354
+ `AsyncOpenAI(max_retries=0)`,再装配 DeepSeek provider 与
355
+ `OpenAIResponsesModel("deepseek-v4-flash")`;
356
+ - model doctor 成功路径固定执行两个完整 Agent run:最小 structured non-stream 与 text
357
+ stream;二者互不推定,也不自动 retry、fallback 或增加第三条 run;
358
+ - doctor stdout 只输出一个脱敏 JSON 结果,stderr 只输出逐行 JSON 运维日志;API key、完整
359
+ prompt、模型 output、HTTP body/headers 和 provider 原始异常均禁止进入两个通道;
360
+ - stderr 日志是可丢失、可重复的非权威运维投影,不是 `RunJournal`,也不能用于恢复 doctor
361
+ 结果;
362
+ - `agentRunBudget=2` 与实际 provider request count 分开记录,不能从 run 数推定网络请求数;
363
+ - live doctor 只允许显式人工启用,默认 pytest、CI、构建和分发验证保持零真实模型请求。
364
+
365
+ 安装 OpenAI-compatible extra 后,可从进程环境读取密钥,或显式指定当前目录中的专用文件:
366
+
367
+ ```bash
368
+ uv run --extra openai-compatible fairlead-doctor deepseek --env-file .env.local
369
+ uv run --extra anthropic fairlead-doctor deepseek-anthropic --env-file .env.local
370
+ ```
371
+
372
+ 命令不接受 `--api-key`,避免密钥进入 shell history 或进程参数。stdout 恰好输出一个
373
+ `fairlead.deepseek-doctor.v1` JSON 结果;stderr 每行是结构化运维日志。成功退出码为 `0`,
374
+ provider/capability run 检查失败为 `1`,operation 级配置、装配、资源清理或内部失败为 `2`;
375
+ 非法参数同样返回 `2`,但不会伪造 doctor report。SDK transport retry 与每条 Agent run 的自动
376
+ 输出重试均关闭;成功路径两条 run 的 provider request limit 各为 `1`。
377
+
378
+ 该证据只回答锁定版本下 DeepSeek Responses + `deepseek-v4-flash` 的两条 synthetic 路径能否在
379
+ 指定时点工作,不覆盖业务 Prompt/Plan、完整 Fairlead Runtime/Coordinator、耐久 Journal、恢复、
380
+ 其他 provider 或持续健康。范围与决策分别见
381
+ [v0.2 范围](https://github.com/gugia/fairlead/blob/v0.10.0/docs/product/v0.2-scope.md) 和
382
+ [ADR-0004](https://github.com/gugia/fairlead/blob/v0.10.0/docs/architecture/0004-provider-composition-and-model-doctor.md)。首次显式 live smoke 的
383
+ 脱敏结果见 [2026-08-27 DeepSeek doctor 证据](https://github.com/gugia/fairlead/blob/v0.10.0/docs/evidence/2026-08-27-deepseek-doctor.json);它是
384
+ 时点证据,不是持续健康声明。
385
+
386
+ ## GLM/ZAI 国内同步资格
387
+
388
+ `fairlead.experimental.providers.zai` 使用原生 `ZaiModel` / `ZaiProvider`,但由 composition root
389
+ 显式注入国内 `https://open.bigmodel.cn/api/paas/v4`、`max_retries=0` 和固定
390
+ `glm-5.3-flash` target;provider 身份保持 `zai`,不会因 wire 兼容而被改写为 OpenAI。当前同步
391
+ Agent 路径固定 low reasoning、1024 output tokens 与 `tool_choice=auto`;智谱异步任务接口不属于
392
+ 这个 Model Adapter。
393
+
394
+ 安装 ZAI extra 后可显式运行两 Run doctor:
395
+
396
+ ```bash
397
+ uv run --extra zai fairlead-doctor glm --env-file .env.local
398
+ ```
399
+
400
+ stdout 为 `fairlead.glm-doctor.v1` 脱敏报告,stderr 仍使用与 DeepSeek doctor 相同的 allowlist
401
+ 结构日志;structured non-stream 与 text stream 各有独立 request limit 1。配置、profile、异步边界
402
+ 与实网证据见 [ADR-0011](https://github.com/gugia/fairlead/blob/v0.10.0/docs/architecture/0011-glm-zai-provider.md)。
403
+
404
+ ## DeepSeek Anthropic-compatible 离线资格
405
+
406
+ `fairlead.experimental.providers.deepseek_anthropic` 通过可选 Anthropic SDK 提供第二套协议
407
+ composition。它固定 DeepSeek 官方 Anthropic-compatible base URL、`deepseek-v4-flash`、
408
+ `max_retries=0` 和保守 profile,并在 HTTP transport 前拒绝官方说明会被忽略或尚未支持的字段。
409
+ 默认测试实际经过 `Agent -> AnthropicModel -> AsyncAnthropic -> MockTransport`,分别核对文本与
410
+ 工具式结构化输出的最终 wire、usage 和资源关闭,而不是只比较手写 JSON。
411
+
412
+ 两条协议的 `ModelTarget.provider` 都是 `deepseek`;协议由各自 target revision 和 composition
413
+ 边界区分。因此这项证据只能说明同一供应商可经过两套 SDK/profile 接线,不能称为第二供应商已
414
+ 验证。独立的 reference 资格测试已经让 provider 身份分别为 `openai` 与 `anthropic` 的真实
415
+ Pydantic AI 实现完成同一应用全链,证明业务服务无需按 provider 分支;真正的多供应商实网与运营
416
+ 资格仍需要相应账户、官方端点和用户授权的 live 凭据证据。详细限制见
417
+ [ADR-0008](https://github.com/gugia/fairlead/blob/v0.10.0/docs/architecture/0008-deepseek-anthropic-compatible.md)。
418
+
419
+ ## 本地开发
420
+
421
+ 需要 uv `>=0.10.11,<0.13`,本地推荐 Python 3.14;当前发布门禁验证 Python 3.12–3.14,包元数据仍允许后续 Python 版本安装验证。
422
+
423
+ ```bash
424
+ uv sync --locked --all-groups
425
+ uv run ruff format --check .
426
+ uv run ruff check .
427
+ uv run mypy
428
+ uv run python scripts/export_contract_schemas.py --check
429
+ uv run pytest
430
+ uv build --no-sources
431
+ uv run python scripts/verify_distribution.py
432
+ ```
433
+
434
+ 需要更新契约快照时,先修改模型和文档,再显式生成:
435
+
436
+ ```bash
437
+ uv run python scripts/export_contract_schemas.py
438
+ ```
439
+
440
+ ## 在其他项目中复用
441
+
442
+ `v0.10.0` 在 PyPI 发布并完成 hash/安装核验后,应用只安装核心包:
443
+
444
+ ```bash
445
+ uv add "fairlead==0.10.0"
446
+ # 或
447
+ python -m pip install "fairlead==0.10.0"
448
+ ```
449
+
450
+ 首发完成前,或要验证未发布 commit 时,使用本地可编辑依赖:
451
+
452
+ ```bash
453
+ uv add --editable /path/to/fairlead
454
+ ```
455
+
456
+ 两个 reference package 不从公共 PyPI 安装;需要参考宿主时,从同一 `v0.10.0` 仓库 tag 取得源码模板,
457
+ 或使用经批准的 GitHub/private artifact,并按采用项目自己的部署和权限边界接线。
458
+
459
+ 未来的复用分四层:
460
+
461
+ 1. `fairlead` 库提供稳定契约、端口和编排不变量。
462
+ 2. 可选 provider/Harness extras 提供能力,不污染最小安装。
463
+ 3. reference host 展示 PostgreSQL-first、Redis-optional 的 Operation/HTTP/SSE 接入方式;在第二个
464
+ 真实业务采用者验证前,它仍是参考实现而不是新增稳定核心端口。
465
+ 4. 一次性的 `fairlead init` 脚手架将在契约稳定后生成应用骨架;生成后的业务代码归应用所有,不要求运行时继承模板。
466
+
467
+ 当前可以用 `fairlead.adapters.InMemoryRunJournal` 和 `fairlead.testing.ScriptedModelRuntime` 做无网络接线测试。`fairlead.experimental` 的导入路径刻意表明其输出恢复、多 Attempt 与生产并发语义尚未冻结,不应封装成业务项目的长期稳定接口。
468
+
469
+ 需要导出审计证据时,应用可调用 `read_run_audit_bundle`;若应用已在自己的数据库事务中取得同一 revision 的完整数据,也可直接调用 `build_run_audit_bundle`。需要 API、报表或预算视图时,对该当前记录调用 `project_run_metering`。浏览器业务请求、授权原文流、业务输出持久化和最终用户文案仍由各应用定义,前端不应直接构造 `RunSpec`。v0.7 reference host 的 Operation 是应用级聚合:它可以链接多个 Run,但不能替代 `RunJournal` 或把 Notification/Outbox 当作运行真值。
470
+
471
+ 幂等命中不会盲目重放 provider。`ExecutionOutcome.recovery_required` 用于区分已经可靠结束的回放与仍为 queued/running 的记录:若最后一个 Attempt 已有受校验的终态,协调器只补齐对应 Run 终态;竞争协调器已经提交完全等价终态时也接受该权威结果。若 Attempt 仍为 running,或尚无足够事实判断 provider 是否执行,则返回 `recovery_required=true`,交给应用或运维流程协调。协调器会在已知成功或已知失败的持久化窗口遭遇首次取消时尽力保留原结果,并通过 `lastEventAt` 保证全部权威事件时间非递减;普通清理故障不会替代原始 `CancelledError`,但重复取消和进程终止仍可能中断清理。本版没有 lease、超时接管或自动恢复,因此不能从内存测试推导出进程崩溃恢复能力。
472
+
473
+ 几个跨语言边界需要保持精确:usage 的去重身份是 `(attempt_id, response_key)`,`response_key` 只需在所属 Attempt 内稳定;`ArtifactRef.uri` 必须是无 userinfo、query、fragment 和空白的带 scheme 稳定 URI,并拒绝 `data`、`javascript`、`vbscript`,短期签名 URL 留在存储 Adapter 内;成本金额以非指数、无多余尾零的十进制定点字符串序列化。稳定 Journal 异常从 `fairlead.errors` 导入,例如 `from fairlead.errors import RevisionConflictError`。
474
+
475
+ ## 目录
476
+
477
+ ```text
478
+ src/fairlead/contracts/ # 不可变、可 JSON 往返的公共契约
479
+ src/fairlead/audit.py # 完整 Run 历史的构造与一致性验证
480
+ src/fairlead/journal.py # 有界高水位分页与一致审计读取
481
+ src/fairlead/metering.py # Run/Attempt usage 与成本覆盖纯投影
482
+ src/fairlead/ports.py # 持久化、模型运行与通知的最小端口
483
+ src/fairlead/adapters/ # 当前仅含单进程内存参考实现
484
+ src/fairlead/experimental/ # 协调器、Pydantic AI adapter、provider composition 与 doctor CLI
485
+ src/fairlead/testing/ # scripted fake、记录器与 Journal 资格套件
486
+ src/fairlead/schemas/v1/ # 随 Python 分发物交付的版本化 JSON Schema
487
+ docs/ # 范围、架构决策、提示契约和质量门禁
488
+ tests/contracts/ # 不变量、失败模式和序列化测试
489
+ tests/architecture/ # 依赖边界、公共 API 和 Schema 漂移测试
490
+ ```