Skip to main content
Task apps are lightweight FastAPI services that expose your environment, scoring rubric, and tracing hooks. The CLI spins them up locally (uvx synth-ai serve) or remotely on Modal (uvx synth-ai deploy). For SFT the goal is to produce rich traces and SFT-ready JSONL dumps without manual intervention. The math demo in the quickstart is a concrete reference implementation you can copy when wiring your own app. For CLI deep dives, see Run Task Apps Locally, Run Task Apps on Modal, Deploy Task Apps, and Run Evaluations.

Minimum contract

Every task app must provide:
  • A TaskAppConfig object registered through register_task_app.
  • Implementations of /health, /task_info, and /rollout (the Synth helpers wire these up for you).
  • Access to an environment API key (read from ENVIRONMENT_API_KEY or its aliases).
  • Optional rubrics and dataset metadata so rollouts can be judged and filtered later.
When the CLI runs uvx synth-ai eval it expects to:
  1. Fetch /task_info to understand actions, observations, and limits.
  2. POST /rollout requests for each seed with policy + environment parameters.
  3. Receive a RolloutResponse containing trajectories and/or a v3 trace payload.

Enable tracing and SFT dumps

The easiest way to keep your dataset in sync is to enable tracing and SFT sinks by default. The Crafter task app in the SDK uses the following pattern:
from synth_ai.task.server import TaskAppConfig
from synth_ai.task.tracing_utils import (
    build_tracer_factory,
    resolve_sft_output_dir,
    resolve_tracing_db_url,
    tracing_env_enabled,
)
from synth_ai.tracing_v3.session_tracer import SessionTracer

def build_config() -> TaskAppConfig:
    tracing_enabled = tracing_env_enabled(default=True)
    tracer_factory = build_tracer_factory(
        SessionTracer,
        enabled=tracing_enabled,
        db_url=resolve_tracing_db_url(),
    )
    sft_output_dir = resolve_sft_output_dir()

    app_state = {"tracing_enabled": tracing_enabled}
    if tracer_factory:
        app_state["session_tracer_factory"] = tracer_factory
    if sft_output_dir:
        app_state["sft_output_dir"] = sft_output_dir

    return TaskAppConfig(
        app_id="your-task-id",
        name="My Task App",
        description="Pipelines traces into SFT JSONL",
        base_task_info=base_info,
        describe_taskset=lambda: describe_taskset(dataset),
        provide_task_instances=lambda seeds: provide_instances(dataset, base_info, seeds),
        rollout=rollout_executor,
        dataset_registry=registry,
        rubrics=RubricBundle(outcome=OUTCOME_RUBRIC, events=EVENTS_RUBRIC),
        app_state=app_state,
    )
Key environment variables:
  • TASKAPP_TRACING_ENABLED=1 – turns on trace recording and ensures SessionTracer is created.
  • TASKAPP_SFT_OUTPUT_DIR=ft_data – writes per-run JSONL to that directory after each rollout.
  • ENVIRONMENT_API_KEY – used by require_api_key_dependency to protect /rollout.
When set, every rollout produces:
  1. A persistent session trace in traces/v3/synth_ai.db.
  2. An SFT dump with system/user/assistant messages and optional tool-call metadata.

Local iteration checklist

# 1. Run the task app locally (auto-loads .env written by `uvx synth-ai setup`)
uvx synth-ai serve your-task-id --trace traces/v3

# 2. Kick off rollouts to populate traces
uvx synth-ai eval \
  --app-id your-task-id \
  --model Qwen/Qwen3-4B \
  --seeds 1-50

# 3. Inspect the generated SFT JSONL
ls ft_data
The eval command is idempotent—agents can re-run it whenever you need fresh data. Because evaluation and rollouts are the same API, you can reuse the same path for benchmarking later.

Remote deployments (Modal)

For remote training you typically deploy the task app to Modal:
uvx synth-ai deploy your-task-id \
  --name my-task-app \
  --trace traces/v3
Before launching SFT or RL jobs, confirm the deployment is healthy:
curl -H "X-API-Key: $ENVIRONMENT_API_KEY" https://<modal-subdomain>.modal.run/health
curl -H "X-API-Key: $ENVIRONMENT_API_KEY" https://<modal-subdomain>.modal.run/task_info
Alternatively, run uvx synth-ai eval --task-url https://<modal-subdomain>.modal.run --seeds 1-2 to execute a smoke rollout. Once /health and /task_info respond with HTTP 200, you can safely collect production traces and kick off training jobs. The rest of the SFT pipeline assumes the task app is reachable at the URL you supply in your TOML configs. Tip: pass --env-file only when you want to override the automatically-discovered .env. You can repeat the flag if you need to merge multiple files.