Spaces:
Running
A newer version of the Gradio SDK is available: 6.20.0
Fabella Anonymized Agent Traces
When the public publisher is enabled, one anonymized row per request lands at
build-small-hackathon/fabella-traces.
The dataset repo is public and contains the live card, the publisher schema, and 5 seed rows for the data viewer. The Space publishes to it only when FABELLA_SHARE_TRACES=1 is set on the Space (default 0); with the flag off, rows only live in the per-parent bucket and are exportable via the Space's Settings β Download my history button.
This file is a local copy of the Hugging Face dataset card for Fabella's anonymized agent-trace publication. The live card on the Hub is the source of truth; this file is a convenience for reviewers reading the source repo.
Why this exists
Fabella is a small Hugging Face Space that drafts short, kind, age-appropriate explanations a parent can use to talk to their child about a hard thing (a hospitalization, a move, a pet dying). The first version is generated by google/gemma-4-E4B-it; a nvidia/NVIDIA-Nemotron-3-Nano-4B-BF16 judge scores it against a six-criterion rubric; the drafter revises if needed. An optional read-aloud uses openbmb/VoxCPM2.
The dataset captures the full ReAct loop for every successful generation, with PII removed before the row leaves the Space. It's the Sharing is Caring merit-badge artifact for the Build Small Hackathon submission.
Where it lives
- Live dataset (when enabled): https://huggingface.co/datasets/build-small-hackathon/fabella-traces (default)
- Personal-namespace fallback: set
FABELLA_TRACE_REPO=Kiy-K/fabella-tracesto publish to the maker's personal dataset instead. Used for local dev where the Space's HF_TOKEN can't create in the org namespace. - Default for this demo: the publisher is OFF (
FABELLA_SHARE_TRACES=0); data lives in the per-parent bucket and is exportable via the Space's Settings β Download my history button.
The dataset repo is created on first publish by trace.py::_ensure_repo via HfApi.create_repo(exist_ok=True), so a fresh deployment does not require a one-time setup step.
Schema (one row per request, when publishing is enabled)
{
"trace_id": "<uuid>",
"created_at": "2026-06-14T14:04:05+00:00",
"schema_version": 1,
"app_version": "fabella-1",
"model_versions": {
"drafter": "google/gemma-4-E4B-it",
"judge": "nvidia/NVIDIA-Nemotron-3-Nano-4B-BF16"
},
"request": {
"age": 7,
"tone": "gentle",
"child_name_present": true,
"child_name_redacted": "[name]",
"situation_hash": "sha256:9a3ba115d57a3789",
"situation_preview": "My 7yo's grandma is in the hospital.",
"situation_length": 36,
"history_turns": 0
},
"agent": {
"system_prompt": "<static, public, in-repo>",
"user_prompt": "A parent needs help...\n\nThe latest thing the parent is asking about: <redacted, see request.situation_length>",
"messages": [ /* full LangChain ReAct trace */ ],
"final_draft": { "opener": "...", "body": "...", "closer": "...", "followup": "..." }
},
"judge": {
"ok": true,
"issues": [],
"score": 0.92,
"verdict": "approve",
"reasoning": "good"
},
"latency_ms": 1234,
"tool_calls": 1
}
Anonymization
The publisher in trace.py runs every row through five anonymization passes before the row leaves the Space:
- Child name is dropped from the request and replaced with
"[name]"in the final draft. - Raw situation is never stored. Only its SHA-256 hash, the first 60 chars as a topic preview, and its character length are kept.
- Freeform history turns are replaced with role + length counts. The drafter's user prompt additionally replaces the embedded situation with a length-only marker.
- System prompt is shipped in full (it's a static string in this repo, not user input).
- LangChain 1.x
contentlists are normalized so list-of-blocks content is joined to its text.
Self-export
Every parent can pull their own data via Settings β Download my history in the running Space. The downloaded JSON bundle contains:
- All chat turns stored under the parent's owner key (full, not redacted β the parent already has this in their browser).
- The durable memory summary.
- A
trace_publicationsection that says exactly which dataset the redacted rows go to, the URL, the per-session row estimate, and the same five anonymization passes above.
The download path exists for two reasons: (1) the parent can verify that what we ship to the public dataset does not include raw situation text, child name, or anything that would link a row back to them; and (2) for a 3-day hackathon demo, it sidesteps the cold-start tax of the Hub push entirely.
Opt-out
- Set
FABELLA_SHARE_TRACES=0on the Space to kill the publisher (this is the default). - Or pass
share_trace=Falseonmake_explanationto opt out per request.
The default for this demo is off: rows only live in the per-parent bucket, accessible via the Download button.
Source code
trace.pyβ the source of truth for the schema and the anonymization rules.agent.py::_maybe_publish_traceβ the call site that builds and submits each row.app.pyβ calls_trace.publisher.start()at import time, exposesGET /api/history/downloadfor the parent self-export.app.pyINDEX_HTMLβ wires the Download my history button in the settings dialog.