Metadata-Version: 2.4
Name: tokensaver-sdk
Version: 0.1.6
Summary: Python SDK for the TokenSaver API: pipelines, chat sessions, and pricing estimates
Author: TokenSaver
License-Expression: MIT
Project-URL: Homepage, https://platform.tokensaver.fr
Project-URL: Documentation, https://platform.tokensaver.fr
Keywords: tokensaver,llm,api,client
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx<0.28,>=0.26.0
Requires-Dist: pydantic>=2.5.0
Provides-Extra: dev
Requires-Dist: build>=1.0.0; extra == "dev"
Requires-Dist: pytest>=7.4.0; extra == "dev"
Requires-Dist: pytest-httpx>=0.28.0; extra == "dev"
Requires-Dist: python-dotenv>=1.0.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: twine>=5.0.0; extra == "dev"
Dynamic: license-file

# TokenSaver SDK

Python client for the **TokenSaver API** (`POST /pipelines/run`, RAG, chat sessions, pricing).  
Only HTTP transport and response normalization run in this package; **all pipeline logic stays on the server**.

**Get started (public)** : sign up or sign in at **[platform.tokensaver.fr](https://platform.tokensaver.fr)**, open your workspace in the console, then the **API Reference** page (examples, parameters, SDK methods).

**Also** : **[tokensaver.fr](https://tokensaver.fr)** — product marketing / positioning. **`https://api.tokensaver.fr/api/v1`** — HTTP API this SDK calls by default (`base_url`); override `base_url` only for another deployment.

## Installation

```bash
pip install tokensaver-sdk
```

Development inside the monorepo:

```bash
cd packages/sdk
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
```

Maintainers with a clone of the private repository can read the long-form architecture notes at `docs/ARCHITECTURE-SDK-TOKENSAVER.md` (repo root). That path is not published on PyPI.

## Configuration

- **`api_key`** (required): TokenSaver API key (`ts_...`).
- **`base_url`** (optional): API base URL; default **`https://api.tokensaver.fr/api/v1`**.
- **`provider_api_key`** (optional): LLM provider key (`openai`, `anthropic`, etc.) sent on **every** `ask` / `run_pipeline` when set here; takes precedence over organization keys stored in the database for that run only; **never** persisted by TokenSaver. You can also pass `provider_api_key=` on a single call.

```python
from tokensaver_sdk import TokenSaver

ts = TokenSaver(api_key="ts_...")
# Local backend:
# ts = TokenSaver(api_key="ts_...", base_url="http://localhost:8000/api/v1")
# Default LLM key for all runs (optional):
# ts = TokenSaver(api_key="ts_...", provider_api_key="sk-...")
```

## Pipeline calls (`ask` / `run_pipeline`)

`ask()` returns a **`RunResult`** (`.text`, `.metrics`, `.trace`, `.context`).  
`run_pipeline()` returns the **raw API JSON**.

**Module flags** (off by default in the SDK, same as in the console):  
`use_cache`, `use_rag`, `use_compression`, `use_pii_filter`, `stream`.

**Advanced parameters** (aligned with `PipelineRunRequest` / console) — passed through to the JSON body as-is:

| Parameter | Purpose |
|-----------|---------|
| `temperature` | LLM temperature (0–2). |
| `rag_similarity_threshold` | RAG similarity threshold (0–1). |
| `cache_similarity_threshold` | Semantic cache similarity threshold (0–1). |
| `compression_level` | Compression level 1–5. |
| `rag_options` | Dict: `document_ids`, `top_k`, `query_image_url`. |
| `pii_options` | Dict: `engine`, `strategy`, `confidence_threshold`, `entity_types`, `language`, `regex_fallback`. |
| `context_layers` | Canonical shape (instructions, knowledge, interaction, `token_budget`). |
| `system_prompt`, `profile_context`, `workspace_instructions` | Legacy flat fields (if no `context_layers`). |
| `provider_api_key` | **SDK**: ephemeral LLM key for this run; overrides DB keys; not persisted. |

IDE helpers: `from tokensaver_sdk import RagOptions, PiiOptions` (`TypedDict`).

```python
result = ts.ask(
    "Your question",
    provider="openai",
    model="gpt-4o",
    use_rag=True,
    rag_similarity_threshold=0.55,
    rag_options={"document_ids": ["uuid-doc"], "top_k": 8},
)
```

## RAG (documents)

| Method | Role |
|--------|------|
| `rag_list_documents()` | Lists PDFs in the workspace. |
| `rag_upload_document(path, …)` | Multipart upload (no wait). Raises `ValidationError` (`RAG_FILE_NOT_FOUND`) if the file is missing. |
| `rag_get_document(id)` | Status / metadata. |
| `rag_wait_document_ready(id, …)` | Wait for ingestion. |
| `rag_upload_and_wait(path, …)` | Upload + wait. |
| `rag_ensure_document(path, …)` | Reuses an already ingested file (same **file name**) or upload + wait. |

Minimal example with a question over a PDF:

```python
doc = ts.rag_ensure_document("handbook.pdf")
ts.ask(
    "What are the key points?",
    provider="openai",
    model="gpt-4o",
    use_rag=True,
    rag_options={"document_ids": [doc["document_id"]]},
)
```

## Chat sessions

```python
from tokensaver_sdk import HISTORY_NONE, HISTORY_LOCAL, HISTORY_SERVER

# Stateless (default for ask: history=HISTORY_NONE)
ts.ask("…", provider="openai", model="gpt-4o", history=HISTORY_NONE)

# Server-side persistence
session = ts.chat.session(history=HISTORY_SERVER, name="My chat")
session.ask("…", provider="openai", model="gpt-4o")
```

## Cost estimate (no LLM call)

```python
ts.estimate_cost(1200, 300, provider="openai", model="gpt-4o")
```

## Errors

```python
from tokensaver_sdk import ERROR_RAG_FILE_NOT_FOUND
from tokensaver_sdk.errors import (
    TokenSaverError,
    AuthenticationError,
    ProviderKeyMissingError,
    QuotaExceededError,
    RateLimitError,
    ValidationError,
    ServerError,
    TimeoutError,
)
```

HTTP errors map to these exceptions. For RAG uploads (`rag_upload_document`, `rag_upload_and_wait`, `rag_ensure_document` when a file is sent), a missing PDF path on the client raises `ValidationError` with `code="RAG_FILE_NOT_FOUND"` (compare to `ERROR_RAG_FILE_NOT_FOUND`); the `raw` payload includes `"path"` among other fields.

## Tests & quality

```bash
pytest
ruff check src tests && ruff format src tests
```

Useful variables for integration tests: `TOKENSAVER_API_KEY`, base URL depending on your deployment.

## Publishing to PyPI

1. **Version**: bump `__version__` in `src/tokensaver_sdk/__init__.py` (single source of truth for the build).
2. **Build & check**: `python -m build` then `twine check dist/*` (dev deps: `pip install -e ".[dev]"`).
3. **Upload**: `twine upload dist/*` (PyPI username: `__token__`, password: your API token). Try TestPyPI first with `--repository testpypi`.
4. **CI**: after adding the **`PYPI_API_TOKEN`** repository secret, pushing tag `sdk-v0.1.0` (same as `__version__`) triggers the **Publish SDK to PyPI** workflow.

## Further reference

- **Product site**: [tokensaver.fr](https://tokensaver.fr) — positioning and presentation.
- **TokenSaver app**: [platform.tokensaver.fr](https://platform.tokensaver.fr) — sign in, workspace, **API Reference** (examples, SDK methods).
- **HTTP API** (SDK default): `https://api.tokensaver.fr/api/v1`.
- **Architecture & decisions**: internal to the TokenSaver monorepo (`docs/ARCHITECTURE-SDK-TOKENSAVER.md`); not linked here because source repositories are private.
