Metadata-Version: 2.4
Name: fathom-sdk
Version: 0.1.0
Summary: Python SDK for Fathom — typed client for the agent gateway over HTTP + SSE.
Project-URL: Homepage, https://github.com/darklake-ai/fathom-py
Project-URL: Repository, https://github.com/darklake-ai/fathom-py
Project-URL: Issues, https://github.com/darklake-ai/fathom-py/issues
Project-URL: Documentation, https://github.com/darklake-ai/fathom-py#readme
Author: darklake.ai
License: MIT
License-File: LICENSE
Keywords: agent,ai,claude,darklake,fathom,llm,ollama,openai,sse
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27.0
Provides-Extra: dev
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# fathom-sdk

Python SDK for [Fathom](https://github.com/darklake-ai/fathom) — a typed
client for the agent gateway over HTTP + SSE. Sync **and** async.

## Install

```sh
pip install fathom-sdk
# or uv pip install / poetry add / pdm add
```

## Quick start (sync)

```python
from fathom import Fathom

with Fathom(base_url="http://localhost:8790", token="...") as f:
    thread = f.threads.create()
    reply = f.threads.send(thread["id"], "What's the weather in NYC?")
    print(reply["agent_message"]["content"])
```

`base_url` and `token` can also come from `FATHOM_BASE_URL` and
`FATHOM_TOKEN` env vars. If both are set you can call `Fathom()` with
no args.

## Async

```python
import asyncio
from fathom import AsyncFathom

async def main() -> None:
    async with AsyncFathom() as f:  # picks up FATHOM_* env vars
        thread = await f.threads.create()
        async for evt in f.threads.stream(thread["id"]):
            if evt["type"] == "agent_done":
                print(evt["message"]["content"])
                break

asyncio.run(main())
```

Sync streaming works the same with a regular `for` loop:

```python
for evt in f.threads.stream(thread["id"]):
    if evt["type"] == "agent_thinking":
        print("⏳ agent is working…")
    elif evt["type"] == "agent_done":
        print("✅", evt["message"]["content"])
        break
```

## Pairing (for headless / CLI use)

If you don't have a token — say a one-shot CLI pairing against a
relay-hosted gateway — call `pair()` once:

```python
with Fathom(base_url="https://relay.darklake.ai/c/<rid>") as f:
    claim = f.pair(code="123456", device_name="ci-bot")
    authed = f.with_token(claim["token"])
    threads = authed.threads.list()
```

`pair()` is the only method that doesn't require a token; everything
else raises `PairingRequiredError`.

## Error handling

```python
from fathom import Fathom, UnauthorizedError, HttpError

with Fathom() as f:
    try:
        f.threads.list()
    except UnauthorizedError:
        # token expired or wrong — re-pair / refresh
        ...
    except HttpError as err:
        print(err.status, err.body)
```

All errors derive from `FathomError`. Specific subclasses:
`UnauthorizedError`, `HttpError`, `PairingRequiredError`,
`MalformedResponseError`.

## Reference

| Method | Description |
|---|---|
| `f.threads.list()` | All non-deleted threads, newest first. |
| `f.threads.get(id)` | One thread + its message history. |
| `f.threads.create(title="")` | New thread; auto-titles on first message if blank. |
| `f.threads.send(id, text)` | Send a user message, return user + agent reply. |
| `f.threads.delete(id)` | Soft-delete (restorable). |
| `f.threads.restore(id)` | Undo a soft-delete. |
| `f.threads.stream(id)` | Iterator (sync) / AsyncIterator (async) of SSE events. |
| `f.pair(code=..., device_name=...)` | Claim a pairing code, returns token. |
| `f.with_token(token)` | Returns a new client with a different token, sharing connection pool. |

## Requirements

- Python 3.10+
- Depends on [httpx](https://www.python-httpx.org/) — single dependency.

## License

MIT — see [LICENSE](./LICENSE).
