Metadata-Version: 2.4
Name: uais-wai
Version: 1.0.1
Summary: WAI (Watcher AI) Python SDK — fire-and-forget event ingest, diagnose, trace for UAIS ecosystem apps
Project-URL: Homepage, https://github.com/mehmetulutug/uais-wai
Project-URL: Repository, https://github.com/mehmetulutug/uais-wai
Project-URL: Documentation, https://github.com/mehmetulutug/uais-wai/tree/main/docs
Author: UAIS / WAI Team
License: Apache-2.0
License-File: LICENSE
Keywords: diagnose,observability,tracing,uais,wai
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.12
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.6
Provides-Extra: dev
Requires-Dist: fastapi>=0.110; extra == 'dev'
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Requires-Dist: sqlalchemy>=2.0; extra == 'dev'
Requires-Dist: starlette>=0.36; extra == 'dev'
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.110; extra == 'fastapi'
Requires-Dist: starlette>=0.36; extra == 'fastapi'
Provides-Extra: sqlalchemy
Requires-Dist: sqlalchemy>=2.0; extra == 'sqlalchemy'
Description-Content-Type: text/markdown

# `uais-wai` — WAI Python SDK

Python client for the WAI (Watcher AI) observability + intervention platform.

> Hatayı ben yönetirim, hata beni değil.

## Install

`uais-wai` is published to UAIS' internal Gitea package registry, **not**
public PyPI — a plain `pip install uais-wai` will fail (no matching
distribution / 401). You need **both** the internal index (for `uais-wai`
itself) **and** public PyPI (for its transitive dependencies, e.g. `httpx`,
`pydantic`) — `--extra-index-url` is required, a single `--index-url` alone
is not enough:

```bash
pip install \
  --index-url "https://admin:${GITEA_PKG_READ_TOKEN}@gitea.uais.ai/api/packages/uais/pypi/simple/" \
  --extra-index-url "https://pypi.org/simple/" \
  uais-wai

# with FastAPI adapter
pip install ... 'uais-wai[fastapi]'
# with SQLAlchemy adapter
pip install ... 'uais-wai[sqlalchemy]'
```

`GITEA_PKG_READ_TOKEN` is the read-only package-registry token distributed to
sibling projects (source of truth: `uais-platform/platform-config.md`) — it
is **not** a per-repo git credential, only a package-pull token. For a CI
pipeline, put the same two flags in `pip.conf`:

```ini
[global]
index-url = https://admin:${GITEA_PKG_READ_TOKEN}@gitea.uais.ai/api/packages/uais/pypi/simple/
extra-index-url = https://pypi.org/simple/
```

Python 3.12+.

## Quickstart

```python
import os
from uais_wai import WAIClient

wai = WAIClient(
    endpoint="https://wai.uais.ai",
    tenant_id="example",                     # app_uid bound to your tenant
    api_key=os.environ["WAI_API_KEY"],
    environment=os.getenv("WAI_ENV", "production"),
    app_version=os.getenv("APP_VERSION"),
    # SLA tuning (defaults shown)
    diagnose_timeout_ms=3000,
    diagnose_default_action="escalate",      # what to return on diagnose timeout
    local_buffer_size=1000,
    flush_interval_ms=500,
)

# Fire-and-forget event (returns immediately; batched in the background).
await wai.event(
    source="db",
    severity="error",
    category="db.query.slow",
    payload={"reason": "lock_timeout", "query_ms": 5200},
    context={"workspace_id": "ws-1"},
)

# Fire-and-forget metric.
await wai.metric("llm.latency_ms", 1230.5, unit="ms",
                 labels={"provider": "openrouter", "model": "anthropic/claude-sonnet-4.5"})

# Blocking diagnose (bounded by diagnose_timeout_ms).
try:
    do_thing()
except Exception as exc:
    diagnosis = await wai.diagnose(error=exc, context={"workspace_id": "ws-1", "operation": "db_write"})
    if diagnosis.timed_out:
        log.warning("WAI slow; using local default: %s", diagnosis.action)
    if diagnosis.action == "retry":
        retry_with(diagnosis.payload.get("backoff_ms", 500))
    elif diagnosis.action == "fallback":
        run_fallback(diagnosis.payload)
    else:
        escalate(diagnosis)
    await wai.diagnose_outcome(diagnosis.id, success=True)
```

## Tracing

```python
async with wai.trace("agent_turn", attributes={"role": "architect"}) as span:
    span.set_attribute("retries", 0)
    response = await llm.complete(...)
    span.event("llm_call", attributes={"model": "claude"})
```

Spans nest automatically — `wai.event()` calls inside a `trace()` block
inherit the trace + span IDs.

## FastAPI

```python
from uais_wai.fastapi import WAIMiddleware
app.add_middleware(WAIMiddleware, client=wai, slow_threshold_ms=1000)
```

Emits per-request `http.latency_ms` metric, plus events on 5xx, slow
requests, and unhandled exceptions. Each request opens a trace so handler
events get the same `trace_id`.

## SQLAlchemy

```python
from sqlalchemy import create_engine
from uais_wai.sqlalchemy import attach_wai

engine = create_engine(DSN)
attach_wai(engine, wai, slow_query_ms=500)
```

Emits `db.query.slow`, `db.deadlock.detected`, `db.pool.exhausted`,
`db.connection.failed`. Works with sync + async engines.

## Behavior guarantees

| Concern | What the SDK does |
|---|---|
| Backend down | Events buffer locally; client never crashes |
| Buffer full | Drop newest; emit `wai.ratelimit.local_buffer_full` when reconnected |
| 429 rate limit | Honors `Retry-After` + `X-RateLimit-*`, exponential backoff |
| Diagnose timeout | Returns `Diagnosis(timed_out=True, action=diagnose_default_action)` + fires `wai.diagnose.timeout` event |
| Network partition | Auto-reconnect on next event |
| PII | Stack traces + payloads scrubbed for UUIDs, secrets, emails before send |

## Shutdown

```python
await wai.close()           # flushes buffer + closes connections
# or
async with WAIClient(...) as wai:
    ...                     # auto-close on exit
```

## License

Apache-2.0.
