Run-scoped LLM observability control plane backed by LiteLLM
Project description
LLM Observer Proxy
LLM Observer Proxy runs a temporary LiteLLM data plane for each evaluation or agent run and stores its observable LLM traffic under a stable run_id. It records requests, responses, streaming output, tool calls, token usage, cost estimates, errors, and provider-visible reasoning content.
It is independently installable and can be embedded into any evaluation platform, agent runner, or orchestration service through its HTTP API.
How It Works
There are two HTTP layers:
- The control plane listens on port
8790by default. An orchestrator uses it to create, inspect, and stop run-scoped proxies. - Each data plane listens on a separate, automatically allocated port. An agent uses the returned URL as an OpenAI-compatible or Anthropic-compatible LLM endpoint.
One control-plane process can manage multiple concurrent data planes. Stopping a data plane preserves its history on disk, and history remains readable after the control plane restarts.
The observer can only record information exposed by the upstream API. It cannot obtain hidden chain-of-thought that a provider does not return.
Requirements
- Python 3.11, 3.12, or 3.13
- A provider API key or a custom LiteLLM configuration
- Local permission to start subprocesses and bind data-plane ports
Install
Install the command in the current Python environment:
python -m pip install llm-observer-proxy
Or install it as an isolated command with uv:
uv tool install llm-observer-proxy
Run it directly without a permanent installation:
OPENAI_API_KEY=your-openai-api-key uvx llm-observer-proxy
Because the distribution name and console command are both llm-observer-proxy, uvx resolves the package and starts the server in one command. It caches the isolated environment for subsequent runs.
For a source checkout, install the locked development environment instead:
uv sync --extra dev
LiteLLM is pinned because the observer callback adapts specific response and streaming internals. The package declares the subset of LiteLLM Proxy runtime dependencies needed by the tested LLM data plane rather than installing the full litellm[proxy] extra.
Quick Start
The bundled catalog includes gpt-5.5 and reads the OpenAI credential from the environment:
export OPENAI_API_KEY=your-openai-api-key
llm-observer-proxy
In another terminal, check the control plane:
curl http://127.0.0.1:8790/healthz
Create a data plane for a run:
curl -X POST http://127.0.0.1:8790/api/runs/run-001/proxy \
-H 'Content-Type: application/json' \
-d '{}'
The data plane serves every model in the selected catalog or custom LiteLLM configuration. Each LLM request selects a route through its own model field. The create response includes a dynamically allocated bare base_url, for example http://127.0.0.1:45678, plus openai_base_url (http://127.0.0.1:45678/v1) and anthropic_base_url (http://127.0.0.1:45678). Use the explicit URL for the client SDK, or call an endpoint directly:
curl http://127.0.0.1:45678/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "gpt-5.5",
"messages": [{"role": "user", "content": "Reply with one short sentence."}]
}'
Read the normalized history:
curl http://127.0.0.1:8790/api/runs/run-001/history
Stop the data plane without deleting its history:
curl -X DELETE http://127.0.0.1:8790/api/runs/run-001/proxy
Configuration
The command accepts these options:
--host HOST Control-plane bind host (default: 127.0.0.1)
--port PORT Control-plane port (default: 8790)
--env-file PATH KEY=VALUE file loaded before startup (default: .env)
--artifact-root PATH Artifact storage root
--model-catalog PATH Model catalog JSON
--log-level LEVEL Python log level
Paths are resolved in this order:
--artifact-rootand--model-catalogLLM_OBSERVER_PROXY_ARTIFACT_ROOTandLLM_OBSERVER_PROXY_MODEL_CATALOG_PATH./artifactsand./config/models.jsonin the working directory- The package's secret-free bundled model catalog
Relative paths are resolved against the process working directory. LITELLM_BIN can select a specific LiteLLM executable.
Model Catalog
A model catalog has this shape:
{
"llm_observer_proxy": {
"base_url": "https://api.openai.com",
"api_key": "os.environ/OPENAI_API_KEY",
"litellm_settings": {
"drop_params": true
}
},
"models": [
{
"id": "gpt-5.5",
"upstream_api": "openai"
}
]
}
id is the public model alias exposed by the data plane. upstream_api selects LiteLLM's openai or anthropic provider adapter. Optional input and output token prices are used for local cost estimates. Other catalog properties, including name and provider, are accepted but ignored. Environment references use LiteLLM's os.environ/NAME syntax, which prevents a literal credential from being written to the generated configuration. See config/models.example.json for a complete example.
For advanced provider routing, pass a server-local LiteLLM YAML path as litellm_config_path in the create request. The observer callback is added to that configuration automatically.
Control API
| Method | Path | Purpose |
|---|---|---|
GET |
/healthz |
Check control-plane health |
GET |
/api/proxies |
List process-local running and archived proxies |
POST |
/api/runs/{run_id}/proxy |
Create a run-scoped data plane |
GET |
/api/runs/{run_id}/proxy |
Read proxy state |
DELETE |
/api/runs/{run_id}/proxy |
Stop a data plane |
GET |
/api/runs/{run_id}/history |
Read normalized history |
GET |
/api/runs/{run_id}/conversation |
Read the latest conversation |
The create payload may be empty. Optional fields are port, bind_host, litellm_config_path, litellm_env, and strip_callbacks. Unknown fields are ignored. The complete request and response contract is documented in docs/control-plane-api.md.
Artifacts
Each run writes:
artifacts/runs/<run_id>/platform/llm/
config.yaml
conversation_snapshot.json
events.jsonl
litellm.log
The generated YAML is retained as a run artifact. Keep provider credentials in environment variables rather than writing literal values into a model catalog or custom YAML.
Supported Scope
The supported data-plane surface is LiteLLM's OpenAI-compatible LLM API and Anthropic /v1/messages, plus the control and history APIs documented above. LiteLLM MCP routes, its administration UI, database persistence, and enterprise gateway features are not part of this project's compatibility contract. In particular, the mcp package is intentionally not installed because MCP routes are not used by the observer and are lazy-loaded by the pinned LiteLLM version.
Troubleshooting
- The control plane starts, but proxy creation returns
502: inspect the returnedlog_pathor the run'slitellm.log. Missing provider variables and invalid custom YAML are the most common causes. - LiteLLM cannot be found: install the package and command in the same environment, or set
LITELLM_BINto an executable path. - Proxy creation returns
409: therun_idalready has a running data plane, a requested port is unavailable, or the selected configuration is invalid. - History is empty: confirm the agent is using the returned data-plane URL, not the provider URL directly.
Security
The control API is an administrative interface: it can start subprocesses, select listening ports, pass child environment values, and read server-local LiteLLM configurations. Version 0.1.x has no built-in authentication. Bind it to loopback or a trusted private network and read .github/SECURITY.md before remote deployment.
History can contain prompts, model output, tool output, and credentials. Apply appropriate filesystem permissions, access control, and retention policies to the artifact root.
Development
uv sync --extra dev
uv run pytest
LLM_OBSERVER_PROXY_RUN_INTEGRATION=1 uv run pytest tests/test_integration_proxy.py
uv build
uvx twine check dist/*
License
Apache-2.0. See LICENSE and NOTICE.
Authors
- Shuqiao Zhang stevenjoezhang@gmail.com
- Yue Xiong xxyyyue@gmail.com
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file llm_observer_proxy-0.1.0.tar.gz.
File metadata
- Download URL: llm_observer_proxy-0.1.0.tar.gz
- Upload date:
- Size: 43.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
44c9231326a1e586460705d9bf070b0822f4e7a9c9c8f4ad282547e35deedc63
|
|
| MD5 |
8606dd650e07e0d3dbeed136382eb632
|
|
| BLAKE2b-256 |
b9ef493ab06ca4c49a4941c1db54d607c239504fc7f835ae30b43d39486170b2
|
Provenance
The following attestation bundles were made for llm_observer_proxy-0.1.0.tar.gz:
Publisher:
publish.yml on xxyyue/llm-observer-proxy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
llm_observer_proxy-0.1.0.tar.gz -
Subject digest:
44c9231326a1e586460705d9bf070b0822f4e7a9c9c8f4ad282547e35deedc63 - Sigstore transparency entry: 2138572197
- Sigstore integration time:
-
Permalink:
xxyyue/llm-observer-proxy@6e8973c4c10ef021243feb168b6c2a0608b703d5 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/xxyyue
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6e8973c4c10ef021243feb168b6c2a0608b703d5 -
Trigger Event:
release
-
Statement type:
File details
Details for the file llm_observer_proxy-0.1.0-py3-none-any.whl.
File metadata
- Download URL: llm_observer_proxy-0.1.0-py3-none-any.whl
- Upload date:
- Size: 31.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ea51b7244baa572b71f47b6a50adde7b2602995a7741242927222ee602c6f7ed
|
|
| MD5 |
103db5e5174c6f66b1b39640218dc059
|
|
| BLAKE2b-256 |
8e1571bc4ae8d70a3cfc269146f18ab7c6ab6d739aa6f1f86d101e2303072e8e
|
Provenance
The following attestation bundles were made for llm_observer_proxy-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on xxyyue/llm-observer-proxy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
llm_observer_proxy-0.1.0-py3-none-any.whl -
Subject digest:
ea51b7244baa572b71f47b6a50adde7b2602995a7741242927222ee602c6f7ed - Sigstore transparency entry: 2138572213
- Sigstore integration time:
-
Permalink:
xxyyue/llm-observer-proxy@6e8973c4c10ef021243feb168b6c2a0608b703d5 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/xxyyue
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6e8973c4c10ef021243feb168b6c2a0608b703d5 -
Trigger Event:
release
-
Statement type: