Glasspane is a lightweight, high-fidelity LLM observability proxy service designed for real-time logging, analysis, and diagnostics of LLM traffic.
It is set up as a monorepo containing:
- /backend: A FastAPI proxy service that intercepts LLM calls, forwards them transparently to the Groq API (OpenAI-compatible), logs details (prompts, completions, latency, status code, tokens) to ClickHouse asynchronously via Redis Streams, and evaluates response quality (relevance, hallucinations) in a non-blocking queue.
- /frontend: A React + Vite dashboard displaying live-updating aggregate stats (including average relevance/hallucination), interactive quality and drift metrics timeline charts (SVG), detailed trace lists, and a searchable/expandable trace explorer table with score progress bars.
- Python 3.11+
- Node.js (v18+) & npm
Install the Python dependencies using uv, ensure a local Redis server is running, and launch the server processes:
# Install dependencies
uv sync
# Navigate to backend
cd backend
# Run the FastAPI proxy server
uv run uvicorn main:app --reload --port 8000
# In a separate terminal, run the Redis Stream consumer
uv run uvicorn consumer:app --reload --port 8001The backend proxy server will run on http://localhost:8000, and the log consumer service will run on http://localhost:8001. The backend logs will be stored in a local ClickHouse server on port 8123.
Navigate into the /frontend folder, install npm dependencies, and start the development server:
# Navigate to frontend
cd frontend
# Install react & vite
npm install
# Run frontend development server
npm run devThe React dashboard will be available at http://localhost:5173.
You only need to change the base_url (and optionally authorization tokens) in your code to direct your LLM requests through the proxy.
Instead of pointing to Groq's default URL, configure the base_url to direct requests to Glasspane:
from openai import OpenAI
# Initialize the client pointing at Glasspane's proxy port
client = OpenAI(
base_url="http://localhost:8000/openai/v1",
api_key="your_groq_api_key" # The proxy forwards this to the actual Groq API
)
# Non-streaming request (Logged in ClickHouse)
response = client.chat.completions.create(
model="llama3-8b-8192",
messages=[
{"role": "system", "content": "You are a helpful coding assistant."},
{"role": "user", "content": "What is Glasspane?"}
],
stream=False
)
print(response.choices[0].message.content)
# Streaming request (Parsed, reconstructed, and logged in ClickHouse on completion)
stream = client.chat.completions.create(
model="llama3-8b-8192",
messages=[{"role": "user", "content": "Count from 1 to 5"}],
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)You can query the observability proxy endpoints directly or trigger calls to verify operations.
curl -X POST http://localhost:8000/openai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_GROQ_API_KEY" \
-d '{
"model": "llama3-8b-8192",
"messages": [{"role": "user", "content": "Hello! Explain what a proxy is in 10 words."}],
"stream": false
}'curl -X POST http://localhost:8000/openai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_GROQ_API_KEY" \
-d '{
"model": "llama3-8b-8192",
"messages": [{"role": "user", "content": "Count from 1 to 5."}],
"stream": true
}'Returns the most recent logged request/response pairs (defaults to 50):
curl http://localhost:8000/logs?limit=10Returns full logged record by its UUID trace_id:
curl http://localhost:8000/logs/YOUR_TRACE_UUIDReturns metrics like total requests, error rate, average latency, token consumption, and average evaluation scores:
curl http://localhost:8000/statsWhenever an LLM chat completion is successfully routed through Glasspane and returns a 200 OK status, a background worker asynchronously evaluates the prompt-response interaction using Groq's llama-3.1-8b-instant model.
Two metrics are calculated and logged:
-
Relevance Score: Represents how well the response answers the prompt.
1.00: Perfectly relevant, directly answers the prompt or complies with constraints.0.75: Relevant, but misses minor details or includes minor unnecessary text.0.50: Partially relevant, off-topic in parts.0.25: Mostly irrelevant, fails to answer the main prompt.0.00: Completely irrelevant or gibberish.
-
Hallucination Score: Indicates the severity of false details or fabricated claims in the response.
1.00: Severe hallucinations, completely fabricated or factually incorrect.0.75: Significant hallucinations or factually incorrect claims.0.50: Moderate hallucinations, contains some unsupported/made-up claims.0.25: Minor hallucinations, slightly misleading but mostly grounded.0.00: No hallucinations, completely grounded and factually correct, or complies with non-factual constraint.
These scores are stored as float values (between 0.0 and 1.0) in the database, displayed inline in the Traces Table, visualized as progress bars, plotted on the Quality & Drift Trends SVG Line Chart on the dashboard, and averaged under overview KPI cards.
To ensure the proxy is working correctly, perform the following steps:
-
Verify Backend Offline Log Handling: Fire a curl request with an invalid/empty key or structure:
curl -X POST http://localhost:8000/openai/v1/chat/completions -H "Content-Type: application/json" -d '{"model": "llama3-8b"}'
Confirm that the error response is returned and immediately logged. Check
http://localhost:8000/logsto see the logged failed trace. -
Verify Live Updates in Dashboard: Open
http://localhost:5173in your browser. Ensure the connection badge displays "Active Proxy" (green indicator). Trigger a few requests using the cURL examples above and watch the Stats Cards and the Traces table update live. -
Verify Trace Drill-down: Click on any row inside the traces table in the dashboard to expand it. You should see the pretty-printed JSON payloads for the request and response.
-
Run System Benchmarks: Compare the legacy synchronous logging + LLM evaluation processing against the decoupled Redis Stream queuing using the benchmark script:
python benchmark.py
By implementing a decoupled queueing architecture with Redis Streams, Glasspane achieves significant performance improvements by offloading high-I/O database writes and network-bound quality evaluations from the main request path:
- Proxy Latency Overhead: Reduced proxy-induced overhead from ~1027.3ms (legacy sync SQLite insert + blocking HTTP evaluation call) to ~1.1ms (asynchronous Redis Stream
XADDunder persistent connection), a 99.9% reduction in ingestion latency. - Ingestion Throughput: Scaled maximum ingestion throughput by 90x+ (from ~1.0 rps when blocked by synchronous DB and API processes to ~900+ rps under parallel streaming).
- Resource Decoupling: Offloaded 100% of disk writes and evaluation request payloads to an asynchronous consumer service, eliminating event loop blocking.
- Data Reliability: Leveraged Redis Streams consumer group acknowledgements (
XACK), guaranteeing zero loss of LLM telemetry even during database downs or restarts.
