Skip to content

V2 parse & extract SDK support (implementation plan) #95

Description

@yzld2002

Full code-level plan: docs/superpowers/plans/2026-07-07-v2-parse-extract-ade-python.md (13 TDD tasks with complete code + test cycles). This issue is the summary — GitHub's 64 KB body limit won't hold the full plan.

Scope: ade-python only (TS is a separate port). Additive client.v2 sub-client for V2 parse, V2 extract, their async job routes, and file staging — routed to the ADE host — with hand-written ergonomics (unified Job, wait() polling, pydantic-schema acceptance, save_to parity).


Contract verification (against the real openapi.json)

Verified all 13 routes. Several items in the design doc's table were stale/wrong; the plan encodes the corrected reality:

  1. idempotency_key is extract-only — absent from every parse route in the spec.
  2. 206 partial-success is on parse, not extract. POST /v2/parse returns 206 with metadata.failed_pages; POST /v2/extract returns only 200/422 — strict-mode failure is 422.
  3. Encrypted PDFs are supported — both parse routes accept password. (Doc item "password → 422" is obsolete.)
  4. Parse result is untyped in the spec (prose only); extract is typed (V2ExtractResult/V2ExtractMetadata). V2 parse metadata differs from V1's.
  5. Envelope divergence is worse than flagged:
parse job extract job
timestamps received_at, created_atint epoch secs created_at, completed_atISO strings
terminal payload data (inline) or output_url result
failure failure_reason (string) error {code, message}
status enum +cancelled no cancelled
list envelope {jobs, org_id, has_more} {jobs, page, page_size, has_more}

Even within extract, list uses failure_reason but get uses error{code,message}.

  1. /v1/files: single file part in, open {string:string} map out (file_ref is a key). Lives on the ADE host.
  2. /v2/workflow: full third parallel surface (sync + jobs + get + list). Out of scope here, but the resource/waiter machinery accepts it later with no rework.
  3. No cancel endpoint despite parse's cancelled status. Sync routes can 504 on timeout (extract 504 ⇒ workflow cancelled; retry w/ same idempotency_key restarts).

Design decisions

  • Unified Job shape (chosen): one normalized model for parse + extract — status (common enum), created_at/completed_at (datetime, both int-epoch and ISO normalized via the existing parse_datetime), progress (float), result (typed: V2ParseResponse | V2ExtractResult), error (JobError), plus a .raw escape hatch for envelope-specific fields (org_id, output_url, version). Stable if aide unifies the API later.
  • Routing needs no new HTTP client. The transport's _prepare_url passes absolute URLs through untouched, so v2 resources build absolute URLs against a resolved _v2_base_url and inherit auth/retries/http client. /v1/files routes to the ADE host despite its /v1 path.
  • Environment = the whole user-facing story. 4-entry matrix of (V1, V2) host pairs; LANDINGAI_ADE_ENVIRONMENT env var; explicit base_url/v2_base_url overrides; if only base_url is set, V2 follows it (one mock captures everything).
  • Generated-file edits are minimal & additive_client.py (env map, v2_base_url resolution, one v2 cached property per client, widened environment Literal) + two re-exports. All other V2 code is new files under types/v2/, resources/v2/, lib/. Holds whether or not the Stainless exit ("Problem 1") has landed.
  • Schema acceptance: v2.extract takes a pydantic model class, a dict, or a JSON string (extends lib/schema_utils.py), sent as a JSON object in the request body.

Environment matrix

environment V1 base URL V2 base URL
production (default) https://api.va.landing.ai https://api.ade.landing.ai
eu https://api.va.eu-west-1.landing.ai https://api.ade.eu-west-1.landing.ai
staging https://api.va.staging.landing.ai https://api.ade.staging.landing.ai
dev https://api.va.dev.landing.ai https://api.ade.dev.landing.ai

Task breakdown (13 TDD tasks)

Each task ends with an independently testable deliverable and a commit. Full code in the plan doc.

  1. Environment map + dual base-URL resolution_client.py (sync+async): 4-pair map, v2_base_url param, LANDINGAI_ADE_ENVIRONMENT/LANDINGAI_ADE_V2_BASE_URL, _v2_base_url attr, widened environment Literal, copy(). Tests: tests/test_v2_environment.py.
  2. V2 typestypes/v2/{job,parse_response,extract_response,file_upload_response}.py: JobStatus/JobError/Job, V2ParseResponse+metadata+billing, V2ExtractResult+V2ExtractMetadata, V2FileUploadResponse. Tests: tests/test_v2_types.py.
  3. Job normalizersresources/v2/_normalize.py: normalize_parse_job, normalize_extract_job (epoch vs ISO, data/result, failure_reason/error). Tests: tests/test_v2_normalize.py.
  4. Schema coercionlib/schema_utils.py: coerce_schema_to_dict(pydantic|dict|str) -> dict. Tests: tests/test_v2_schema.py.
  5. V2 errors + 504 wrapperlib/v2_errors.py: V2SyncTimeoutError, JobWaitTimeoutError, JobFailedError, raise_if_sync_timeout. Tests: tests/test_v2_errors.py.
  6. v2 sub-client + routing + waiterresources/v2/{_base,v2}.py: V2ResourceMixin._v2_url, poll_until_terminal/apoll_until_terminal (injected clock/sleep), V2Resource/AsyncV2Resource, client.v2 cached properties. Tests: routing via respx.
  7. client.v2.files.uploadresources/v2/files.py → returns file_ref, hits ADE host. Tests: tests/api_resources/v2/test_files.py.
  8. client.v2.parse (sync)resources/v2/parse.py: options JSON-encoded, 206 returns response with failed_pages, 504 → V2SyncTimeoutError, save_to. Tests: tests/api_resources/v2/test_parse.py.
  9. client.v2.parse_jobs — create/list/get/wait returning normalized Job; JobList carries has_more/org_id; priority, output_save_url. Tests appended.
  10. client.v2.extract (sync)resources/v2/extract.py: JSON body, schema coercion, idempotency_key, options.strict, 504 handling, save_to. Tests: tests/api_resources/v2/test_extract.py.
  11. client.v2.extract_jobs — create/list/get/wait; maps error{code,message}, page/page_size envelope. Tests appended.
  12. Async parity + full suite/lint/typecheck — async smoke tests; rye run pytest -q; rye run lint. Tests: tests/api_resources/v2/test_async_smoke.py.
  13. Docs & examplesapi.md V2 section, README V2 subsection, examples/v2_parse.py, examples/v2_extract.py.

Open items for the aide team (non-blocking)

  • Parse idempotency_key: absent from parse routes though present on extract — confirm intent.
  • Envelope unification before GA (normalizers collapse toward identity; public Job shape unaffected).
  • /v2/workflow scope confirmation.
  • Typed parse response: V2ParseResponse is permissive until the gateway publishes a typed schema.
  • 504 retries: transport retries 5xx (incl. 504) before conversion — consider suppressing retries for sync 504s in a follow-up.

🤖 Generated with Claude Code

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions