Metadata-Version: 2.4
Name: ci1t-sdk
Version: 0.2.2
Summary: Python SDK for CI-1T -- real-time stability monitoring for any numeric signal
Author-email: Collapse Index <alex@collapseindex.org>
License-Expression: MIT
Project-URL: Homepage, https://collapseindex.org
Project-URL: Documentation, https://github.com/collapseindex/ci1t-sdk
Project-URL: Repository, https://github.com/collapseindex/ci1t-sdk
Project-URL: Issues, https://github.com/collapseindex/ci1t-sdk/issues
Keywords: ai,monitoring,stability,llm,ml,ci1t,collapse-index
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
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 :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.25.0
Dynamic: license-file

# CI-1T Python SDK v0.2.2

**Version:** 0.2.2
**Last Updated:** March 2026

Real-time stability monitoring for any numeric signal. One dependency. Three lines to start.

## Install

```bash
pip install ci1t-sdk
```

## Quick Start

```python
import ci1t

client = ci1t.Client()  # reads CI1T_API_KEY env var
result = client.evaluate([0.8, 0.85, 0.79])

for ep in result.episodes:
    print(f"CI: {ep.ci:.3f}  AL: {ep.al}  Status: {ep.status}")
```

## What It Does

CI-1T measures signal stability over time. You send scores (confidence values, probabilities, sensor readings, any 0-1 signal), and CI-1T tells you:

- **Collapse Index (CI)** -- how stable your signal is (0 = rock solid, 1 = collapsing)
- **Authority Level (AL)** -- how much to trust the source right now (0 = full trust, 4 = override it)
- **Ghost Detection** -- flags sources that look stable but are silently wrong

## API Key

