Spaces:
Running
title: contimp-app
emoji: π
colorFrom: indigo
colorTo: pink
sdk: docker
app_port: 7860
pinned: false
contimp-app
Live app: https://lefft-contimp-app.hf.space (team passcode β ask in Slack) Β· hosted as a Hugging Face Space
A small demo application that exercises an LLM on narrow, repeatable tasks and logs every interaction (prompts, responses, tool calls, ground truth, auto-scores) to LangFuse. It exists to generate real live traffic for dogfooding Oumi's continuous model improvement ("contimp") loop: import traces as datasets, define evals, fine-tune a small model, compare, and swap the endpoint.
The app is deliberately simple: one FastAPI service, a static vanilla-JS frontend, no database. All state lives in committed data files and LangFuse.
Tasks
Tasks are plugins (see Adding a task below). Current tasks:
| id | what the model does | ground truth |
|---|---|---|
pr-area |
Read a real merged oumi PR (title+body) and classify which subsystem it touches | Derived from the files the PR actually changed |
config-copilot |
Turn a natural-language training-run request into a valid oumi training YAML, using get_schema / validate_config tools |
Requests are generated from known target field values; YAML graded on validity + per-field match |
"Deal me one" gives auto-scorable inputs; users can also type their own (scored on validity/thumbs only).
Run locally
cp .env.example .env # fill in keys
uv sync
uv run uvicorn app.main:app --reload --port 7860 --env-file .env
Tests: uv run pytest
The endpoint swap
The app talks to a backend β a wire protocol + endpoint + model + key.
The default backend is set by the LLM_* env vars, and supports two protocols
via LLM_PROVIDER:
openai(default): OpenAI Chat Completions (LLM_BASE_URLends in/v1/).anthropic: Anthropic Messages (LLM_BASE_URLends in/inference). Needed for an Oumi deployment that proxies an Anthropic model β Oumi's/chat/completionsrejects the anthropic provider (seedocs/findings.md).
Moving the day-1 model (Haiku via Anthropic's OpenAI-compat endpoint) to an OpenAI-compatible Oumi deployment (e.g. a tuned model) is an env-only change:
LLM_BASE_URL=https://<oumi>/inference/v1/
LLM_MODEL=projects/<project_id>/deployments/<deployment_id>
LLM_API_KEY=<oumi api key>
Per-task backends
Tasks can target different backends β e.g. one demo on Anthropic-direct,
another on an Oumi proxy. A task pins itself by setting backend="<name>"
(see app/tasks/base.py); the named backend is configured with LLM_<NAME>_*
env vars (see .env.example). Tasks that don't set one use the default. Each
trace records its backend in metadata so traffic can be segmented per backend.
Observability
One LangFuse trace per run; nested generations per LLM call (OpenAI wire format, tool calls included). Every trace carries:
- tags:
[<task_id>, human|synthetic] - metadata:
task_id,app_version,model,input_id,ground_truth - scores: per-task auto-scores (
exact_match;yaml_valid,schema_valid,field_match) plus humanthumbsfeedback user_id: self-reported handle (ortraffic-bot),session_id: browser session
Filter by tag/metadata task_id before exporting per-task datasets. Besides the
LangFuse UI batch export, scripts/export_traces.py pulls traces via the API as
observation-level JSONL in the shape the oumi platform's LangFuse importer
consumes (see docs/findings.md for one importer gap found doing this).
Data files
data/prs.jsonlβ mined merged PRs (title, body, changed files, derived area). Rebuild:uv run python scripts/build_pr_dataset.pydata/oumi_training_schema.jsonβ curated JSON Schema subset of oumi's TrainingConfig used by theconfig-copilotvalidator tool. May drift from oumi HEAD; rebuild:uv run python scripts/dump_oumi_schema.py
Traffic generation
uv run python scripts/generate_traffic.py --target https://<deployment-url> --task pr-area --n 25
Synthetic traffic is tagged synthetic with user traffic-bot so it can be
segmented from human traffic.
Adding a task
Drop one module into app/tasks/ defining a TASK = Task(...) (see
app/tasks/base.py), plus any data file it needs. The registry auto-discovers
it; nav, sampling, running, scoring, and tracing all come for free.
Deploy
The live app is a Hugging Face Space
that builds from its own copy of these files (the YAML frontmatter above is its
config; it serves on port 7860). Merging to main does not deploy β the
Space is updated explicitly by uploading the working tree to it:
uv run --with huggingface_hub python scripts/deploy.py -m "what changed"
When to run it: after a change is merged to main and you want it live. The
upload triggers a rebuild (~1-2 min).
deploy.py uploads your local working tree, not remote main β whatever is
in the directory ships, regardless of git state. So run this checklist first:
git checkout main && git pullβ deploy the canonical merged state.git statusβ clean tree, no stray files (the upload skips.git,.venv,__pycache__,.env*,.pytest_cache, and.claude/*).uv run pytestβ green.hf auth whoamiβ logged in with write access to the Space.- Deploy, then verify:
curl https://lefft-contimp-app.hf.space/api/healthand confirm the task shows at/api/tasks(or click the app). Traffic only reaches LangFuse if the Space has theLANGFUSE_*secrets set (see Prerequisites).
Prerequisites:
- A Hugging Face login with write access to the Space β run
hf auth loginonce; the token is cached under~/.cache/huggingface/and reused across sessions. Check withhf auth whoami. - That's the only credential
deploy.pyneeds. The app's own secrets (LLM_API_KEY,LANGFUSE_*,APP_PASSCODEβ see.env.examplefor the names) are not deployed from here: they live in the Space's Settings β Variables and secrets, encrypted, and are never committed. To rotate one, edit it there and restart the Space; the repo doesn't change.
The app is host-agnostic (plain Docker + env vars), so it also runs on any other
container host β point that host at the Dockerfile and supply the
.env.example variables as environment secrets.