Metadata-Version: 2.4
Name: nace-sdk
Version: 0.1.0
Summary: Python SDK for the Drex API: calibrated decisions and document jobs
Keywords: drex,nace,decision-model,document-intelligence
Author: Nace AI
Author-email: Nace AI <engineering@nace.ai>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: THIRD_PARTY_NOTICES.md
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic>=2.11.5,<3.0.0
Requires-Dist: typing-extensions>=4.13.0
Requires-Python: >=3.11
Project-URL: Homepage, https://console.nace.ai
Project-URL: Documentation, https://console.nace.ai/docs/developer-tools/python
Description-Content-Type: text/markdown

# nace-sdk (Python)

The official Python client for the [Drex API](https://console.nace.ai/docs): calibrated answers to typed questions (`POST /v1/systemone`) and document jobs — parse, split, classify, extract and ground.

The client is built on the [TypeSafe AI Python SDK](https://pypi.org/project/typesafe-sdk/) (MIT; see `THIRD_PARTY_NOTICES.md`): `system_one`, `Noul`, `Choice`, `Score`, `SystemOneResponse` with `.nouls`, `.choices` and `.scores`, `RetryPolicy` and the error classes match `typesafe-sdk`, so `TypeSafeClient` code runs on `NaceClient` after renaming the imports. It depends only on `httpx`, `pydantic` and `typing-extensions`.

```bash
pip install nace-sdk
export NACE_API_KEY=nace_sk_...
```

Python 3.11 or later. Create a key on the [API keys page](https://console.nace.ai/dashboard/api-keys).

## Decisions

```python
from nace_sdk import Choice, NaceClient, Noul, Score

with NaceClient() as client:  # reads NACE_API_KEY, NACE_BASE_URL, NACE_DEFAULT_DECISION_MODEL
    result = client.system_one(
        state="I was charged twice for my March invoice.",
        questions={
            "wants_refund": Noul(instructions="Is the customer asking for a refund?"),
            "topic": Choice(instructions="Which topic is it?", criteria={"billing": None, "shipping": None, "other": None}),
            "urgency": Score(instructions="How urgent is it?", criteria=["low", "medium", "high"]),
        },
    )

print(result.nouls["wants_refund"].noul)
print(result.choices["topic"].choice, result.request_id)
```

`AsyncNaceClient` has the same methods, awaited.

## Documents

```python
from nace_sdk import NaceClient, UrlSource

with NaceClient() as client:
    # An https:// URL whose last path segment names the file: its extension picks the parser.
    created = client.documents.parse("https://example.com/invoice.pdf", output={"formats": ["markdown"]})
    job = client.jobs.wait(created.job_id)
    print(job.result["document"]["markdown"])

    # A URL whose last segment isn't the file's name needs a file_name.
    client.documents.parse(UrlSource(url="https://arxiv.org/pdf/1706.03762", file_name="attention.pdf"))

    # on_conflict="new_version" replaces the file at that path instead of failing with path_conflict.
    file = client.documents.upload("invoice.pdf", path="invoices/invoice.pdf", on_conflict="new_version")
    extracted = client.documents.extract(
        file.as_source(),
        schema={"type": "object", "properties": {"total": {"type": "number"}}},
        wait_seconds=60,
    )

    # Or save the schema once and name it.
    saved = client.extraction_schemas.create("Invoice", {"type": "object", "properties": {"total": {"type": "number"}}})
    client.documents.extract(file.as_source(), schema_id=saved.schema_id)
```

Uploads go straight to the document service with a one-use token, never with your API key; files of 32 MiB and more go up in parts. To run a resumable upload yourself, use `create_upload_session`, `upload_part`, `complete_upload_session`, `get_upload_session` (which parts landed) and `abort_upload_session`; `create_upload_grant` mints a token for another client to upload with. Every document job and upload session create carries an `Idempotency-Key`, so retries never start a second job or session. The `/v1` routes send no `Access-Control-Allow-Origin` header, so keep Drex calls on your server: a browser uploads with a grant your server mints, and opens job files through `jobs.file_link` links.

`client.jobs` has `get`, `list`, `iter`, `wait`, `delete`, `events`, `get_request` (the request a job ran under), `rows` (a parsed sheet's rows, a page at a time), `file_link` (a signed link to a stored file) and `download` (any job file, including full Markdown that is still being prepared). `client.extraction_schemas` has `list`, `iter`, `get`, `create`, `list_versions` and `iter_versions` (oldest first), `get_version` and `create_version`; creates are not retried unless you pass `retry`, since a retry could save a second schema.

## Errors and retries

Every failure raises a `NaceError`. HTTP failures are `NaceAPIError` subclasses (`NaceBadRequestError`, `NaceAuthenticationError`, `NaceInsufficientCreditError`, `NacePermissionDeniedError`, `NaceNotFoundError`, `NaceConflictError`, `NaceUnprocessableEntityError`, `NaceRateLimitError`, `NaceInternalServerError`, `NaceOverloadedError`) with `status`, `body`, `request_id`, `type`, `code`, `issues`, `detail` and `server_retryable`; a success whose body doesn't match is a `NaceAPIResponseValidationError`, and a failed connection a `NaceAPIConnectionError` (`NaceAPITimeoutError` on a timeout). `jobs.wait` raises `NaceJobFailedError` or `NaceJobTimeoutError`. The client retries 408, 429, 5xx, dropped connections and timeouts (2 retries within a 30 s budget, 0.5 s doubling to 5 s, honoring `retry-after-ms`), except an error the server marks `"retryable": false`. The budget counts from the first attempt, so with the defaults an attempt that fails after 30 s, such as one that hit the 60 s timeout, is not retried: raise `RetryPolicy(timeout=...)` above your attempts and waits, or pass `None`. `jobs.download` retries opening each file the same way, but not a transfer cut off midway; `jobs.events` is not retried, and saved-schema creates aren't unless you pass `retry`.

```python
from nace_sdk import NaceClient, RetryPolicy

# Up to 5 attempts of up to 90 s each, and the waits between them.
client = NaceClient(timeout=90, retry=RetryPolicy(max_retries=4, timeout=500))
```

The SDK logs to the `nace_sdk` logger: `NACE_LOG_LEVEL=debug` sets its level, and a handler (for example `logging.basicConfig()`) shows the records. At debug it logs headers and bodies, with the API key, upload tokens and PDF passwords redacted.

The `nace` command line tool is `pip install nace-cli`; it reads the key `nace login` saves through `nace_sdk.credentials`. Full guide: [Python SDK](https://console.nace.ai/docs/developer-tools/python).

## License

Apache-2.0
