> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chucky.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Chucky Client

> Python SDK client reference

<Note>
  **Status: Actively Maintained** - The Python SDK is actively maintained alongside the TypeScript SDK and provides full feature parity.
</Note>

# Chucky Client

The main client class for interacting with Chucky in Python.

## Installation

```bash theme={null}
pip install chucky-sdk
```

Or with Poetry:

```bash theme={null}
poetry add chucky-sdk
```

## Import

```python theme={null}
from chucky import Chucky
```

## Constructor

```python theme={null}
client = Chucky(
    token: str,
    url: str = 'wss://conjure.chucky.cloud/ws',
    model: str = 'claude-sonnet-4-5-20250929',
    system_prompt: Optional[str] = None,
    max_turns: Optional[int] = None,
    timeout: float = 30.0,
    keepalive_interval: float = 60.0,
)
```

### Parameters

| Parameter            | Type    | Required | Default                           | Description                   |
| -------------------- | ------- | -------- | --------------------------------- | ----------------------------- |
| `token`              | `str`   | Yes      | -                                 | JWT authentication token      |
| `url`                | `str`   | No       | `'wss://conjure.chucky.cloud/ws'` | WebSocket server URL          |
| `model`              | `str`   | No       | `'claude-sonnet-4-5-20250929'`    | Claude model to use           |
| `system_prompt`      | `str`   | No       | `None`                            | System prompt                 |
| `max_turns`          | `int`   | No       | `None`                            | Max conversation turns        |
| `timeout`            | `float` | No       | `30.0`                            | Connection timeout (seconds)  |
| `keepalive_interval` | `float` | No       | `60.0`                            | Keep-alive interval (seconds) |

### Example

```python theme={null}
from chucky import Chucky

client = Chucky(
    token='your-jwt-token',
    model='claude-sonnet-4-5-20250929',
    system_prompt='You are a helpful assistant.',
    timeout=60.0,
)
```

## Methods

### prompt()

Send a one-shot prompt and get the response.

```python theme={null}
async def prompt(
    message: str,
    *,
    model: Optional[str] = None,
    system_prompt: Optional[str] = None,
    tools: Optional[List[Tool]] = None,
    max_turns: Optional[int] = None,
) -> PromptResult
```

#### Parameters

| Parameter       | Type         | Description                    |
| --------------- | ------------ | ------------------------------ |
| `message`       | `str`        | The prompt message             |
| `model`         | `str`        | Override default model         |
| `system_prompt` | `str`        | Override default system prompt |
| `tools`         | `List[Tool]` | Tools available to Claude      |
| `max_turns`     | `int`        | Max conversation turns         |

#### Returns

`PromptResult` with the response.

#### Example

```python theme={null}
result = await client.prompt('What is the capital of France?')
print(result.result)  # "The capital of France is Paris."
print(result.total_cost_usd)  # 0.0012
```

***

### stream()

Send a prompt with streaming response.

```python theme={null}
async def stream(
    message: str,
    *,
    model: Optional[str] = None,
    system_prompt: Optional[str] = None,
    tools: Optional[List[Tool]] = None,
    max_turns: Optional[int] = None,
) -> AsyncGenerator[StreamEvent, None]
```

#### Example

```python theme={null}
async for event in client.stream('Write a poem about coding'):
    if event.type == 'message':
        print(event.data, end='', flush=True)
    elif event.type == 'tool_use':
        print(f'\n[Using tool: {event.name}]')
```

***

### create\_session()

Create a new conversation session. Returns a `Session` immediately; connection happens on first `send()`.

```python theme={null}
def create_session(
    *,
    model: Optional[str] = None,
    system_prompt: Optional[str] = None,
    tools: Optional[List[Tool]] = None,
    max_turns: Optional[int] = None,
) -> Session
```

#### Example

```python theme={null}
# create_session is synchronous - returns Session immediately
session = client.create_session(
    model='claude-sonnet-4-5-20250929',
    system_prompt='You are a math tutor.',
)

# Connection happens on first send()
await session.send('What is calculus?')
async for msg in session.stream():
    if msg.get('type') == 'result':
        print(msg.get('result'))

await session.close()
```

***

### close()

Close the client and release resources.

```python theme={null}
async def close() -> None
```

#### Example

```python theme={null}
await client.close()
```

## Session Class

The Session class provides a multi-turn conversation interface.

### Methods

#### send()

Send a message to the session. Use `stream()` to get the response.

```python theme={null}
async def send(message: str) -> None
```

