Metadata-Version: 2.4
Name: kronaxis
Version: 0.1.0
Summary: Python SDK for the Kronaxis Panel Studio API
Home-page: https://github.com/kronaxis/kronaxis-python-sdk
Author: Kronaxis Limited
Author-email: dev@kronaxis.co.uk
Keywords: kronaxis panel-studio synthetic-personas causal-reasoning
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
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 :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28.0
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# Kronaxis Python SDK

Python client for the Kronaxis Panel Studio API. Build, query, and export synthetic persona panels for causal reasoning research.

## Installation

```bash
pip install kronaxis
```

Or install from source:

```bash
cd sdk/
pip install -e .
```

## Quick Start

```python
from kronaxis import KronaxisClient

client = KronaxisClient(
    api_key="kx_your_api_key",
    base_url="https://panel.kronaxis.co.uk"
)

# List panels
panels = client.panels.list()
for p in panels:
    print(f"{p.name}: {p.persona_count} personas")

# Create a panel from a description
job = client.panels.create_from_description(
    "50 middle-income London women aged 30-45",
    country="GB"
)
build = client.panels.wait_for_build(job.job_id)
print(f"Panel created: {build.panel_id}")
```

## Conversations

```python
# Start a conversation and ask a question
result = client.conversations.ask(
    panel_id,
    "Your favourite brand raises prices by 20%. What do you do?"
)

# A/B test two message variants
comparison = client.conversations.compare(
    panel_id,
    conversation_id,
    stimulus_a="We are raising prices to invest in quality.",
    stimulus_b="Due to rising costs, prices will increase.",
    sample_size=50
)
print(f"Winner: {comparison.winner}")
```

## Stimulus Templates

```python
# List available templates
templates = client.stimulus.templates()

# Fill a template
text = client.stimulus.from_template(
    "pricing_test",
    current_price="9.99",
    new_price="12.99"
)

# Bulk run multiple stimuli
results = client.stimulus.bulk_run(
    panel_id,
    conversation_id,
    ["How do you feel about Brand X?", "Would you switch to Brand Y?"]
)
```

## Exports

```python
# Export conversation data
client.export.jsonl(panel_id, conversation_id, output_path="data.jsonl")
client.export.csv(panel_id, conversation_id, output_path="data.csv")
client.export.parquet(panel_id, conversation_id, output_path="data.parquet")
client.export.pptx(panel_id, conversation_id, output_path="report.pptx")
```

## Webhooks

```python
# Create a webhook for build notifications
client.create_webhook(
    "https://example.com/webhook",
    events=["build_complete", "conversation_complete"],
    secret="my-signing-secret"
)

# List webhooks
hooks = client.list_webhooks()

# Test a webhook
client.test_webhook(hooks[0]["id"])
```

## Living Panels

```python
# Enable simulation on a panel
client.panels.set_simulation(panel_id, depth="full")

# Inject a life event
client.panels.inject_event(
    panel_id,
    event_type="job_loss",
    description="Made redundant due to company restructuring",
    target="random",
    target_pct=10
)

# View timeline
timeline = client.panels.timeline(panel_id, per_page=20)
```

## Error Handling

```python
from kronaxis import KronaxisClient, KronaxisError, KronaxisAuthError, KronaxisNotFoundError

try:
    panel = client.panels.get("nonexistent-id")
except KronaxisNotFoundError:
    print("Panel not found")
except KronaxisAuthError:
    print("Authentication failed")
except KronaxisError as e:
    print(f"API error: {e} (HTTP {e.status_code})")
```

## Webhook Payload Format

All webhook payloads are JSON POST requests:

```json
{
    "event": "conversation_complete",
    "panel_id": "uuid",
    "timestamp": "2026-03-20T12:00:00+00:00",
    "data": {
        "conversation_id": "uuid",
        "turn_id": "uuid",
        "response_count": 50
    }
}
```

If a signing secret is configured, the `X-Kronaxis-Signature` header contains `sha256=<hmac_hex>`.

Verify the signature:

```python
import hmac
import hashlib

def verify_webhook(payload_bytes, signature_header, secret):
    expected = hmac.new(secret.encode(), payload_bytes, hashlib.sha256).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature_header)
```

## Licence

Proprietary. See https://kronaxis.co.uk/licensing for details.
