Metadata-Version: 2.4
Name: nessyapi-sdk
Version: 0.3.0
Summary: Python SDK for NessyAPI — Clinical Decision Support API
Author-email: "HealthyNess.cz" <dev@healthyness.cz>
License: MIT
Project-URL: Homepage, https://healthyness.cz
Project-URL: Documentation, https://github.com/jachymvrtiskaHN/NessyAPI/tree/master/sdk
Project-URL: Repository, https://github.com/jachymvrtiskaHN/NessyAPI
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Healthcare Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"

# NessyAPI Python SDK

Python SDK for [NessyAPI](https://healthyness.cz) — Clinical Decision Support API.

## Install

```bash
pip install nessyapi-sdk
```

## Quick Start

```python
from nessyapi_sdk import NessyClient

with NessyClient(api_key="nsy_live_...") as client:
    # Create a session for a patient
    session = client.create_session(
        chief_complaint="headache",
        age=35,
        sex="male",
        patient_id="your-patient-123",  # links to patient profile
    )

    # Answer questions from the engine
    q = session.current_question
    while q:
        print(f"Q: {q.text}")
        
        # Send patient's answer (NLP extracts medical fields automatically)
        result = client.answer(session.session_id, q.question_id, raw_text="3 days, getting worse")
        
        if result.is_complete:
            break
        q = result.current_question

    # Get final results
    results = client.finalize(session.session_id)
    print(f"Triage: {results.triage_level}")
    for dx in results.differentials:
        print(f"  {dx.diagnosis} ({dx.probability:.0%}) — ICD-10: {dx.icd10}")
```

## One-Line Assessment

```python
results = client.run_assessment("chest_pain", age=55, sex="male")
print(results.triage_level)  # "urgent"
print(results.differentials[0].diagnosis)  # "Acute Coronary Syndrome"
```

## Async Support

```python
from nessyapi_sdk import AsyncNessyClient

async with AsyncNessyClient(api_key="nsy_live_...") as client:
    session = await client.create_session("headache", age=35, sex="male")
    results = await client.run_assessment("fever", age=28, sex="female")
```

## Session Management

```python
# List all sessions
sessions = client.list_sessions(status="finalized", limit=10)
print(f"Total: {sessions.total}")

# Get questions with progress
questions = client.get_questions(session_id)
print(f"Progress: {questions.questions_asked}/{questions.questions_total}")

# Export complete session record
export = client.export_session(session_id)

# Update demographics after creation
client.update_demographics(session_id, age=42, sex="female")

# Submit feedback on diagnosis accuracy
client.submit_feedback(session_id, "correct", notes="Confirmed by specialist")
```

## Patient Profiles

```python
# Create/update patient profile (data persists across sessions)
client.update_patient("patient-123",
    chronic_conditions=["diabetes", "hypertension"],
    current_medications=["metformin", "lisinopril"],
    allergies=["penicillin"],
)

# Get patient profile
profile = client.get_patient("patient-123")

# Get aggregated patient epicrisis (summary of all sessions)
summary = client.get_patient_summary("patient-123")

# List patient's sessions
sessions = client.list_patient_sessions("patient-123")
```

## Admin & Billing

```python
# Token balance
balance = client.get_balance()
print(f"Remaining: {balance.balance} tokens ({balance.tier} tier)")

# Usage stats
usage = client.get_usage(days=30)
print(f"Used: {usage.total_tokens} tokens")

# Rate limit status
rl = client.get_rate_limit()
print(f"Requests: {rl['requests_used']}/{rl['rate_limit_rpm']} RPM")

# Tenant statistics
stats = client.get_stats()

# API key management
keys = client.list_keys()
new_key = client.create_key("production-server")
client.revoke_key(key_id)
```

## Webhook Verification

```python
from nessyapi_sdk import verify_webhook_signature

# In your webhook handler (Flask example):
@app.post("/webhooks/nessy")
def handle_webhook():
    is_valid = verify_webhook_signature(
        payload=request.data,
        secret="whsec_...",
        signature=request.headers["X-NessyAPI-Signature-256"],
        timestamp=request.headers["X-NessyAPI-Timestamp"],  # always pass this
    )
    if not is_valid:
        abort(403)
    
    event = request.json
    if event["event_type"] == "assessment.completed":
        # Process completed assessment
        print(f"Triage: {event['data']['triage_level']}")
```

## Error Handling

```python
from nessyapi_sdk import NessyClient, NessyAPIError

try:
    results = client.finalize("invalid-session-id")
except NessyAPIError as e:
    print(e.status)    # 404
    print(e.detail)    # "Session not found"
    print(e.error_code)  # "not_found"
```

## Token Costs

| Method | Cost |
|--------|------|
| `create_session()` | 1 token |
| `route()` | 5 tokens |
| `answer()` | 8 tokens |
| `answer(skip=True)` | 0 tokens |
| `get_results()` | FREE |
| `get_state()` | FREE |
| `finalize()` | FREE |
| `get_questions()` | FREE |
| All admin/patient methods | FREE |

## Features

- Sync and async clients
- Typed response models (dataclasses with `.raw` dict access)
- Automatic retry with exponential backoff (429, 5xx)
- HTTPS enforcement
- ID validation (prevents path traversal)
- Webhook signature verification with replay protection

## Links

- [Contact](mailto:dev@healthyness.cz)
- [PyPI](https://pypi.org/project/nessyapi-sdk/)