#### stream()

Stream the response after sending a message.

```python theme={null}
async def stream() -> AsyncIterator[Dict[str, Any]]
```

Returns an async iterator of SDK messages (dicts with `type`, `message`, etc.).

#### receive()

Alias for `stream()` for V2 SDK compatibility.

```python theme={null}
async def receive() -> AsyncIterator[Dict[str, Any]]
```

#### close()

Close the session and release resources.

```python theme={null}
async def close() -> None
```

### Properties

#### session\_id

The session's unique identifier.

```python theme={null}
@property
def session_id(self) -> str
```

### Session Example

```python theme={null}
session = client.create_session(model='claude-sonnet-4-5-20250929')

# First turn
await session.send('Hello, I am Alice')
async for msg in session.stream():
    if msg.get('type') == 'assistant':
        text = get_assistant_text(msg)
        print(text)
    if msg.get('type') == 'result':
        break

# Second turn - maintains conversation context
await session.send('What is my name?')
async for msg in session.stream():
    if msg.get('type') == 'result':
        print(msg.get('result'))  # "Your name is Alice."

await session.close()
```

## Message Types

Messages are dictionaries matching the SDK protocol.

### Result Message

```python theme={null}
{
    'type': 'result',
    'subtype': 'success',  # or 'error'
    'result': str,         # Final response text
    'session_id': str,
    'total_cost_usd': float,
    'duration_ms': int,
    'num_turns': int,
}
```

### Assistant Message

```python theme={null}
{
    'type': 'assistant',
    'message': {
        'role': 'assistant',
        'content': [
            {'type': 'text', 'text': '...'},
            {'type': 'tool_use', 'id': '...', 'name': '...', 'input': {...}},
        ]
    }
}
```

### Helper Functions

Use these helpers to extract text from messages:

```python theme={null}
from chucky import get_assistant_text, get_result_text

# Extract text from assistant message
text = get_assistant_text(msg)  # Returns concatenated text blocks

# Extract result from result message
result = get_result_text(msg)  # Returns result string or None
```

### StreamEvent (Legacy)

For the legacy `client.stream()` API:

```python theme={null}
@dataclass
class StreamEvent:
    type: str   # 'message', 'result', 'error', 'done'
    data: Any   # The message dict
```

## Complete Example

```python theme={null}
import asyncio
from chucky import (
    Chucky,
    create_token,
    create_budget,
    tool,
    text_result,
    get_assistant_text,
)

# Create a token (server-side only)
token = create_token(
    user_id='user-123',
    project_id='your-project-id',  # From app.chucky.cloud
    secret='your-hmac-secret',     # From app.chucky.cloud
    budget=create_budget(ai_dollars=1.00, compute_hours=1, window='day'),
)

# Define a tool
@tool('get_time', 'Get current time')
async def get_time() -> dict:
    from datetime import datetime
    return text_result(datetime.now().isoformat())

async def main():
    # Create client
    client = Chucky(
        url='wss://conjure.chucky.cloud/ws',
        token=token,
        model='claude-sonnet-4-5-20250929',
    )

    try:
        # Simple prompt
        result = await client.prompt('What is 2 + 2?')
        print(f"Answer: {result.get('result')}")
        print(f"Cost: ${result.get('total_cost_usd', 0):.4f}")

        # Multi-turn session with tools
        session = client.create_session(tools=[get_time])

        await session.send('What time is it?')
        async for msg in session.stream():
            if msg.get('type') == 'assistant':
                print(get_assistant_text(msg))
            if msg.get('type') == 'result':
                print(f"Done: {msg.get('result')}")
                break

        await session.close()

    finally:
        await client.close()

asyncio.run(main())
```

## Error Handling

Errors are returned as result messages with `subtype: 'error'`:

```python theme={null}
from chucky import Chucky

client = Chucky(url='wss://conjure.chucky.cloud/ws', token='...')

try:
    result = await client.prompt('Hello')
    if result.get('subtype') == 'error':
        print(f"Error: {result.get('result')}")
    else:
        print(result.get('result'))
except RuntimeError as e:
    # Connection errors, timeouts
    print(f'Connection error: {e}')
except Exception as e:
    print(f'Unexpected error: {e}')
```

For streaming, check for error messages:

```python theme={null}
session = client.create_session()
await session.send('Hello')
async for msg in session.stream():
    if msg.get('type') == 'error':
        print(f"Error: {msg.get('error', {}).get('message')}")
        break
```
