Metadata-Version: 2.4
Name: llmwho
Version: 0.4.0
Summary: Passive LLM endpoint identity and stability instrumentation
Project-URL: Homepage, https://github.com/tcztzy/llmwho
Project-URL: Repository, https://github.com/tcztzy/llmwho
Project-URL: Issues, https://github.com/tcztzy/llmwho/issues
Author: LLMWho contributors
License-Expression: MIT
License-File: LICENSE
Keywords: fingerprinting,llm,middleware,observability,stability
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.10
Provides-Extra: test
Requires-Dist: httpx>=0.24; extra == 'test'
Requires-Dist: requests>=2.28; extra == 'test'
Description-Content-Type: text/markdown

# llmwho

Passive LLM endpoint identity and stability instrumentation for Python.

```bash
pip install llmwho
```

```python
import llmwho

handle = llmwho.init()
```

Direct Anthropic Messages API calls need no LLMWho-specific wrapper:

```python
import llmwho
from anthropic import Anthropic

llmwho.init()
client = Anthropic()
client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=64,
    messages=[{"role": "user", "content": "Hello"}],
)
```

One call instruments installed HTTPX (sync and async) and requests clients for
recognized LLM routes. It writes content-free observations to
`~/.llmwho/events.jsonl`; it never sends active traffic. Calls are idempotent,
streaming responses are not consumed, and `handle.shutdown()` safely restores
only LLMWho-owned patches.

Send the same observations to a self-hosted Collector without changing request
sites:

```python
handle = llmwho.init(
    collector_url="http://127.0.0.1:7734",
    collector_token="…",
)
```

The remote queue is bounded, non-blocking, and fail-open. Native and OTLP modes
are available; no Collector URL keeps the local JSONL default. Run the service
with `llmwho collector`; deployment and security details are in the
[Collector guide](https://github.com/tcztzy/llmwho/blob/main/docs/COLLECTOR.md).

The CLI also accepts content-free Claude Code and Codex lifecycle events:
`llmwho hook claude-code|codex --event EVENT`. Project configuration and
privacy details are in the
[agent hook guide](https://github.com/tcztzy/llmwho/blob/main/docs/AGENT_HOOKS.md).

Run an explicit OpenAI-compatible compatibility canary:

```python
report = llmwho.probe(
    base_url="https://api.openai.com/v1",
    api_key="…",
    model="gpt-4o-mini",
)
```

Compare explicitly supplied output corpora without network or persistence:

```python
report = llmwho.science.output_affinity_matrix({
    "reference": ["reference answer"],
    "endpoint": ["endpoint answer"],
})
print(report["evidence"]["matrix"][0][1])
```

The symmetric character-trigram distance measures surface style only. It does
not prove model identity, distillation, or capability transfer. Full method and
limits: [Output-affinity matrix](https://github.com/tcztzy/llmwho/blob/main/docs/OUTPUT_AFFINITY.md).

Inspect local history:

```bash
llmwho summary
llmwho dashboard
```

Version 0.4 records the response body's declared `model` field only as a
provider declaration. It never turns that declaration into model identity or
confidence. Identity remains `unknown` unless an independent calibrated
detector supplies evidence. Undocumented model response headers are ignored;
the smoke suite measures capability and does not uniquely identify arbitrary
model weights. Raw prompts, responses, headers, query strings, and credentials
are never stored.

Full documentation, research review, and source:
[github.com/tcztzy/llmwho](https://github.com/tcztzy/llmwho).
