Metadata-Version: 2.4
Name: aurelle-py
Version: 0.1.0
Summary: Unofficial Python client for the alphaxiv.org Assistant API (aurelle-1 model)
Project-URL: Homepage, https://github.com/center4aai/aurelle-py
Project-URL: Source, https://github.com/center4aai/aurelle-py
Project-URL: Bug Tracker, https://github.com/center4aai/aurelle-py/issues
Author: center4aai
License: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-timeout>=2; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == 'mcp'
Description-Content-Type: text/markdown

# aurelle-py

[![PyPI version](https://img.shields.io/pypi/v/aurelle-py)](https://pypi.org/project/aurelle-py/)
[![Python 3.12+](https://img.shields.io/pypi/pyversions/aurelle-py)](https://pypi.org/project/aurelle-py/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

Unofficial Python client library for the [alphaxiv.org](https://www.alphaxiv.org) Assistant API — the `aurelle-1` model that provides AI-powered Q&A over arXiv papers.

---

> **Disclaimer**: This is an unofficial, community-developed client.
> `aurelle-py` is **not** affiliated with, endorsed by, or sponsored by alphaXiv.
> This client uses an undocumented internal API that may change without notice.
> Please respect alphaxiv's [Terms of Service](https://alphaxiv.org/terms).
> The API key used is generated via alphaxiv.org's account settings.

---

## How it was discovered

The API endpoint was found by inspecting network traffic in browser DevTools while using alphaxiv.org. The payload schema, authentication method, SSE event format, and rate-limit headers were all reverse-engineered from real browser requests. There is no official documentation.

## Features

- Sync and async clients (`AurelleClient`, `AsyncAurelleClient`)
- Streaming support (`stream()` method)
- `ask_paper()` convenience method for arXiv paper Q&A
- Pydantic v2 request validation
- Typed exceptions (`AuthError`, `RateLimitError`, `AurelleAPIError`)
- MCP server for use with Claude Desktop and other LLM agents
- Python 3.12+

## Installation

```bash
pip install aurelle-py
```

With MCP server support:

```bash
pip install "aurelle-py[mcp]"
```

Or with [uv](https://docs.astral.sh/uv/):

```bash
uv add aurelle-py
```

## Getting an API key

1. Create an account at [alphaxiv.org](https://www.alphaxiv.org)
2. Go to **Account → API Keys**
3. Generate a new key

Set it as an environment variable:

```bash
export AURELLE_API_KEY=your_key_here
```

## Quick start

### Sync client

```python
from aurelle import AurelleClient

client = AurelleClient()  # reads AURELLE_API_KEY from environment

# One-shot question
response = client.chat("What is the attention mechanism in Transformers?")
print(response.text)
print(f"chat_id: {response.chat_id}")

# Ask about a specific arXiv paper
response = client.ask_paper(
    arxiv_id="1706.03762",
    question="What BLEU scores were reported?",
)
print(response.text)
```

### Streaming

```python
for chunk in client.stream("Summarize the Transformer architecture"):
    print(chunk.delta, end="", flush=True)
```

### Multi-turn conversation

```python
r1 = client.chat("Summarize arxiv 1706.03762")
r2 = client.chat("What BLEU scores were reported?", chat_id=r1.chat_id)
```

> **Note**: Multi-turn context is limited — the API does not expose `parentMessageId`,
> so the model may not reliably recall earlier messages in the same session.

### Async client

```python
import asyncio
from aurelle import AsyncAurelleClient

async def main():
    async with AsyncAurelleClient() as client:
        response = await client.chat("Explain O(n²) attention complexity")
        print(response.text)

        async for chunk in client.stream("Summarize arxiv 1706.03762"):
            print(chunk.delta, end="", flush=True)

asyncio.run(main())
```

### Web search

```python
response = client.chat("Latest diffusion model papers 2025", web_search=True)
```

### Thinking mode

```python
response = client.chat("Why does self-attention scale quadratically?", thinking=True)
```

## API reference

### `AurelleClient(api_key=None, *, timeout=90.0, max_retries=3)`

| Parameter | Type | Description |
|---|---|---|
| `api_key` | `str \| None` | alphaxiv API key. Falls back to `AURELLE_API_KEY` env var. |
| `timeout` | `float` | HTTP timeout in seconds (default 90 — responses can be slow). |
| `max_retries` | `int` | Retry attempts on timeout / 5xx (default 3). |

#### `.chat(message, *, chat_id=None, paper_version_id=None, web_search=False, thinking=False) → ChatResponse`

Send a message and return the fully assembled response.

#### `.stream(message, ...) → Iterator[StreamChunk]`

Stream the response as an iterator of chunks. Same parameters as `chat()`.

#### `.ask_paper(arxiv_id, question, *, chat_id=None) → ChatResponse`

Convenience wrapper. Builds `"arxiv:{id} — {question}"` and calls `chat()`.

### `AsyncAurelleClient`

Identical interface with `await`. Use `async for chunk in client.stream(...)`.

### Response types

```python
@dataclass
class ChatResponse:
    text: str            # assembled answer text
    chat_id: str         # session UUID (use in subsequent calls)
    tool_uses: list[ToolUse]
    raw_events: list[dict]

@dataclass
class StreamChunk:
    delta: str           # text fragment (empty for tool events)
    event_type: str      # "delta_output_text" | "tool_use" | "tool_result_text"

@dataclass
class ToolUse:
    tool_use_id: str
    kind: str            # e.g. "Answer PDF Queries", "Fetch arXiv Abstract"
    content: str         # raw JSON string
```

### Exceptions

| Exception | When raised |
|---|---|
| `AuthError` | Missing API key, HTTP 401 or 403 |
| `RateLimitError` | HTTP 429. Has `.reset_in` (seconds) attribute. |
| `AurelleAPIError` | Other HTTP 4xx/5xx. Has `.status_code` attribute. |
| `AurelleError` | Base class for all aurelle exceptions. |

## Rate limits

alphaxiv enforces **40 requests per 60 seconds**.

On hitting the limit, the client raises `RateLimitError` immediately (no silent retry):

```python
from aurelle.exceptions import RateLimitError
import time

try:
    response = client.chat("...")
except RateLimitError as exc:
    print(f"Rate limited. Retry in {exc.reset_in}s")
    time.sleep(exc.reset_in or 60)
```

## MCP server

`aurelle-py` ships an MCP server that exposes aurelle tools to LLM agents.

### Setup

Install with MCP support and start the server:

```bash
pip install "aurelle-py[mcp]"
AURELLE_API_KEY=your_key aurelle-mcp
```

### Claude Desktop configuration

Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):

```json
{
  "mcpServers": {
    "aurelle": {
      "command": "aurelle-mcp",
      "env": {
        "AURELLE_API_KEY": "your_key_here"
      }
    }
  }
}
```

### Claude Code configuration

Create `.mcp.json` in the root of your project (this is the correct location —
**not** `.claude/settings.json`, which is ignored for MCP):

```json
{
  "mcpServers": {
    "aurelle": {
      "type": "stdio",
      "command": "uv",
      "args": ["run", "python", "-m", "aurelle.mcp.server"],
      "cwd": "/absolute/path/to/your/project",
      "env": {
        "AURELLE_API_KEY": "your_key_here"
      }
    }
  }
}
```

If `aurelle-mcp` is installed as a script (via `pip install "aurelle-py[mcp]"`),
you can use the entry point directly:

```json
{
  "mcpServers": {
    "aurelle": {
      "type": "stdio",
      "command": "aurelle-mcp",
      "env": {
        "AURELLE_API_KEY": "your_key_here"
      }
    }
  }
}
```

Restart Claude Code after creating or modifying `.mcp.json` — the config is
read only at startup.

### Available tools

| Tool | Description |
|---|---|
| `ask_arxiv_paper(arxiv_id, question)` | Ask a question about a specific arXiv paper |
| `research_question(query, web_search)` | General research question with optional web search |

## Known limitations

- **Multi-turn context is unreliable**: The API assigns a `llmChatId` for session
  continuity, but `parentMessageId` is never returned, so the model may not
  recall earlier messages in the same session.
- **`assistantVariant="homepage"` times out**: This variant requires a browser
  session context. The client always uses `"paper"` which works for all request
  types.
- **`web_search=True` with a specific paper may hang**: Combining web search with
  `paper_version_id` can cause the request to hang indefinitely. Use one or the
  other, not both.
- **Undocumented API**: This API was reverse-engineered and may change without
  notice. Pin your `aurelle-py` version and test after alphaxiv updates.

## Development

```bash
# Clone and install dev dependencies
git clone https://github.com/center4aai/aurelle-py.git
cd aurelle-py
uv sync --extra dev

# Run unit tests (no API key required)
uv run pytest

# Run integration tests (requires AURELLE_API_KEY)
AURELLE_API_KEY=your_key uv run pytest -m integration
```

## License

MIT — see [LICENSE](LICENSE).
