An agent-native SDK for building data UIs from typed schemas.
The labelling primitive that the modern post-training, evals, and agent-development loop needs does not exist as a library. Heavyweight platforms (Argilla, Label Studio) optimize for a discrete-project workflow that no longer matches how teams work. Vibe-coded one-off UIs (the Hamel/Shreya school) prove the demand but fragment the artifact. There is room for a small, opinionated SDK that turns typed schemas into constrained data UIs, composes into agent loops natively, and produces Hub-native artifacts.
- An SDK you import. Not a hosted product. Not an app. Not a platform.
- Schema-first: typed Python classes drive UI generation.
- Agent-native: the loop primitive (link → handoff → state-diff → continue) is in the core, not bolted on.
- Local-first: defaults to
127.0.0.1, no server-side state required. - Hub-native: sessions serialize to Hub-compatible artifacts; the Hub is the only system of record.
- Standalone-capable: works perfectly well without an agent in the loop.
- Not a platform with workspaces, users, roles, or auth.
- Not a hosted SaaS.
- Not a project-management tool for labelling teams.
- Not an end-to-end pipeline.
- Not a competitor to Gradio (sibling library, shared aesthetics, different loop).
- Not a replacement for Label Studio / CVAT / Argilla for users who actually want those products.
- Tools, not platforms. Sessions are ephemeral by default; persistence is opt-in via the Hub.
- Generality at the workflow layer, specificity at the primitive layer. Inverse of Argilla.
- Strong opinions on the schema vocabulary and component set. No opinions on storage, auth, queues, team workflows — those compose externally.
- The Hub is the system of record, always. No parallel workspace concept.
- Small core, curated extensions. Resist feature creep. The component library is the moat because it's small.
- Optimize for casual contribution. A new component should be a single-file PR.
- Lead docs with the standalone case. Agent integration is the second example, not the first.
- Capability-grounded vocabulary, audience-routed framing. Core types describe what the SDK does; docs route by audience.
- Resist the platform pull. Every requested team/queue/auth feature is a composable adjacency, not a core feature.
An agent-native SDK for building data UIs from typed schemas.
Eight words. The constant. Use it everywhere.
- MLEs training models
- SMEs assessing responses
- MCP/agent developers iterating on servers
- Researchers auditing synthetic data
- Anyone with a CSV who needs to grade things
Each gets a complete, runnable example. None previews the others.
Users (or agents) define a session by declaring a typed schema. The schema is the source of truth for the UI, the state shape, the artifact format, and validation.
- Pydantic models or
@dataclass-style classes (decide during exploration) typing.Annotatedmarkers parameterize components and add UI metadata- Schemas compose: a session can include a list of sub-schemas (e.g. a queue of items each with their own form)
- Schemas are serializable (JSON Schema export for agent consumption and cross-language clients)
- Schemas are versioned (artifact records schema version for reproducibility)
Kept deliberately small. Each is a typed, persistable, validated input primitive.
- Trajectory viewer — agent step list, tool calls, observations; per-step annotation slots
- Rubric grid — N criteria × M items, scores per cell, optional rationale
- Pairwise comparator — A vs B with preference + structured rationale
- Schema prober — render any JSON Schema as a form, capture call+response
- Diff reviewer — side-by-side or unified text/JSON diff with comments
- Annotation queue — paginated list of items, each rendered with a sub-schema
Plus a small set of leaf inputs that compose into the above:
- Text, markdown, code, numeric, boolean, single-select, multi-select, slider, score, free-text rationale, file upload (deferred to v2 if scope-pressed)
session = LabelingSession(schema=..., data=...)— instantiatesession.serve()— start a local HTTP server, return URLsession.read()— return current state (the API the agent's MCP tool calls)session.diff(since=...)— return state changes since a marker (for efficient agent polling)session.complete()— event/future the agent awaits via elicitationsession.artifact()— return the serialized artifact (schema + state + provenance)session.push_to_hub(repo_id)— publish to the HubLabelingSession.from_hub(repo_id)— load a published session back
A Hub-resident folder containing:
schema.json— the typed schema definitionstate.json— the current/final stateevents.jsonl— append-only log of state transitions (provenance)agent_trace.jsonl— if agent-driven, the agent's reasoning tracemanifest.toml— version, timestamps, author, schema hashREADME.md— auto-generated summary, viewable on the Hub
Artifact is:
- Diff-able (events.jsonl makes session-to-session comparison trivial)
- Reproducible (schema + initial data + events = deterministic final state)
- Loadable (one line to rehydrate)
- Shareable (Hub URL)
The SDK ships the pieces an agent needs to drive a session. Not the agent itself.
- Link emission —
session.serve()returns a URL the agent can hand off - State reading —
session.read()/session.diff()for the agent's MCP tool - Completion signal —
session.complete()resolves when the human signals done - Elicitation glue — helper that wraps the link + completion event in an MCP elicitation request
- Optional MCP server wrapper — a tiny FastMCP server exposing
start_session,read_state,complete_sessionas tools, so any MCP-capable agent can drive sessions without bespoke integration
Agent integration is recipes in docs, not framework lock-in. The SDK works the same whether the caller is Claude Code, Codex, Aider, smolagents, a bare LangGraph node, or a Jupyter cell.
- Default: local HTTP server on
127.0.0.1:<random-port> - Optional: HF Spaces deployment for shared/persistent sessions
- Optional: Gradio share-link-style tunneling for ad-hoc external review
- No remote-by-default mode. No telemetry by default.
Same SDK, no agent:
- Write a schema in a Python file
python my_session.py→ opens browser at local URL- Label, grade, audit, whatever
- Ctrl-C → artifact saved locally or pushed to Hub
- Optional
.from_hub()to resume
The agent loop is one valid caller; the human-only loop is another. Both first-class.
- Schema layer — typed schemas,
Annotatedmarkers, validation, JSON Schema export - Component layer — the curated UI primitives, each a self-contained module
- Session layer — runtime state, event log, completion signaling
- Server layer — HTTP + WebSocket (likely on top of Gradio's serving infrastructure or FastAPI directly; explore)
- Artifact layer — serialization, manifest generation, Hub integration
- Agent layer — elicitation helpers, optional MCP server wrapper, polling/diff utilities
Sit on:
- Gradio's serving stack where possible (FastAPI core, Spaces deployment, share-link tunneling)
- Pydantic for schema validation
- HF Datasets / Hub Python client for artifact storage
- FastMCP for the optional MCP server wrapper
Build:
- The typed-schema-to-UI generation pipeline
- The six component primitives
- The session model (state + events + completion)
- The artifact format and Hub integration
- The agent-loop helpers (elicitation, state-diff)
- Docs, recipes, examples
- Pydantic models vs.
@dataclassvs. our own decorator: which gives the best agent-ergonomics? - Build on Gradio's runtime or run parallel? Tradeoff between leverage and architectural freedom.
- WebSocket-based state sync vs. REST polling vs. SSE: which simplifies the agent's state-diff tool?
- How much of the UI is React vs. server-rendered HTML? Affects bundle size, extensibility, contribution friction.
- Component extensibility model: how does a third party add a new component without forking?
- Schema versioning and migration: how do we handle artifact load when the schema has evolved?
- Multi-user collaboration on a single session: in scope for v1, deferred, or never?
The reference flow, documented as the default integration pattern:
- Agent (in any terminal coding tool) determines a human handoff is needed.
- Agent constructs a typed schema describing what the human should do.
- Agent calls
start_session(schema, data)via the SDK's MCP server (or the Python API directly). - SDK returns
{url, session_id}. - Agent issues an MCP elicitation request: "Please complete the task at {url}. Reply 'done' when finished."
- Human visits URL, completes the form, browser shows "all done" state.
- Human replies to elicitation: "done".
- Agent calls
read_state(session_id)and receives the completed artifact. - Agent continues its work, optionally publishing the artifact to the Hub.
Works in every terminal agent that supports elicitation (which is the entire MCP-compliant ecosystem). Works in chat-panel hosts too (Claude, ChatGPT) — they get inline link rendering and elicitation flows for free.
Not the unifying primitive. A chat-panel-hosted client can render the session inline as an MCP App if both the client and SDK support it, but the SDK does not require it and the canonical flow does not depend on it. Treat MCP Apps emission as an enhancement for the chat-panel cohort, not a default.
Each recipe is a complete, copy-pasteable example. These ship as the headline demos.
- Inputs: a Distilabel pipeline's output (HF dataset)
- Schema: side-by-side prompt/response viewer + quality score + failure-mode multi-select + notes
- Flow: agent surfaces suspect items, SME audits, agent retrains judge filter
- Artifact: annotated dataset on the Hub
- Inputs: a DPO preference dataset where the reward model disagrees with humans
- Schema: pairwise comparator + preference + rubric grid for why
- Flow: MLE grades disagreements, agent updates rubric and reward model
- Artifact: preference dataset + versioned rubric on the Hub
- Inputs: an MCP server under development
- Schema: schema prober per tool + pass/fail/ambiguous + suggested description rewrite
- Flow: agent exercises the server, developer reviews findings, iterates descriptions
- Artifact: reproducible test suite on the Hub
Additional recipes (later): RAG retrieval review, prompt eval grading, trajectory annotation for RL environments, dataset spot-check.
To protect the small-core discipline:
- User management, roles, permissions, SSO
- Hosted runtime as a paid service
- Persistent queues, project dashboards, team workflows
- Multi-tenant deployment
- Pixel-level annotation (defer to CVAT via export adapters if needed)
- Video timeline annotation (same)
- Pre-built integrations with every annotation vendor
- An end-to-end training pipeline
- A judge model, an LLM API client, an eval harness
- Agent orchestration logic (the agent is the caller, not the callee)
If a user wants any of the above, they compose it. The SDK stays a library.
- Sibling library, not extension. Different loop, different primitive vocabulary.
- Shared aesthetics where it makes sense. Typography, color, baseline component styling.
- Shared serving infrastructure where it makes sense. Explore using Gradio's FastAPI layer + share-link tunneling rather than rebuilding.
- Interoperable in the same Python process. A user mixing demo-shape and labelling-shape code should not feel friction.
- Long-term relationship unresolved. Three plausible futures (stay sibling, get absorbed as
gradio.label, philosophical convergence). Hold loosely; let usage inform.
- Not a replacement; a different category. Argilla is a deployed app; this is a library you import.
- Complementary positioning. Argilla covers static-schema annotation projects; this covers dynamic, ephemeral, agent-driven sessions.
- Honest about the cautionary tale. Argilla's platform-shape is the failure mode this design avoids. Say it in docs/talks without disrespect.
- No migration path planned. Different enough that direct migration isn't meaningful. Recipes can demonstrate equivalent workflows.
These are real questions the implementation phase must answer. None blocks getting started; all need answers before v1.0.
- Pydantic vs. dataclass vs. custom decorator — which is most agent-ergonomic?
- How are nested/composite schemas best expressed? (List of items, each with its own schema.)
- What's the right vocabulary of
Annotatedmarkers? Erring small to start. - How do we handle schema evolution and artifact migration?
- Can the schema be defined in JSON Schema directly (for non-Python callers / agents emitting JSON)?
- Build on Gradio's serving stack or run parallel?
- WebSocket / SSE / polling for state sync?
- React frontend vs. server-rendered HTML vs. hybrid (HTMX-style)?
- How small can we get the JS bundle for the local-server case?
- What does a third-party component PR look like? Single file? Convention over configuration?
- How do components compose? Slots? Children? Explicit composition API?
- Styling: Tailwind-style utility classes, CSS modules, or component-scoped styles?
- Accessibility baseline: WCAG AA from day one, or accept v1 gaps?
- Should the SDK ship its own MCP server or leave it as a recipe?
- How does the agent know what schema to write? Do we ship "schema templates" the agent can pick from?
- What's the right
read_stategranularity — full state, diff, or query-able? - Should completion signaling support partial completion / pause-resume?
- What's the canonical Hub repo structure for a session artifact? New repo type or a folder convention?
- Should sessions be loadable as HF Datasets directly, or remain a distinct artifact type?
- How does versioning work on the Hub — branches, tags, commit history?
- Private session artifacts: respect Hub permissions as-is.
- Can a user write a single-file Python script and
python my_label.pyJust Works? - CLI entry point?
hf-data new,hf-data run,hf-data publish? - Notebook integration: how does this feel in a Jupyter cell?
- Adapters: do we ship import/export for Label Studio JSON, CVAT XML, Hub datasets at v1?
- Distilabel integration: explicit pipeline step, or just a recipe?
- TRL / verifiers integration: same question.
- VS Code / Cursor extensions: out of scope for v1, but worth thinking about discoverability.
- License: Apache 2.0 (matches Gradio, TRL, Datasets).
- Repo structure: monorepo with core + components, or split?
- CI from day one or after first stable release?
- Docs site: MkDocs (Gradio's choice) or Docusaurus or HF docs builder?
- How do we handle the "audience routing" in docs technically — duplicate examples or canonical examples with audience preambles?
- Project name: TBD. Working title for now. Should be:
- Memorable
- Pronounceable in English and other major languages
- Available as PyPI package, GitHub org/repo, domain (if applicable)
- Not directly evocative of "labelling" (we want the breadth) or "agent" (we want the standalone case)
- Capability-grounded, not audience-grounded
- Scope creep toward platform-shape. Re-read this spec quarterly; refuse features that drift.
- Gradio team politics around naming, positioning, and sibling-status. Surface early, often.
- MCP ecosystem evolution: if elicitation gets replaced or deprecated, the canonical flow needs an alternative.
- The "vibe coding hangover": ship reliability and security from day one to avoid the trust deficit hitting the project unfairly.
- Argilla comparisons in the wild: have a one-sentence clean answer ready.
Not commitments. Signals that the bet is working.
- 300+ GitHub stars
- 50+ session artifacts published to the Hub by non-team users
- 5+ external case studies (blog posts, conference talks, Twitter threads from users)
- 3+ external contributors with merged component PRs
- At least one integration into a major OSS post-training pipeline (TRL, verifiers, SkyRL, Distilabel)
- Mention in a Hamel/Shreya/Karpathy/Lütke/Anthropic/HF blog or post
- 80%+ of recipe issues resolved with "use this recipe" rather than "we'll add that feature"
If 4+ of these hit by month 6, double down. If <2, re-evaluate scope and positioning.
- Naming. Pick a working name.
- Schema-API spike. Sketch the typed-schema → form generation in ~200 lines, decide Pydantic vs. dataclass vs. decorator.
- One component, end-to-end. Build the rubric grid as the canonical first component, including artifact serialization.
- Local-server prototype.
session.serve()+ browser flow +session.read()working. - Standalone recipe. A real user (someone outside the team) labels something with it.
- Agent recipe. Same flow, driven from Claude Code via elicitation.
- Hub artifact roundtrip. Push, view, load back.
- Second component. Trajectory viewer or pairwise comparator, to validate the extension model.
- Public preview. Repo, docs site, first recipes published.
- Iterate based on real usage. Don't add components or features speculatively; let the recipes pull them in.
Each step is a checkpoint where the spec can be revised. The spec is not law; the eight-word tagline is.