Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Methods:

The `client.v2` sub-client targets LandingAI's next-generation ADE gateway, which lives on its own host (`api.ade.[env].landing.ai`) rather than the V1 host (`api.va.[env].landing.ai`). It is **additive**: `client.v2.*` is a separate surface from the top-level `client.*` (V1) methods documented above, and using it does not change any V1 behavior. See the [README](README.md#environments) for environment selection and usage examples.

`client.v2.parse_jobs` and `client.v2.extract_jobs` both return a single, unified <a href="./src/landingai_ade/types/v2/job.py">`Job`</a> shape, even though the underlying parse/extract job envelopes differ upstream -- `Job.raw` retains the full original envelope as an escape hatch for any field not surfaced on the typed model.
`client.v2.parse_jobs`, `client.v2.extract_jobs`, and `client.v2.ground_jobs` all return a single, unified <a href="./src/landingai_ade/types/v2/job.py">`Job`</a> shape, even though the underlying parse/extract/ground job envelopes differ upstream -- `Job.raw` retains the full original envelope as an escape hatch for any field not surfaced on the typed model.

Types:

Expand All @@ -77,6 +77,9 @@ from landingai_ade.types.v2 import (
V2ExtractMetadata,
V2ExtractResult,
V2FileUploadResponse,
V2GroundBilling,
V2GroundMetadata,
V2GroundResult,
V2ParseBilling,
V2ParseBox,
V2ParseElement,
Expand All @@ -95,8 +98,9 @@ from landingai_ade.types.v2 import (

- <code><a href="./src/landingai_ade/types/v2/job.py">Job</a></code> -- unified job shape: `job_id`, `status` (<code><a href="./src/landingai_ade/types/v2/job.py">JobStatus</a></code>: `pending` / `processing` / `completed` / `failed` / `cancelled`), `created_at`, `completed_at`, `progress`, `result` (a `V2ParseResponse` for parse jobs, a `V2ExtractResult` for extract jobs, or `None` until completion), `error` (<code><a href="./src/landingai_ade/types/v2/job.py">JobError</a></code>), `raw` (the full original envelope as a `dict`), and the `.is_terminal` property.
- <code><a href="./src/landingai_ade/types/v2/parse_response.py">V2ParseResponse</a></code> -- `markdown`, `structure`, `grounding`, `metadata` (<code><a href="./src/landingai_ade/types/v2/parse_response.py">V2ParseMetadata</a></code>, which nests <code><a href="./src/landingai_ade/types/v2/parse_response.py">V2ParseBilling</a></code> and carries `output_markdown_chars`, `range_units`, and `openapi_spec`). `structure` is a typed <code><a href="./src/landingai_ade/types/v2/parse_response.py">V2ParseStructure</a></code> tree (`document` → <code><a href="./src/landingai_ade/types/v2/parse_response.py">V2ParsePage</a></code> → <code><a href="./src/landingai_ade/types/v2/parse_response.py">V2ParseElement</a></code>); each node below the root carries its spatial data inline in a <code><a href="./src/landingai_ade/types/v2/parse_response.py">V2ParseNodeGrounding</a></code> (`page`, <code><a href="./src/landingai_ade/types/v2/parse_response.py">V2ParseRange</a></code>, <code><a href="./src/landingai_ade/types/v2/parse_response.py">V2ParseBox</a></code>, normalized page coordinates), and leaf elements additionally carry an `atomic_grounding` list. With `options.inline_markdown`, each node also carries its `markdown` slice. The legacy top-level `grounding` tree (<code><a href="./src/landingai_ade/types/v2/parse_response.py">V2ParseGrounding</a></code> → `V2ParseGroundingPage` → `V2ParseGroundingElement` → `V2ParseGroundingEntry`) is retained for older gateway responses. Element `type`/page `status` are permissive strings and unknown keys are retained.
- <code><a href="./src/landingai_ade/types/v2/extract_response.py">V2ExtractResult</a></code> -- `extraction`, `extraction_metadata`, `markdown`, `output_ref`, `metadata` (<code><a href="./src/landingai_ade/types/v2/extract_response.py">V2ExtractMetadata</a></code>, which carries `model_version`, `range_units`, `openapi_spec`, and nests <code><a href="./src/landingai_ade/types/v2/extract_response.py">V2ExtractBilling</a></code> with `input_markdown_chars` / `output_extraction_chars`).
- <code><a href="./src/landingai_ade/types/v2/extract_response.py">V2ExtractResult</a></code> -- `extraction`, `extraction_metadata`, `markdown`, `output_ref`, `schema_violation_error` (set when `strict=False` and the schema had unextractable fields), `warnings`, and `metadata` (<code><a href="./src/landingai_ade/types/v2/extract_response.py">V2ExtractMetadata</a></code>, which carries `model_version`, `input_markdown_chars`, `output_extraction_chars`, `range_units`, `openapi_spec`, and nests <code><a href="./src/landingai_ade/types/v2/extract_response.py">V2ExtractBilling</a></code>).
- <code><a href="./src/landingai_ade/types/v2/file_upload_response.py">V2FileUploadResponse</a></code> -- `file_ref`.
- <code><a href="./src/landingai_ade/types/v2/ground_response.py">V2GroundResult</a></code> -- `grounding` (a tree mirroring the input `extraction_metadata`, each `{value, ranges}` leaf replaced by the list of `structure` blocks its ranges overlap) and `metadata` (<code><a href="./src/landingai_ade/types/v2/ground_response.py">V2GroundMetadata</a></code>: `job_id`, `duration_ms`, `openapi_spec`, and nested <code><a href="./src/landingai_ade/types/v2/ground_response.py">V2GroundBilling</a></code>).

Methods:

Expand All @@ -115,18 +119,29 @@ Methods:

Synchronous extract. `schema` accepts a pydantic `BaseModel` subclass, a `dict`, or a JSON-encoded string -- all are coerced to a JSON Schema object. Provide exactly one of `markdown` or `markdown_url`. `strict=True` rejects schemas with unsupported fields (HTTP 422) instead of silently pruning them. Raises `V2SyncTimeoutError` on a 504; use `extract_jobs` for long-running documents.

- <code title="post /v2/extract/jobs">client.v2.extract_jobs.<a href="./src/landingai_ade/resources/v2/extract.py">create</a>(\*, schema, markdown=..., markdown_url=..., model=..., strict=..., service_tier=...) -> <a href="./src/landingai_ade/types/v2/job.py">Job</a></code>
- <code title="post /v2/extract/jobs">client.v2.extract_jobs.<a href="./src/landingai_ade/resources/v2/extract.py">create</a>(\*, schema, markdown=..., markdown_url=..., model=..., strict=..., output_save_url=..., service_tier=...) -> <a href="./src/landingai_ade/types/v2/job.py">Job</a></code>
- <code title="get /v2/extract/jobs/{job_id}">client.v2.extract_jobs.<a href="./src/landingai_ade/resources/v2/extract.py">get</a>(job_id) -> <a href="./src/landingai_ade/types/v2/job.py">Job</a></code>
- <code title="get /v2/extract/jobs">client.v2.extract_jobs.<a href="./src/landingai_ade/resources/v2/extract.py">list</a>(\*, page=..., page_size=..., status=...) -> JobList[<a href="./src/landingai_ade/types/v2/job.py">Job</a>]</code>
- <code>client.v2.extract_jobs.<a href="./src/landingai_ade/resources/v2/extract.py">wait</a>(job_id, \*, timeout=600, poll_interval=None, raise_on_failure=False) -> <a href="./src/landingai_ade/types/v2/job.py">Job</a></code>

Same polling/timeout semantics as `parse_jobs.wait`. Extract jobs have no `cancelled` status, so `raise_on_failure` only ever triggers on `failed`.

- <code title="post /v2/ground">client.v2.<a href="./src/landingai_ade/resources/v2/v2.py">ground</a>(\*, extraction_metadata, structure) -> <a href="./src/landingai_ade/types/v2/ground_response.py">V2GroundResult</a></code>

Synchronous ground. Maps each extracted field back to the `structure` blocks it was quoted from by overlapping `extraction_metadata` ranges against every block's inline `grounding.range`. Both `extraction_metadata` (e.g. `client.v2.extract(...).extraction_metadata`) and `structure` (e.g. `client.v2.parse(...).structure`) accept a `dict` or a pydantic model. Block ids resolve only against the `structure` supplied here, so pass the parse result the extraction actually came from. Raises `V2SyncTimeoutError` on a 504; use `ground_jobs` for long-running inputs.

- <code title="post /v2/ground/jobs">client.v2.ground_jobs.<a href="./src/landingai_ade/resources/v2/ground.py">create</a>(\*, extraction_metadata, structure, output_save_url=...) -> <a href="./src/landingai_ade/types/v2/job.py">Job</a></code>
- <code title="get /v2/ground/jobs/{job_id}">client.v2.ground_jobs.<a href="./src/landingai_ade/resources/v2/ground.py">get</a>(job_id) -> <a href="./src/landingai_ade/types/v2/job.py">Job</a></code>
- <code title="get /v2/ground/jobs">client.v2.ground_jobs.<a href="./src/landingai_ade/resources/v2/ground.py">list</a>(\*, page=..., page_size=..., status=...) -> JobList[<a href="./src/landingai_ade/types/v2/job.py">Job</a>]</code>
- <code>client.v2.ground_jobs.<a href="./src/landingai_ade/resources/v2/ground.py">wait</a>(job_id, \*, timeout=600, poll_interval=None, raise_on_failure=False) -> <a href="./src/landingai_ade/types/v2/job.py">Job</a></code>

Same polling/timeout semantics as `parse_jobs.wait`. Ground jobs have no `cancelled` status, so `raise_on_failure` only ever triggers on `failed`. `output_save_url` (create only) delivers the result out-of-band and reports `output_url` on the completed job instead of an inline `result`.

- <code title="post /v1/files">client.v2.files.<a href="./src/landingai_ade/resources/v2/files.py">upload</a>(\*, file) -> str</code>

Stages a file's bytes on the ADE data plane and returns a `file_ref` string for use in job inputs. Served on the ADE host under `/v1/files` (not `/v2/...`). Raises `LandingAiadeError` if the response has no `file_ref`.

Notes:

- `parse_jobs.list` / `extract_jobs.list` both return a `JobList` (a `list[Job]` subclass) carrying pagination metadata: `.has_more`, `.org_id`, `.page`, `.page_size`.
- `parse_jobs.list` / `extract_jobs.list` / `ground_jobs.list` all return a `JobList` (a `list[Job]` subclass) carrying pagination metadata: `.has_more`, `.org_id`, `.page`, `.page_size`.
- All `client.v2.*` methods accept the usual `extra_headers`, `extra_query`, `extra_body`, and `timeout` overrides; sync methods additionally accept `save_to` (parse/extract only, not the job-creation methods) to write the response to disk, mirroring V1's `save_to`.
43 changes: 35 additions & 8 deletions docs/v2-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ what to check when the upstream spec (`specs/v2-aide.json`) changes.

| Layer | Location | What it covers |
| --- | --- | --- |
| Response models | `tests/test_v2_types.py` | Deserialization of `V2ParseResponse` / `V2ExtractResult` and their nested models from plain dicts, including unknown-key tolerance. |
| Job normalization | `tests/test_v2_normalize.py` | `normalize_parse_job` / `normalize_extract_job`: envelope → unified `Job` (status, timestamps, `result`, `error`). |
| Response models | `tests/test_v2_types.py` | Deserialization of `V2ParseResponse` / `V2ExtractResult` / `V2GroundResult` and their nested models from plain dicts, including unknown-key tolerance. |
| Job normalization | `tests/test_v2_normalize.py` | `normalize_parse_job` / `normalize_extract_job` / `normalize_ground_job`: envelope → unified `Job` (status, timestamps, `result`, `error`). |
| Resource wiring | `tests/api_resources/v2/` | `respx`-mocked HTTP: host routing, multipart/JSON bodies, options serialization, job polling. No network. |
| Live smoke | `tests/contract/test_v2_smoke.py` | End-to-end calls against staging (marked `contract`; skipped unless `LANDINGAI_ADE_STAGING_APIKEY` is set). |

Expand Down Expand Up @@ -54,15 +54,42 @@ responses omit it in favor of the inline `grounding` above.

`POST /v2/extract` (and the completed `extract_jobs` result) returns a
`V2ExtractResult` with `extraction`, `extraction_metadata`, `markdown`,
`output_ref` (set when the output was delivered out-of-band), and `metadata`
(`V2ExtractMetadata`): `job_id`, `model_version`, `duration_ms`, `doc_id`,
`credit_usage`, `range_units`, `openapi_spec`, and `billing` (`V2ExtractBilling`
with `input_markdown_chars` / `output_extraction_chars`).
`output_ref` (deprecated; renamed to `schema_violation_error` upstream),
`schema_violation_error` (set when `options.strict` is false and the schema had
fields the model could not extract — the extraction is partial), `warnings`
(non-fatal warnings), and `metadata` (`V2ExtractMetadata`): `job_id`,
`model_version`, `duration_ms`, `doc_id`, `input_markdown_chars`,
`output_extraction_chars`, `credit_usage` (deprecated), `range_units`,
`openapi_spec`, and `billing` (`V2ExtractBilling`). The `input_markdown_chars` /
`output_extraction_chars` char counts moved from `billing` onto `metadata`
upstream; both are retained on `V2ExtractBilling` for backward compatibility.

The async `extract_jobs.create` also accepts `output_save_url` (async jobs only):
when set, the finished result is delivered to that URL and the completed job
reports `output_url` (on `Job.raw`) instead of an inline `result`.

## Current ground-response shape

`POST /v2/ground` (and the completed `ground_jobs` result) returns a
`V2GroundResult` — a pure, stateless join that maps each extracted field back to
the `structure` blocks it was quoted from:

- `grounding` — a tree mirroring the input `extraction_metadata`: nested objects
and arrays keep their shape, and each `{value, ranges}` leaf is replaced by the
list of `structure` blocks its ranges overlap (block ids resolve only against
the `structure` supplied in the request).
- `metadata` (`V2GroundMetadata`) — `job_id`, `duration_ms`, `openapi_spec`, and
`billing` (`V2GroundBilling`).

`client.v2.ground(...)` takes `extraction_metadata` and `structure`, each of which
accepts a plain `dict` or a pydantic model (so a parse response's `.structure` can
be passed directly). `ground_jobs.create` additionally accepts `output_save_url`.

## Async job envelopes

`normalize_parse_job` and `normalize_extract_job` fold the upstream job envelopes
into the unified `Job`. Both are tolerant of field-name drift:
`normalize_parse_job`, `normalize_extract_job`, and `normalize_ground_job` fold
the upstream job envelopes into the unified `Job`. All are tolerant of field-name
drift:

- The parse response lives under `result` (older envelopes used `data`).
- Failures arrive as a structured `error` object (`{code, message}`); older parse
Expand Down
Loading
Loading