Metadata-Version: 2.5
Name: yesil-health-ai-api
Version: 0.5.3
Summary: Official Python client for the Yesil Health API: evidence-grounded health chat with modules, institution knowledge and PDF processing, streamed as typed SSE.
Project-URL: Homepage, https://yesilhealth.com
Author: Yesil Health
License: MIT
License-File: LICENSE
Keywords: ai,b2b,chat,evidence,health,healthcare,medical,sse,yesil
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24
Description-Content-Type: text/markdown

# yesil-health-ai-api (Python)

Official Python client for the **Yesil Health API** (B2B).

**What the API gives you:** streaming health answers grounded in scientific
evidence and in your institution's own content. Answers come with inline citations,
structured proposals your system applies (reminders, measurements), memory
records to store, appointment/service links, and billing on every turn.

### Endpoints

Point the client at the chat and document endpoints you use:

| Option | Endpoint |
|---|---|
| `chat_endpoint` | `/api/v2/chat/stream/compat` (classic `delta`/`citation` events plus module events) or `/api/v2/chat/stream` (native: final answer, cards, state proposals) |
| `document_endpoint` | `/api/v2/documents/process` (structured lab rows, quality metadata, `context_block`) |
| `evidence_endpoint` | `/api/v2/evidence/search` (ranked sources with relevance, no AI answer) |

The router picks the modules each message needs (memory, measurements, reminders,
scientific evidence, institution knowledge, appointment handoff and service
matching) and returns them as additive events. Unknown events stay safe to
ignore. The full reference is the API guide in the Yesil Health console.

Synchronous, built on [httpx](https://www.python-httpx.org/). The hard part of
integrating, parsing the typed Server-Sent Events stream, is handled for you.
Mirror of the TypeScript client `@yesilsci/health-ai-api`.

## Install

```bash
pip install yesil-health-ai-api
```

## Quick start

```python
import uuid
from yesil_health import YesilHealthClient

client = YesilHealthClient(
    base_url="https://api.yesilhealth.com",
    api_key="...",  # sent as X-API-Key
    chat_endpoint="/api/v2/chat/stream/compat",
)

for ev in client.chat_stream({
    "question": "Is metformin safe for a patient with stage 3 CKD?",
    "request_id": str(uuid.uuid4()),       # idempotent billing — recommended
    "demographics": {"age": 62, "sex": "female"},
    "health_context": "eGFR 45 mL/min/1.73m². On lisinopril 10mg daily.",
}):
    if ev["type"] == "delta":
        print(ev["content"], end="", flush=True)
```

One-shot (no live rendering):

```python
result = client.chat({"question": "..."})
print(result["text"])
```

The client is a context manager:

```python
with YesilHealthClient(base_url=..., api_key=...) as client:
    ...
```

## Authentication

Every request is authenticated with your enterprise API key via the `X-API-Key`
header. Keep it server-side; never ship it in a client app.

## The event stream

`chat_stream()` yields event dicts. Route on `event["type"]`:

| `type`             | Payload                              | Notes |
|--------------------|--------------------------------------|-------|
| `meta`             | `phase`, `conversation_id`, research preview | Lifecycle + mid-stream research previews. |
| `delta`            | `content`                            | Append to build the answer text. |
| `citation`         | citation fields                      | A literature citation. |
| `memory_extracted` | `signals`                            | Records to store; send them back in `memory`. |
| `state_patch` | `status`, `operations` | Native action proposals; apply in your system and deduplicate by operation `id`. |
| `reminder_action` / `measurements_extracted` | `status`, `operation_id`, `action` | Compat record proposals; apply once per `operation_id`. |
| `handoff_recommendation` | `status`, `action` | Compat: verified institution link; no booking or live transfer. |
| `service_recommendation` | `status`, `action` | Compat: approved catalog selection; no purchase or booking. |
| `answer` | `markdown`, `blocks`, `follow_up`, `references` | Native V2 final snapshot of the deltas already streamed. |
| `card` | `card_type`, `payload`, `markdown_fallback` | Native V2 structured card, normalized by this SDK. |
| `done`             | `summary`, `billing`, `usage`         | Terminal event. V2 `billing.cost_usd` is the final customer charge; `usage.worker_summary` contains content-free worker outcomes. |
| `error`            | `message`, `code`                    | Terminal failure event. |

> **Forward compatibility.** The API only ever *adds* fields and event types
> within a contract version. Always ignore unknown `type` values — this client
> yields them as plain dicts rather than raising.

## Errors

Non-2xx responses raise `YesilApiError` with `status`, `code` (server
`error_code`), and message:

```python
from yesil_health import YesilApiError

try:
    client.chat({"question": "..."})
except YesilApiError as e:
    if e.code == "RATE_LIMITED":  # 429
        ...
    if e.status == 402:           # quota exhausted / billing
        ...
```

## Evidence search

Ranked literature retrieval for a query, no LLM answer synthesis. Requires the
`evidence:read` scope on your API key.

```python
res = client.evidence_search({
    "question": "omega-3 and cardiovascular risk reduction",
    "needs_trials": False,
})
print(res["papers"])
```

## Account endpoints

```python
client.billing()  # billing status
client.quota()    # quota / rate-limit status
client.usage()    # usage report
```

## Compat and native

**Compat** (`/api/v2/chat/stream/compat`) streams `delta` and `citation` events
with `[n]` markers, plus `reminder_action` / `measurements_extracted` proposals
(apply once per `operation_id`) and `handoff_recommendation` /
`service_recommendation` links. The answer text also contains the link, so don't
render the same call-to-action twice.

**Native** (`/api/v2/chat/stream`) adds `state_patch` proposals, `card` events
and a final `answer` snapshot. Replace the streamed message with the snapshot if
you need it; don't append it as a second message.

Both chat endpoints end each turn with `done.billing` (charged lines, each with
the module and result that triggered it) and `done.usage` (module names,
outcome codes, durations and counts; never patient text or source titles).
Exact rates come from your institution's rate card.

Your system owns patient state: store `memory_extracted` records and applied
proposals, and send the current reminders and relevant context on later turns.
See Conversation state in the console API guide. Document processing returns a
`context_block` to send as `health_context`; unusable results are not charged.

```python
client = YesilHealthClient(
    base_url="https://api.yesilhealth.com",
    api_key="...",
    chat_endpoint="/api/v2/chat/stream",
    document_endpoint="/api/v2/documents/process",
)

doc = client.parse_document(open("lab.pdf", "rb"), document_hint="auto")
# Keep doc["context_block"] in your own patient context and send it as health_context.
```

Full reference: the API guide in the Yesil Health console (API Docs).
