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

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions