OpenAnchor
LLM token-attribution middleware: capture, attribute, and observe every LLM call — with real OpenTelemetry export and real semantic caching.
OpenAnchor sits alongside your LLM calls (via a LangChain middleware today, or by calling its collector API directly from any provider) and captures token consumption events, breaks them down across 6 dimensions (phase, operation type, prompt template, session, model, and pattern), and exposes that data through a query API. It also ships real OTEL span export and a real semantic-caching layer, both backed by actual embeddings and storage — not mocked stand-ins.
30-Second Start
from openanchor import TokenCollector, Analytics, AttributionModel
collector = TokenCollector()
collector.set_session("session_1")
collector.capture_event(
call_id="call_1",
model="gpt-4",
provider="openai",
input_tokens=120,
output_tokens=45,
)
attribution = AttributionModel(collector.store)
analytics = Analytics(collector, attribution)
summary = analytics.get_summary("session_1")
print(summary["total_tokens"], summary["by_operation"])
Or wrap a LangChain runnable directly:
from openanchor.middleware.langchain import OpenAnchorMiddleware
middleware = OpenAnchorMiddleware(project_name="my_app")
wrapped_chain = middleware(my_langchain_runnable)
result = wrapped_chain.invoke({"model": "gpt-4", "prompt": "..."})
print(middleware.get_session_stats())
See examples/basic_usage.py and examples/mcp_openanchor.py for full
runnable examples.
What's actually implemented
| Capability | Status |
|---|---|
| Token capture + 6D attribution (phase/operation/prompt/session/model) | Real, tested |
| In-memory + SQLite event storage (indexed, WAL, connection-reused) | Real, tested |
LangChain middleware (OpenAnchorMiddleware, WrappedRunnable) |
Real, tested |
OpenTelemetry span export (openanchor.otel) |
Real, tested — see below |
Semantic caching (openanchor.semantic_cache, 12 MCP tools) |
Real, tested — see below |
Cost governance / token profiles / optimization tracking (okf_*) |
Real, tested |
| Docker image with a working entry point | Real (python -m openanchor) |
Observability (OpenTelemetry)
Every TokenCollector.capture_event call is wrapped in a real OTEL span
(token counts, model, provider, operation type, and cost-if-provided as
span attributes) — not a mocked stand-in. Tracing is off by default
(the collector's hot path shouldn't pay tracing overhead or make network
calls unless asked to):
from openanchor import configure_tracing
configure_tracing(exporter="console") # safe default, no network calls
# or: configure_tracing(exporter="otlp", otlp_endpoint="http://localhost:4318/v1/traces")
Full details, env-var-only configuration, and the span attribute reference:
OTEL_SETUP_GUIDE.md. Tests use OTEL's in-memory
span exporter (tests/test_otel.py) to verify real spans are produced.
Semantic caching
openanchor.semantic_cache implements real embedding-based caching:
- Embeddings: uses a local Ollama server (
nomic-embed-textor similar) when reachable atlocalhost:11434, and automatically falls back to a deterministic, dependency-free feature-hashing embedder when it isn't — so the cache always works, online or offline. - Storage: SQLite-backed (
SemanticCacheStore), storing embeddings + responses + real lookup history (for real hit-rate stats, not hardcoded numbers). - Lookup: real cosine-similarity search, not string matching.
from openanchor import SemanticCache
from openanchor._mcp_tools import OpenAnchorMCPHandler
cache = SemanticCache(cache_db_path="cache.db")
handler = OpenAnchorMCPHandler(cache)
await handler.cache_prompt_embedding("Summarize this report", response="...")
matches = await handler.find_cached_similar("Summarize this report for me")
All 12 MCP tools (cache_prompt_embedding, find_cached_similar,
analyze_cache_hit_rate, get_cache_statistics, etc) compute real numbers
from actual cache contents and lookup history — see
openanchor/_mcp_tools.py. Tests: tests/test_semantic_cache.py,
tests/test_mcp_tools.py.
MCP connector security
If you expose these tools over a network port via
SemanticCache.start_mcp_connector(), the defaults are deliberately
locked down: binds to 127.0.0.1 (not 0.0.0.0), no CORS origins allowed
(not *), and least-privilege read-only permissions (not wildcard
actions/roles). Widening any of that requires explicit opt-in — see
openanchor/_mcp_connector.py and PRODUCTION_DEPLOYMENT.md.
Privacy
OpenAnchor's job is intercepting and storing metadata about LLM calls, so its privacy posture matters:
- Raw prompt/response text capture is off by default. The LangChain
middleware's
WrappedRunnable.invoke()records only a SHA-256 hash and length of the input/output by default — never the actual text — unless you constructOpenAnchorMiddleware(capture_raw_content=True). - Opt-in captures are redacted by default. When raw capture is
enabled, excerpts are run through best-effort PII/secret redaction
(emails, phone numbers, API keys, credit-card-like numbers, SSNs) before
being stored, unless you explicitly disable that with
redact_captured_content=False. - Retention/TTL.
SqliteEventStore(retention_days=N)automatically purges events older than the retention window (in addition to the existing manual.clear()), so persisted call data doesn't accumulate indefinitely once a retention policy is configured.
from openanchor import SqliteEventStore
from openanchor.middleware.langchain import OpenAnchorMiddleware
store = SqliteEventStore("events.db", retention_days=30)
middleware = OpenAnchorMiddleware(
store=store,
capture_raw_content=False, # default; only hash+length stored
# capture_raw_content=True, # opt in to store excerpts
# redact_captured_content=True, # default when opted in
)
See openanchor/privacy.py and tests/test_privacy.py.
Installation
pip install openanchor
# with OTLP exporter support:
pip install "openanchor[otel]"
Docker
docker build -t openanchor .
docker run -p 8080:8080 openanchor
curl http://localhost:8080/health
See PRODUCTION_DEPLOYMENT.md for details.
Documentation
Testing
pip install -e ".[dev]"
pytest tests/ -v
ruff check .
mypy openanchor/
bandit --ini .bandit -r openanchor/
248 tests across 14 test files, covering the collector/attribution/analytics
core, the LangChain middleware (including the token-capture hot path),
SQLite and in-memory storage, OTEL span export, semantic caching, the MCP
connector's security defaults, the OKF cost-governance/token-profile/
optimization-tracking modules, the __main__ CLI entry point, the 6D
attribution analyzer, and the federated-learning / multi-agent-optimization
/ model-evolution modules.
License
Proprietary License — free to use with explicit attribution. See LICENSE.
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 openanchor-0.6.1.tar.gz.
File metadata
- Download URL: openanchor-0.6.1.tar.gz
- Upload date:
- Size: 72.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.16
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d81db59cb36ce0c1e5164a72009cfb5e028e4ecb8537ae9bc693d23c27583d8f
|
|
| MD5 |
2167a08510a71397ed8453faf1f9f437
|
|
| BLAKE2b-256 |
84203f82a7b2f8c4d213703d4caa52aeac814d846a01359f09c643991a51e336
|
File details
Details for the file openanchor-0.6.1-py3-none-any.whl.
File metadata
- Download URL: openanchor-0.6.1-py3-none-any.whl
- Upload date:
- Size: 57.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.16
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9ae7ba10c799907c0d1ca0b08d4e91462f6e9a24621943fa36c6c9e6207615a
|
|
| MD5 |
63ec9c6f167f72bc98db5de81285488f
|
|
| BLAKE2b-256 |
b074f9933290855a7235f9d6104748766445c8cf8eec26062ee7702047bc4ae6
|