LLM logging for OpenAI, Claude, Openrouter
  • Common Lisp 61.5%
  • Python 33.3%
  • Nix 2.6%
  • Prolog 2.2%
  • Shell 0.3%
  • Other 0.1%
Find a file
nsaspy 1cc9c27002
Some checks failed
subscription-proxy / subscription-proxy (push) Failing after 3s
Analytics API / test (push) Failing after 4s
Analytics API / package (push) Has been skipped
ci: verify subscription proxy and token attribution
2026-09-19 01:43:38 -04:00
.github/workflows ci: verify subscription proxy and token attribution 2026-09-19 01:43:38 -04:00
.opencode/skills docs: add reusable issue and solution knowledge system 2026-09-12 20:25:48 -04:00
.prolog/kb docs: record expert corpus backfill green 2026-09-14 22:34:26 -04:00
evidence docs: record expert corpus backfill green 2026-09-14 22:34:26 -04:00
expert docs: record expert corpus backfill green 2026-09-14 22:34:26 -04:00
llm_log feat: record structured worker and subagent attribution 2026-09-19 01:36:28 -04:00
nix feat: wire expert plane through Home Manager service 2026-08-30 16:12:40 -04:00
proxy test: prove worker attribution headers stay local 2026-09-19 01:37:23 -04:00
research feat: backfill and export expert corpus 2026-09-14 22:29:39 -04:00
roam docs: record expert corpus backfill green 2026-09-14 22:34:26 -04:00
scripts docs: add reusable issue and solution knowledge system 2026-09-12 20:25:48 -04:00
tests test: cover subscription proxy token and worker attribution 2026-09-19 01:36:54 -04:00
.gitignore feat: add provider-neutral token analytics API 2026-09-12 20:19:53 -04:00
AGENTS.md docs: add reusable issue and solution knowledge system 2026-09-12 20:25:48 -04:00
flake.lock build: commit flake lock (nixpkgs + tek9 pin) 2026-08-29 21:37:34 -04:00
flake.nix test: gate subscription proxy token attribution in Nix 2026-09-19 01:43:26 -04:00
idea.org docs: track subscription proxy token attribution 2026-09-19 01:37:56 -04:00
LICENSE Initial commit 2026-08-29 07:12:18 -04:00
pyproject.toml feat: add capture proxy recorder actor and Prolog classifier 2026-08-29 07:18:42 -04:00
README.md feat: add provider-neutral token analytics API 2026-09-12 20:19:53 -04:00

llm-log

Transparent LLM traffic capture for building a durable training corpus and a symbolic Prolog knowledge base.

The first slice is deliberately small: route an LLM client through llm-log, forward the request unchanged, stream the response back immediately, and append the completed exchange to disk.

Documentation

Canonical documentation is Org-mode under research/:

The Common Lisp runtime lives in proxy/; the expert plane in expert/.

Architecture

LLM client
   |
   v
llm-log proxy
   |---------------------------> configured upstream
   |                                OpenAI / OpenRouter / Anthropic / local
   |
   +--> recorder actor --> data/events.jsonl   # lossless corpus
                       +--> data/events.pl      # compact Prolog projection

events.jsonl is the source of truth for future fine-tuning/export. events.pl is the symbolic index used for request classification and later expert-system rules; it intentionally does not duplicate giant prompt/completion blobs.

Authorization, cookie, and API-key header values are forwarded to the upstream but replaced with <redacted> before persistence.

Run

nix develop
python -m pip install -e .
llm-log serve --log-dir ./data

Default upstream prefixes:

Client base URL Upstream
http://127.0.0.1:8787/openai/v1 https://api.openai.com/v1
http://127.0.0.1:8787/openrouter/api/v1 https://openrouter.ai/api/v1
http://127.0.0.1:8787/anthropic https://api.anthropic.com

Keep using the provider's normal API-key mechanism in the client. The proxy does not own or store the key.

Custom/local endpoints are explicit:

llm-log serve \
  --log-dir ./data \
  --upstream ollama=http://127.0.0.1:11434 \
  --upstream vllm=http://127.0.0.1:8000

Then point the client at http://127.0.0.1:8787/ollama/... or http://127.0.0.1:8787/vllm/....

Captured event

Each JSONL row includes event/timing IDs, provider/upstream, method/path/query, redacted headers, complete request bytes, complete response bytes, response status, model when discoverable, latency, SHA-256 hashes, and Prolog classifier labels. Non-UTF-8 bodies are stored as base64.

Provider-reported token counters are normalized as input_tokens, output_tokens, and total_tokens. The extractor recognizes OpenAI/OpenRouter-compatible, Anthropic, Gemini, Cohere, and Ollama JSON fields in regular JSON, SSE, and text WebSocket responses. Missing counters remain null; llm-log does not estimate tokens from body size or text.

Analytics API

The capture service exposes a provider-neutral, read-only analytics API on the same listener:

Endpoint Result
GET /api/v1/stats/summary total requests, usage coverage, and aggregate token I/O
GET /api/v1/stats/timeline?granularity=minute exact minute/hour/day buckets for graphs
GET /api/v1/stats/models token totals grouped by provider and model
GET /openapi.json OpenAPI 3.1 contract
GET /docs Swagger UI

All stats endpoints accept optional RFC 3339 start (inclusive), end (exclusive), provider, and model query parameters. The timeline bucket_seconds and exact UTC bucket edge let consumers such as the Qtile telemetry widget calculate token rates without maintaining a second provider-specific history database.

The recorder is a single-writer asyncio.Queue actor. Concurrent proxy requests can complete in parallel, but only the recorder actor appends corpus/KB records, preventing interleaved file writes.

The initial SWI-Prolog classifier is intentionally coarse (coding, research, search, writing, analysis, fallback chat). It is a seed for an evolving expert system, not training truth.

Test locally

nix develop
python -m unittest discover -s tests -v

No GitHub Actions development loop is required for this slice.

Capture boundary

This captures traffic from software you deliberately point at the proxy: gptel, OpenAI-compatible tools, OpenRouter clients, local model clients, and similar configurable callers. It does not magically capture the ChatGPT/Claude web apps or arbitrary HTTPS applications. Doing that later would require a system proxy / TLS interception design and should be a separate security-sensitive slice.

Next ARADR directions

Later slices can derive fine-tuning datasets, mine repeated failure paths, grow Prolog expert rules, add semantic retrieval, route by symbolic intent, and optionally inject search/tool results before forwarding. Those are intentionally outside ARADR-001.