Get one at [collapseindex.org/dashboard](https://collapseindex.org/dashboard) (API Keys panel).

Set it as an env var:

```bash
export CI1T_API_KEY=ci_your_key_here
```

Or pass it directly:

```python
client = ci1t.Client(api_key="ci_your_key_here")
```

## Configuration

### Client

| Parameter | Env var | Default | Description |
|-----------|---------|---------|-------------|
| `api_key` | `CI1T_API_KEY` | (required) | Your CI-1T API key |
| `base_url` | `CI1T_API_URL` | `https://collapseindex.org/api` | API base URL |
| `timeout` | -- | `30.0` | Request timeout in seconds |

```python
client = ci1t.Client(
    api_key="ci_...",
    base_url="https://collapseindex.org/api",
    timeout=30.0,
)
```

### Engine Config

Pass a `config` dict to `evaluate()` or `fleet_evaluate()` to override engine defaults. Only include the fields you want to change.

```python
result = client.evaluate(
    [0.8, 0.85, 0.79, 0.92, 0.88, 0.91],
    config={
        "n": 3,                # scores per episode (default 3, min 2)
        "ghost_enabled": True,  # ghost detection (default True)
        "th1": 4000,           # AL 0/1 boundary (Q0.16)
        "th2": 10000,          # AL 1/2 boundary
        "th3": 20000,          # AL 2/3 boundary
        "th4": 30000,          # AL 3/4 boundary
    },
)
```

For fleet endpoints, wrap engine config inside `engine_config`:

```python
result = client.fleet_evaluate(
    {"gpt-4o": [0.9, 0.88, 0.91], "claude": [0.87, 0.85, 0.86]},
    config={"engine_config": {"n": 3, "th1": 4000, "th4": 30000}},
)
```

All threshold values are Q0.16 integers (0-65535). Use `ci1t.to_q16()` to convert from floats.

### Monitor

| Parameter | Default | Description |
|-----------|---------|-------------|
| `name` | (required) | Session name (for your reference) |
| `nodes` | (required) | List of node names |
| `api_key` | `CI1T_API_KEY` | API key (passed to underlying Client) |
| `base_url` | `CI1T_API_URL` | API base URL override |
| `episode_size` | `3` | Scores per episode per node before auto-flush |
| `on_drift` | `None` | Callback: `(node_name, snapshot) -> None` |
| `on_ghost` | `None` | Callback: `(node_name, snapshot) -> None` |
| `on_round` | `None` | Callback: `(result: RoundResult) -> None` |

## Usage

### Evaluate Scores

Send prediction scores and get stability results. Scores are grouped into episodes of 3.

```python
import ci1t

client = ci1t.Client()

# Accepts floats (0.0-1.0) -- auto-converted to Q0.16
result = client.evaluate([0.8, 0.85, 0.79, 0.92, 0.88, 0.91])

for ep in result.episodes:
    print(f"CI: {ep.ci:.3f}  Status: {ep.status}  AL: {ep.al} ({ep.al_label})")
    if ep.warn:
        print("  Warning: stability threshold crossed")
    if ep.ghost_confirmed:
        print("  Ghost confirmed!")
```

### Fleet Monitoring (Stateless)

Compare multiple models in a single call. Ghost detection flags models with unnaturally low variance.

```python
result = client.fleet_evaluate({
    "gpt-4o":  [0.9, 0.88, 0.91],
    "claude":  [0.87, 0.85, 0.86],
    "llama":   [0.50, 0.50, 0.50],  # suspiciously stable
})

print(f"Ghosts: {result.snapshot.ghost_confirmed_count}")
for node in result.snapshot.nodes:
    print(f"  CI: {node.ci:.3f}  Ghost: {node.ghost_confirmed}")
```

### Fleet Sessions (Persistent)

For ongoing monitoring, use sessions. They track state across rounds.

```python
# Create session
info = client.session_create(node_count=2, node_names=["gpt-4o", "claude"])

# Push rounds over time
r1 = client.session_round(info.session_id, {
    "gpt-4o": [0.9, 0.88, 0.91],
    "claude":  [0.87, 0.85, 0.86],
})
print(f"Round {r1.round}: {r1.snapshot.ghost_suspect_count} suspects")

# Check state anytime
state = client.session_state(info.session_id)

# Clean up
client.session_delete(info.session_id)
```

### Monitor (Auto-Session)

`Monitor` handles session lifecycle automatically. Buffer scores and it flushes when ready.

```python
import ci1t

with ci1t.Monitor("my-fleet", nodes=["gpt-4o", "claude"]) as mon:
    # Push one score at a time -- buffers until episode_size (3) is reached
    mon.push({"gpt-4o": 0.92, "claude": 0.87})
    mon.push({"gpt-4o": 0.89, "claude": 0.85})
    mon.push({"gpt-4o": 0.91, "claude": 0.86})
    # ^ Third push triggers a round to CI-1T

    state = mon.state()
    print(f"Session: {mon.session_id}")
# Session auto-deleted on exit
```

### Callbacks

Get notified when stability events happen:

```python
def on_drift(node_name, snapshot):
    print(f"DRIFT: {node_name} is no longer stable")

def on_ghost(node_name, snapshot):
    print(f"GHOST: {node_name} confirmed as ghost!")

monitor = ci1t.Monitor(
    "my-fleet",
    nodes=["gpt-4o", "claude"],
    on_drift=on_drift,
    on_ghost=on_ghost,
)
```

### Watch Decorator

Auto-track any function that returns a prediction score:

```python
import ci1t

@ci1t.watch("my-classifier")
def predict(text: str) -> float:
    return model.predict_proba(text)[1]

# Every call to predict() buffers the returned score.
# Every 3 calls, scores are pushed to CI-1T automatically.
score = predict("hello world")  # returns normally, tracked in background
```

Access the underlying monitor:

```python
predict.monitor.state()   # check current stability
predict.monitor.close()   # stop tracking
```

## Response Types

All responses are typed dataclasses with convenience properties:

| Type | Key Properties |
|------|---------------|
| `Episode` | `.ci`, `.ci_ema`, `.al`, `.status`, `.stable`, `.warn`, `.ghost_confirmed` |
| `EvaluateResult` | `.episodes`, `.compute_ns`, `.credits_remaining` |
| `FleetNode` | `.ci`, `.al`, `.status`, `.ghost_suspect`, `.ghost_confirmed` |
| `FleetSnapshot` | `.nodes`, `.has_ghosts`, `.ghost_confirmed_count` |
| `RoundResult` | `.snapshot`, `.round`, `.compute_ns` |

CI values are normalized floats (0.0-1.0). Raw Q0.16 integers available via `.ci_raw`.

## Score Format

CI-1T uses Q0.16 fixed-point internally (integers 0-65535). The SDK accepts both:

- **Floats** (0.0-1.0) -- auto-converted. Use this.
- **Integers** (0-65535) -- passed through as-is.

Manual conversion: `ci1t.to_q16(0.85)` returns `55704`.

## Thresholds

| CI Range | Status | Meaning |
|----------|--------|---------|
| 0.00 - 0.15 | Stable | Predictions are consistent |
| 0.15 - 0.45 | Drifting | Starting to wander |
| 0.45 - 0.70 | Unstable | Significant instability |
| 0.70 - 1.00 | Collapsing | Signal is failing |

Authority levels: AL0 (full trust) through AL4 (no authority/override).

## Logging

The SDK logs every API call via Python's standard `logging` module under the `ci1t` logger. No output by default.

Enable it:

```python
import logging

# See all CI-1T SDK logs
logging.getLogger("ci1t").setLevel(logging.DEBUG)
logging.basicConfig()
```

Log levels:

| Level | What's logged |
|-------|---------------|
| `DEBUG` | Request method, endpoint, payload size before each call |
| `INFO` | Results summary after each call (episode count, ghost count, latency, credits) |
| `WARNING` | Callback failures, flush errors on close |

## Requirements

- Python 3.10+
- httpx >= 0.25.0

# Links

- https://pypi.org/project/ci1t-sdk/
- https://collapseindex.org

## Changelog

### v0.2.2 (2026-03)
- Fix README version sync with PyPI

### v0.2.1 (2026-03)
- Ship LICENSE file in wheel (was 0 bytes in v0.2.0)
- Add py.typed marker for PEP 561 type checker support

### v0.2.0 (2026-03)
- Added structured logging to Client (DEBUG: request details, INFO: results/timing)
- Monitor and watch() already log via the `ci1t` logger
- Updated wording: "any numeric signal" instead of "AI systems" throughout
- Added Configuration section: Client params, engine config, Monitor params

### v0.1.0 (2025-07)
- Initial release
- Client with evaluate, fleet_evaluate, session management
- Monitor class with auto-buffering and callbacks
- watch() decorator for function-level tracking
- Typed response dataclasses with float/Q0.16 dual access
- Auto Q0.16 conversion (accept floats, convert internally)

## License

MIT + Commons Clause. Free to use, modify, and integrate. Cannot be resold as a standalone product. See [LICENSE](LICENSE).
