Skip to main content
A Synth task app turns your environment into a hosted API. Once deployed (typically on Modal), the same service powers evaluation scripts, fine-tuning jobs, and RL rollouts. This guide explains the pieces you implement, how the CLI discovers them, and how to ship the app.

1. What the task app does

  • Exposes /health and /task_info so Synth can verify the service and surface metadata to teammates.
  • Implements /rollout, streaming observations, actions, and rewards between Synth’s trainers and your environment.
  • Optionally forwards vendor requests (OpenAI, Groq, etc.) through /proxy endpoints when the task needs external LLMs.
  • Emits structured traces so you can audit runs and build datasets.

2. Start with TaskAppConfig

  • Use synth_ai/task/apps/grpo_crafter.py as the reference implementation.
  • TaskAppConfig bundles:
    • task_info (TaskInfo in synth_ai/task/contracts.py) describing datasets, rubric weights, observation schema, and user-facing copy.
    • rollout_executor – translates RolloutRequest objects into your environment loop.
    • Optional provide_task_instances – map seed IDs to metadata (difficulty, scenario tags).
    • proxy + tracing settings to enable LLM routing and persistent logs.
  • Keep configuration dynamic: read flags and secrets from environment variables, not hardcoded constants.

3. Implement build_config() and register it

Export a factory that returns TaskAppConfig, then register it with the built-in registry:
from synth_ai.task.apps import TaskAppEntry, ModalDeploymentConfig, register_task_app

APP_ID = "my-task-app"

def build_config() -> TaskAppConfig:
    # construct TaskInfo, rollout executor, tracing config, etc.
    return TaskAppConfig(...)

register_task_app(entry=TaskAppEntry(
    app_id=APP_ID,
    description="Hosted task app for …",
    config_factory=build_config,
    modal=ModalDeploymentConfig(app_name="my-modal-app"),
))
  • Respect runtime flags such as TASKAPP_TRACING_ENABLED, SQLD_DB_PATH, and TASKAPP_SFT_OUTPUT_DIR so tracing can be toggled without code changes.
  • Load secrets (API keys, storage credentials) from the environment or injected .env files—never bake them into the image.

4. Describe datasets and rubrics

  • Populate task_info.datasets with a TaskDatasetRegistry so Synth knows which evaluation seeds or assets exist.
  • Provide rubric metadata (TaskRubric) so outcome and event rewards render correctly in dashboards.
  • Match dataset IDs with the ones you reference in your RL/SFT TOML files.

5. Wire rollouts and rewards

  • Implement a rollout executor that orchestrates environment steps, rewards, and done flags. Crafter’s executor shows how to translate between the SDK contract and an existing simulator.
  • Emit OutcomeReward and EventReward via the helpers in synth_ai/task/contracts.py; Synth aggregates them into metrics automatically.
  • Use TaskRolloutContext to keep per-episode state (inventory, difficulty tier, etc.).

6. Deploy and operate

  • Preview changes with uvx synth-ai modal-serve <APP_ID>—this launches a temporary Modal container with your latest code.
  • Deploy production instances with uvx synth-ai deploy <APP_ID> --name my-modal-app. The CLI bundles your code, uploads secrets, and prints the hosted URL.
  • Store the URL (commonly TASK_APP_URL) in your .env so training commands can pick it up automatically.

7. Iterate continuously

  • Keep tracing enabled (TASKAPP_TRACING_ENABLED=1) so every rollout yields reusable data.
  • Use uvx synth-ai train --type rl and --type sft with your configs; the CLI always verifies /health and /task_info before submitting jobs.
  • When you update environment logic, redeploy with uvx synth-ai deploy—Synth handles rolling updates without downtime.
By following this contract you ship only the environment logic; the SDK handles auth, routing, tracing, and deployment so every Synth workflow can reach your task app reliably.
I