Metadata-Version: 2.4
Name: finn-sdk
Version: 0.1.0
Summary: Python SDK for connecting to the Finn (finn-ai) agent over WebSocket.
Project-URL: Homepage, https://docs.riseanalytics.io
Project-URL: Documentation, https://docs.riseanalytics.io
Author: Rise Analytics
License: MIT
Keywords: ai,analytics,credit-union,finn,riseanalytics,websocket
Classifier: Framework :: AsyncIO
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.9
Requires-Dist: python-socketio[asyncio-client]>=5.11.0
Provides-Extra: test
Requires-Dist: aiohttp>=3.9; extra == 'test'
Requires-Dist: pytest-asyncio>=0.23; extra == 'test'
Requires-Dist: pytest>=8.0; extra == 'test'
Description-Content-Type: text/markdown

# finn-sdk (Python)

Python SDK for the Finn (finn-ai) agent over WebSocket. The async counterpart of
the Node/TypeScript [`@riseanalytics/finn-sdk`](https://docs.riseanalytics.io) —
same connection, events, and error codes.

## Install

```bash
pip install finn-sdk
```

Requires Python 3.9+.

## Usage

```python
import asyncio
from finn_sdk import FinnClient

async def main():
    finn = FinnClient(url="https://finn.example.com", api_key="your-key")
    await finn.connect()

    # Stream tokens as they arrive
    turn = await finn.send_message(
        "What were Q3 deposits?",
        system_prompt="You are a terse analyst.",
    )
    async for chunk in turn:
        print(chunk.text, end="", flush=True)
    result = await turn.completed  # TurnResult(conversation_id, message_id, token_usage, ...)

    # Continue the same conversation
    follow = await finn.send_message("And Q4?", conversation_id=result.conversation_id)
    await follow.completed

    await finn.disconnect()

asyncio.run(main())
```

Or use the client as an async context manager (auto connect/disconnect):

```python
async with FinnClient(url=url, api_key=key) as finn:
    answer = await finn.ask("How many active members do I have?")
    print(answer.text)
```

## Notes

- **Auth:** pass `api_key` (static) or `get_api_key` (sync or async callable, for
  rotation). Sent as the Socket.IO handshake token.
- **System prompt:** `system_prompt` (client default or per-call override) applies
  only when a conversation is first created; it is ignored on continuation turns.
- **Not streaming?** `await finn.ask(text)` returns an `AskResult` (the turn result
  plus the full `text`). `await finn.ask_json(text)` parses the answer as JSON —
  prompt for JSON yourself; it raises `FinnError(code="invalid_json")` if it can't
  parse, with the raw answer on `error.details["raw"]`.
- **Interrupt:** `turn.interrupt()` stops generation; the turn settles with
  `interrupted=True`.
- **Errors:** failures raise a `FinnError` whose `code` is one of `unauthorized`,
  `rate_limited`, `timeout`, `conversation_read_only`, `stream_error`,
  `disconnected`, `invalid_json`.
- **Events:** `finn.on("connected" | "disconnect" | "reconnect" | "rateLimit" | "unauthorized", cb)`.
- **Naming:** methods and fields use Python `snake_case` (`send_message`,
  `conversation_id`, `token_usage`) — the TypeScript SDK uses `camelCase`.

## Develop

```bash
pip install -e ".[test]"
pytest
```
