Embedded OTel-native trace + eval store for LLM apps(no container, no database).
315
mancube/evald)An OTel-native trace + eval store in one static binary. Point your OpenInference/OTel exporter at :4318, see your traces, run evals from a JSONL dataset, get scores keyed to the exact span — and gate CI on regressions. No database, no Python runtime: a single static musl binary that runs on your laptop, inside a locked-down CI runner, or on an air-gapped host.
mancube/evald — static musl binary on distroless/static, runs as nonroot (uid 65532), no shell, no package manager.linux/amd64, linux/arm64 · Binary inside: /usr/local/bin/evald (entrypoint) · Exposes: 4318 (OTLP/HTTP receiver + query/SQL API + embedded UI)evald is the trace-ingest + eval store that sits between the apps emitting
LLM/agent telemetry and the humans (and CI) reading it. Point any OpenInference/OTel
exporter at :4318; it normalizes each span, keeps it in a durable local store, and
serves the UI, SQL, and eval/CI surfaces off the same data. No collector, no external
database, no Python.
SOURCES EVALD STORE CONSUMERS
(emit OTLP spans) (this image) (local, durable) (read + gate)
┌──────────────┐
│ LLM app │ OTel SDK ─┐
│ (SDK export) │ │
└──────────────┘ │
┌──────────────┐ │ ┌─────────────┐ ┌──────────────────┐
│ Agent / │ spans ────┼────▶ │ WAL (redb) │ │ Embedded UI │
│ framework │ traces │ │ (durable) │──▶ │ (trace → tree → │
└──────────────┘ │ └──────┬──────┘ │ scores) │
┌──────────────┐ ┌───────┴──────┐ │ └──────────────────┘
│ OTel │ ─▶ │ evald │ ◀────┘ ┌────▶ │ SQL / query API │
│ Collector │ │ ingest·norm │ ┌─────────┐ │ (DataFusion) │
└──────────────┘ │ ·store·eval │─────▶│ Parquet │ └──────────────────┘
└──────┬───────┘ │ blocks │ ┌──────────────────┐
POST /v1/traces │ scores ────▶│ ∪ scores│─│ eval run/compare │
(protobuf · OTLP-JSON) │ └─────────┘ │ (CI regression │
└──────────────────────────│ gate, exit ≠ 0) │
└──────────────────┘
evald is the receiver
(POST /v1/traces, protobuf or OTLP-JSON) — no separate collector required.spans ∪ scores, and offline eval run/eval compare that gate CI on a
regression.| Tag | Notes |
|---|---|
latest | newest stable release |
0.1.0 | first release — OTLP ingest + normalization, durable WAL→Parquet store, scores, offline eval run/eval compare with CI gating, SQL, embedded UI |
*-rc.* | pre-release smoke builds (not tagged latest) — don't use in production |
Pin a version in production: mancube/evald:0.1.0.
The binary is the entrypoint, so the docker args are just evald subcommands (serve / eval / query / version). Mount a data dir for the durable store; everything stays local.
docker run --rm mancube/evald:0.1.0 version
1. Run the store — the OTLP/HTTP receiver + query API + embedded UI. Bind 0.0.0.0 inside the container (the default 127.0.0.1 isn't reachable from the host):
docker run --rm -p 4318:4318 \
-v "$PWD/evald-data:/data" \
mancube/evald:0.1.0 \
serve --otlp-http 0.0.0.0:4318 --data-dir /data
# point your app's OTel SDK at http://127.0.0.1:4318, then open http://127.0.0.1:4318/
2. Gate CI on an offline eval — score a JSONL dataset and exit non-zero on a regression:
docker run --rm \
-v "$PWD:/work" -w /work \
mancube/evald:0.1.0 \
eval run --config eval.yaml --data-dir /work/evald-data
# then diff two runs, failing the build only on a statistically significant drop:
docker run --rm -v "$PWD:/work" -w /work mancube/evald:0.1.0 \
eval compare <run_a> <run_b> --data-dir /work/evald-data \
--fail-on-regression --significance
3. Query the store with SQL — DataFusion over the Parquet blocks ∪ scores:
docker run --rm -v "$PWD/evald-data:/data" mancube/evald:0.1.0 \
query "SELECT model, COUNT(*) n, SUM(total_tokens) tok FROM spans GROUP BY model" \
--data-dir /data
Permissions: the image runs as nonroot (uid
65532), so the mounted/datadir must be writable by that uid — e.g.mkdir -p evald-data && chmod 777 evald-data(orchown 65532:65532 evald-data) before the first run.
| Command | What it does |
|---|---|
serve --otlp-http 0.0.0.0:4318 --data-dir <dir> | OTLP/HTTP receiver + durable store + query/SQL API + embedded UI |
eval run --config <eval.yaml> --data-dir <dir> | score a JSONL dataset; exit non-zero on a threshold regression (CI gate) |
eval compare <runA> <runB> [--fail-on-regression] [--significance] | diff two runs by evaluator; gate on a (statistically significant) regression |
query "<SQL>" --data-dir <dir> | read-only DataFusion SQL over spans ∪ scores |
version | print the version |
The default build (this image) makes zero network calls — ingest, storage, query, and the deterministic Tier-1 evaluators are entirely local and offline. It runs disconnected, on a laptop, in a locked-down CI runner, or on an air-gapped host. distroless/static + nonroot keeps the attack surface to just the binary. (Optional LLM-as-judge / embedding evaluators are BYO-key and call out only when you opt in.)
Apache-2.0.
Content type
Image
Digest
sha256:f9c739fea…
Size
14.6 MB
Last updated
2 months ago
docker pull mancube/evald