Metadata-Version: 2.4
Name: arxivis
Version: 1.0.0
Summary: Official Python SDK for the Arxivis document store
Project-URL: Homepage, https://github.com/v019-Labs/arxivis
Project-URL: Repository, https://github.com/v019-Labs/arxivis
Project-URL: Bug Tracker, https://github.com/v019-Labs/arxivis/issues
Author-email: v019 Labs <hello@v019labs.com>
License: MIT
Keywords: arxivis,document-store,file-storage,sdk
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: httpx>=0.27.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Description-Content-Type: text/markdown

# arxivis-python

Official Python SDK for the [Arxivis](https://github.com/v019-Labs/arxivis) document store.

## Requirements

- Python ≥ 3.9
- [httpx](https://www.python-httpx.org/) (installed automatically)

## Installation

```bash
pip install arxivis
```

Or from source:

```bash
pip install ./sdk/python
```

## Quick start

### Synchronous

```python
from arxivis import ArxivisClient, UploadOptions

with ArxivisClient("http://localhost:8080", "axv_xxxx_yyyy") as client:
    # Upload a file
    record = client.upload(
        open("invoice.pdf", "rb").read(),
        "invoice.pdf",
        UploadOptions(path="/invoices/2024/", tags=["cliente", "enero"]),
    )
    print(f"Stored: {record.id}  ({record.size} bytes)")

    # List files
    result = client.list("/invoices/2024/", limit=20)
    for f in result.files:
        print(f.original_name)

    # Full-text search
    hits = client.search("factura enero")
    for f in hits.files:
        print(f.original_name)

    # Download
    data = client.download(record.id)
    open("copy.pdf", "wb").write(data)

    # Delete
    client.delete(record.id)
```

### Asynchronous

```python
import asyncio
from arxivis import AsyncArxivisClient, UploadOptions

async def main():
    async with AsyncArxivisClient("http://localhost:8080", "axv_xxxx_yyyy") as client:
        stats = await client.get_stats()
        print(f"Total files: {stats.total_files}")

        record = await client.upload(
            open("doc.pdf", "rb").read(),
            "doc.pdf",
            UploadOptions(path="/docs/"),
        )
        print(record.id)

asyncio.run(main())
```

### Upload from disk

```python
record = client.upload_file("/path/to/file.pdf", UploadOptions(path="/docs/"))
```

## API reference

### `ArxivisClient` / `AsyncArxivisClient`

Both classes expose identical methods; async versions are prefixed with `await`.

| Method | Description |
|---|---|
| `get_stats()` | Aggregate storage statistics |
| `upload(data, filename, opts)` | Upload raw bytes |
| `upload_file(path, opts)` | Upload from disk |
| `list(path, limit, offset)` | Paginated file listing |
| `get(id)` | Fetch file metadata |
| `download(id)` | Download raw bytes |
| `delete(id)` | Soft-delete a file |
| `download_zip(ids)` | Export multiple files as ZIP |
| `create_folder(path)` | Create a virtual folder |
| `search(query, limit, offset)` | Full-text search (FTS5) |
| `semantic_search(query, limit)` | Vector similarity search |
| `hybrid_search(query, limit)` | FTS5 + semantic fusion |
| `create_key(name)` | Create an API key |
| `list_keys()` | List all API keys |
| `revoke_key(id)` | Revoke an API key |
| `download_url(id)` | Direct download URL |
| `preview_url(id)` | Inline preview URL |
| `path_url(file_path)` | Virtual-path URL |

### `UploadOptions`

```python
from arxivis import UploadOptions

opts = UploadOptions(
    path="/invoices/2024/",   # virtual folder (default "/")
    tags=["cliente", "ene"],  # list of tags (default [])
    compress=True,            # override server default (default None)
    encrypt=True,             # override server default (default None)
)
```

### `ArxivisError`

```python
from arxivis import ArxivisError

try:
    client.get("nonexistent-id")
except ArxivisError as e:
    print(e.status)           # HTTP status code
    print(e.is_not_found)     # True if 404
    print(e.is_unauthorized)  # True if 401
    print(e.is_rate_limited)  # True if 429
```

## License

MIT
