Metadata-Version: 2.5
Name: forgefile
Version: 0.1.0
Summary: Python client for the ForgeFile REST API — translate, transcribe, convert, OCR, summarize and rewrite files.
Project-URL: Homepage, https://forgefile.com
Project-URL: Documentation, https://forgefile.com/docs
Project-URL: Source, https://github.com/ForgeFile/forgefile-python
Project-URL: Issues, https://github.com/ForgeFile/forgefile-python/issues
Project-URL: Changelog, https://github.com/ForgeFile/forgefile-python/blob/main/CHANGELOG.md
Author-email: ForgeFile <support@forgefile.com>
License: MIT
License-File: LICENSE
Keywords: api-client,file-conversion,forgefile,ocr,transcription,translation
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
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 :: Implementation :: CPython
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Linguistic
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.7
Description-Content-Type: text/markdown

# forgefile-python

Python client for the [ForgeFile](https://forgefile.com) REST API — translate, transcribe,
convert, OCR, summarize and rewrite files.

Requires Python 3.11+.

## Install

```bash
pip install forgefile
```

## Quick start

```python
from forgefile import ForgeFile

with ForgeFile("your-token") as api:
    job = api.jobs.translate("contract.pdf", target_language="es")
    api.files.wait(job.uuid)
    api.files.download(job.uuid, "contract.es.pdf")
```

The token is read from `FORGEFILE_API_KEY` when the first argument is omitted.
Public endpoints need no token at all:

```python
with ForgeFile() as api:
    for language in api.public.languages():
        print(language.code, language.name)
```

## Processing a file

Every job endpoint uploads the file and starts the work in one call, returning a `FileJob`
whose `uuid` identifies it from then on.

```python
api.jobs.translate("contract.pdf", target_language="de")  # source_language is optional
api.jobs.transcribe("interview.mp3")
api.jobs.convert("report.docx", to_format="pdf")
api.jobs.ocr("receipt.jpg")
api.jobs.summarize("paper.pdf")
api.jobs.rewrite("draft.docx")
api.jobs.compress("scan.pdf")
```

Uploads are capped at 10 MB by the API.

### Waiting for the result

`wait()` blocks until the job reaches a terminal state and raises `JobTimeoutError` if it
does not. The job keeps running server-side, so calling again resumes waiting.

```python
finished = api.files.wait(job.uuid, timeout=900, interval=5)

if finished.succeeded:
    transcript = api.files.result(job.uuid)  # structured output
    api.files.download(job.uuid, "interview.srt")
```

`track()` yields every observed state instead, for progress reporting:

```python
for state in api.files.track(job.uuid, interval=5):
    print(state.status)
```

Branch on `job.is_finished` and `job.succeeded` rather than comparing status strings — a
status this client does not recognise is never treated as finished, so a wait loop cannot
end early on a state it has not seen.

## Errors

Every failure raises a subclass of `ForgeFileError` carrying the API's stable `error_code`,
so you can branch on the code rather than on message text.

```python
from forgefile import RateLimitError, ValidationError

try:
    api.jobs.translate("contract.pdf", target_language="es")
except ValidationError as exc:
    print(exc.context)  # field errors
except RateLimitError as exc:
    print(f"retry in {exc.retry_after}s")
```

| Exception | Raised when |
|---|---|
| `AuthenticationError` | 401 — no token, or rejected |
| `ForbiddenError` | 403 — token lacks the right |
| `NotFoundError` | 404 |
| `ValidationError` | 422 — field errors in `context` |
| `RateLimitError` | 429 — `retry_after` in seconds |
| `ServerError` | 5xx — safe to retry with backoff |
| `TransportError` | no response at all: DNS, TLS, timeout |
| `JobTimeoutError` | `wait()` gave up; the job still runs |

The API allows 60 requests per minute.

## Configuration

| Argument | Environment variable | Default |
|---|---|---|
| `api_key` | `FORGEFILE_API_KEY` | none — public endpoints only |
| `base_url` | `FORGEFILE_BASE_URL` | `https://forgefile.com/api/v1` |
| `timeout` | — | 60 seconds |

## Examples

Runnable scripts in [`examples/`](examples):

| File | Shows |
|---|---|
| `01_public_data.py` | reference data without a token |
| `02_translate_document.py` | submit, wait, download |
| `03_transcribe_with_progress.py` | streaming progress with `track()` |
| `04_convert_a_folder.py` | batching — submit all, then collect |
| `05_handling_errors.py` | every failure mode and its remedy |
| `06_custom_transport.py` | replacing the HTTP layer |

## Architecture

| Module | Responsibility |
|---|---|
| `config` | where to connect and with what headers |
| `envelope` | the API's `{success, message, data}` wrapper — the only place that knows it |
| `errors` | turning a failed response into the right exception |
| `transport` | HTTP, as a `Protocol` plus an httpx implementation |
| `resources/` | one class per endpoint group: `system`, `public`, `account`, `files`, `jobs` |

Resources depend on the `HTTPTransport` protocol, never on httpx, so the HTTP layer can be
replaced with a recorded fixture, a proxy or a different library — see
`examples/06_custom_transport.py`.

Response models accept unknown fields, which stay reachable through `model_extra`. Only
fields observed against the live API are typed explicitly; authenticated endpoints could not
be inspected without a token while this client was written, so their payloads are permissive
rather than guessed.

The package ships a `py.typed` marker, so your type checker sees these signatures.

## Development

```bash
git clone https://github.com/ForgeFile/forgefile-python
cd forgefile-python
uv sync

uv run ruff check .
uv run ruff format --check .
uv run mypy
uv run pytest
```

Tests make no network calls: the resource layer runs against a fake transport, the httpx
layer against `respx`. CI runs these commands on Python 3.11, 3.12 and 3.13.

## Links

- [ForgeFile](https://forgefile.com) — the product
- [API reference](https://forgefile.com/docs)
- [Source and issues](https://github.com/ForgeFile/forgefile-python)
- [ForgeFile on GitHub](https://github.com/ForgeFile)
- Support — <support@forgefile.com>

MIT licensed. See [LICENSE](LICENSE).
