【免费下载链接】ade-python
Python library for Agentic Document Extraction (ADE).
ade-python 是 LandingAI 的Agentic Document Extraction(ADE)Python SDK,用于文档解析、结构化抽取与 grounding 对齐。它的测试体系由三层组成:respx 离线 Mock 测试、contract 契约测试和surface-lock 发布门禁,共同保证 SDK 与上游网关的契约始终一致。本文带你完整走一遍这三层测试的设计思路与运行方式。🧪
测试体系全景:四层金字塔
ade-python 的测试不是"一套用例跑到底",而是按可信度分层,各层职责清晰:
| 层级 | 位置 | 覆盖内容 | 是否需要凭证 |
|---|---|---|---|
| 响应模型 | tests/test_v2_types.py | V2ParseResponse/V2ExtractResult等模型的反序列化、未知字段容错 | ❌ |
| 任务归一化 | tests/test_v2_normalize.py | 上游任务信封 → 统一Job的转换 | ❌ |
| respx Mock 资源层 | tests/api_resources/v2/ | 主机路由、multipart/JSON 请求体、轮询逻辑 | ❌ |
| Live 契约测试 | tests/contract/test_v2_smoke.py | 对 staging 环境的端到端真实调用 | ✅ |
分层设计带来一个直接好处:rye run pytest的默认运行永远不会碰真实环境。在 pyproject.toml 中,addopts写死了--tb=short -n auto -m 'not contract and not production',契约测试和 production 测试都是显式开启的。
respx Mock 测试:不发一个网络请求的 HTTP 断言
respx 是 httpx 的 Mock 库。ade-python 在 tests/api_resources/v2/ 下大量使用@respx.mock装饰器,把 SDK 的每次请求都拦截在本地,实现对请求内容(而不只是响应)的精确断言。
以 parse 接口为例,test_parse.py 展示了典型的断言方式:
@respx.mock def test_parse_sync_ok_routes_to_v2_and_sends_options_json() -> None: client = LandingAIADE(apikey=APIKEY, environment="production") route = respx.post("https://api.ade.landing.ai/v2/parse").mock( return_value=httpx.Response(200, json=PARSE_BODY) ) result = client.v2.parse(document=b"pdf", model="dpt-3-latest", options={"foo": "bar"}) # options 必须以 JSON 编码的字符串表单字段发送 sent = route.calls.last.request.content assert b'{"foo": "bar"}' in sent这里的关键技巧是检查发出去的字节流:
- 路由是否正确:断言请求打到了
https://api.ade.landing.ai/v2/parse而不是 V1 主机; - multipart 字段是否干净:未设置的
Omit/NotGiven哨兵值绝不能以"<...Omit object...>"或"NOT_GIVEN"字面量泄漏进请求体; - 敏感信息如何折叠:
password=参数只能落入options.password,test_parse_sync_folds_password_into_options 专门钉死这一行为,因为第二份副本会让密钥双重暴露。
@respx.mock还带来一个隐性收益:它是密闭(hermetic)的——没有注册路由的请求会直接失败,所以"URL 写错"这类回归无法溜过 CI。
一个实现细节:respx 不支持自定义 transport,因此 conftest.py 会自动跳过"aiohttp 客户端 + respx_mock"组合的测试用例,避免这类用例永远报错。
contract 契约测试:对 staging 的真实环境断言
Mock 测试验证的是"SDK 认为正确的行为",而 contract 测试验证的是"staging 上真实发生的行为"。它位于 tests/contract/,全部用例打上pytest.mark.contract标记,只有导出LANDINGAI_ADE_STAGING_APIKEY才会运行:
LANDINGAI_ADE_STAGING_APIKEY=... rye run pytest tests/contract/test_v2_smoke.py -m contractcontract/test_v2_smoke.py 里有几处很值得新手学习的设计:
- 快速失败优先:SDK 默认 8 分钟超时 + 2 次重试,这对解析 500 页扫描件是合理的,但对"合并门禁"是灾难——一个卡住的端点会烧掉约 24 分钟。所以契约测试把单次请求压到
CONTRACT_TIMEOUT = 45.0秒且max_retries=0,让"staging 没响应"直接以APITimeoutError的醒目形式暴露,而不是伪装成"套件很慢"。 - 断言行为而非状态码:例如
grounding=False的抽取测试不断言 2xx,而是断言返回的ranges全部为null——因为"参数没到线上"在 staging 上不会报错,只会静默地照常 grounding。 - 用真实 PDF 跑 parse:套件自带 tests/contract/sample.pdf 作为两页样本文件,保证 parse 路径走的是真实的 PDF 解码。
V1 侧的契约测试在 tests/contract/test_v1_smoke.py 与 test_v1_production.py,后者打production标记、消耗真实生产环境额度,只由专门的 CI 作业触发。
surface-lock 门禁:锁定已发布的公共 API
ade-python 的模型代码由 OpenAPI 规范自动生成,AI 可以参与"接线",但向后兼容的保证由 CI 强制执行——这就是 surface-lock 门禁的意义。
scripts/spec-sync/surface-lock.sh 的工作流程:
- 确定基线:用
git describe --tags找到最后一个 release tag。基线是"已发布的表面",而不是 main 上一个 commit——已合并但未发布的接口允许继续变化,只有发布出去的承诺才被锁定; - 对比检查:运行
griffe check landingai_ade --against <baseline> --search src,逐行报告破坏性变更; - 豁免加法变更:公共常量的值变更(比如
ENVIRONMENTS映射新增一项)被识别为加法配置而非签名破坏,自动放行; - 真正的破坏则失败:剩余任何一行都视为 API 破坏,脚本退出码非 0,PR 被拦截。
发布路径上还有第二道锁:scripts/spec-sync/release-gate.sh 会把本地提交的 V1 快照与生产环境的实时 OpenAPI 规范逐(path, method)对比——SDK 实现了但生产还没有的路由会挡住发布,防止 staging-only 功能提前上车。
围绕规范同步的完整工具链都在 scripts/spec-sync/ 下:
| 脚本 | 作用 |
|---|---|
| check-drift.sh | 拉取线上规范并与本地快照 diff;exit 10 表示有漂移 |
| fetch-normalize.sh | 拉取并规范化 OpenAPI 规范 |
| gen-models.sh | 用 datamodel-codegen 从快照生成参考模型到 specs/_generated/ |
| surface-lock.sh | 已发布 API 的破坏性变更门禁 |
| release-gate.sh | "已实现路由必须存在于生产"门禁 |
| notify-new-drift.sh | 检测到新漂移时向同步 PR 发通知(含去重) |
本地运行测试:速查清单
🚀 三条命令覆盖日常所有场景:
# 1. 离线套件(模型 + 归一化 + respx Mock),无需任何凭证 rye run pytest tests/test_v2_types.py tests/test_v2_normalize.py tests/api_resources/v2 # 2. 契约测试(需要 staging 密钥) LANDINGAI_ADE_STAGING_APIKEY=... rye run pytest tests/contract/test_v2_smoke.py -m contract # 3. pydantic v1 兼容会话 rye run nox -s test-pydantic-v1其中 nox 会话定义在 noxfile.py,会额外安装pydantic<2以验证旧版本兼容。
规范变更时的测试更新流程
当上游规范(specs/v2-aide.json)发生变化,docs/v2-testing.md 给出固定四步:
- 阅读机械 diff(
specs/v2-aide.json与 specs/_generated/v2_models.py); - 加法型请求字段→ 变成
run/create方法上新的可选关键字参数(仅限requestBody顶层属性); - 加法型响应字段→ 加入 src/landingai_ade/types/v2/ 对应模型,被删除/改名的字段保留为可选以保证向后兼容;
- 在 tests/api_resources/v2/ 补 respx 测试,在 tests/contract/test_v2_smoke.py 补一条 live 断言,最后更新 api.md 与测试指南。
小结:为什么这套体系对新手友好
- 默认离线:平时写代码只需
rye run pytest,秒级反馈、零凭证、零外部依赖; - 分层可信:Mock 层钉死"SDK 的行为",契约层验证"环境的真实行为",两层各有分工、互不替代;
- 门禁代替自觉:surface-lock 与 release-gate 把"不许破坏已发布 API"从口头约定变成 CI 里的一条退出码,让自动化改动(包括 AI 参与的接线)也能安全合并。
理解这三层之后,你再去看 CONTRIBUTING.md 中的贡献流程,整个 spec-sync 流水线就会顺理成章了。🔒
【免费下载链接】ade-python
Python library for Agentic Document Extraction (ADE).
相关推荐
微服务契约测试新范式:SpringCloud Contract实战指南
微服务契约测试新范式:SpringCloud Contract实战指南 你是否还在为微服务接口联调焦头烂额?团队协作时服务提供者与消费者频繁扯皮?本文将带你使用
六足机器人技术架构深度解析:从18自由度到智能步态控制的创新实践
六足机器人技术架构深度解析:从18自由度到智能步态控制的创新实践 在机器人技术日新月异的今天,六足机器人以其独特的运动稳定性和地形适应性成为研究热点。hexap
机器人嵌入式硬件开发智能硬件如何使用laravel-adjacency-list:5分钟快速实现树形数据结构
如何使用laravel adjacency list:5分钟快速实现树形数据结构 laravel adjacency list是一个强大的Laravel扩展包,
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考