Skip to main content
A reward function turns a model completion into a number. GRPO uses these scores to compute advantages: completions above the group mean get reinforced, completions below get penalized. Rewards are not a separate resource on Veri. You attach the function’s Python source directly to the training job at submit time; the platform stores it with the job and it lives and dies with the job. There is nothing to upload, name, or clean up.

Format

Veri runs reward functions in the trl shape, the format the production GRPO runner uses. At submit, the platform checks that each source parses as Python and defines a function; a violation rejects the job with HTTP 400 before anything is billed. Loading and invocation happen in the open-source runtime: see load_reward_function in training_runtime.py (and score_episode in harness_rollout.py for how harness trajectories are scored) if you need to check exactly how your function is called.

TRL format

Receives a batch of completions, returns one score per completion.
  • completions — list of strings OR list of message-dict lists (depending on how the runner formats prompts)
  • answer — list of ground-truth strings (from the dataset’s answer column)
  • Return — list of floats, same length as completions

Attach it to a job

The reward rides the job submission itself. Pick your surface:
From the CLI, veri jobs create configs/train.toml reads [reward].file, or override it inline with --reward ./reward.py. The CLI runs an AST lint before submitting (the file parses as Python and defines at least one function), so a broken file fails locally instead of at the API. A method=grpo or method=grpo_harness job requires a reward (or environments, which is mutually exclusive with reward sources). sft_text, dpo, sft_video_gen, and custom_script jobs must omit it: the dataset is their training signal. The job response carries reward_count (the number of attached sources; null for reward-free methods). Limits: up to 8 sources per job, 256 KiB each, and every source must define a function. Violations are HTTP 400 at submit.

Sandbox + limits

Reward functions run inside the training runner. They have:
  • No network egress — calls to external APIs will silently fail or hang
  • Standard library + the runner image’s pre-installed packages only — no pip install at runtime
  • A timeout per rollout batch — exceeding it fails the job with error.code = "training_error"
For PoC, treat this as “trusted users only.” Real isolation (gVisor / Firecracker) is on the Phase 2 roadmap.

Composing rewards

A common pattern is to weight multiple signals:
Keep each component bounded so the total stays in a stable range ([0, 1] is conventional). GRPO normalizes scores within a group, so the absolute scale matters less than the spread.

Multiple weighted reward functions

Instead of summing signals inside one function, you can attach several reward sources and let GRPO combine them with weights. TRL’s GRPOTrainer scores each completion with every function and sums the results, scaled by reward_weights. This keeps each signal in its own file (independently testable) and lets you retune the mix without editing code. Each function has the standard trl signature and returns one score per completion. Pass the sources and matching weights:
reward_weights is optional; omit it to weight every function equally. When provided, its length must match reward_sources (the SDK and API reject a mismatch). A single reward_source still works for the one-reward case.

Viewing a job’s reward

Every job keeps the exact source it was submitted with. Click a training job on the dashboard to view the reward source it was submitted with, or fetch it programmatically:
Because the source is stored per job, rerunning an old experiment is copy-paste: read the source back, tweak it, submit a new job.

Shaped rewards for harness jobs

Harness-in-the-loop jobs score the trajectory’s final assistant message by default, the deploy-aligned choice, since at inference time the final answer is all that matters. When you need more signal (partial credit for fetching the right page, penalties for invalid tool calls), declare a trajectory parameter and the platform passes the full message history of the rollout’s final turn (protocol-shaped dicts, including tool_use / tool_result content), one history per completion:
Rewards that don’t declare trajectory are called exactly as before; the parameter is opt-in. One sharp edge with chatty agent runtimes (the Claude Agent SDK especially): because the default scores only the final message, instruct your harness to put the answer there (“finish with a last line of exactly ANSWER: <url>”) or a correct mid-trajectory answer can score zero.

Where to go next

GRPO algorithm

What the score actually does inside the training loop.

Submit a job

Wire reward_source into training_jobs.create.

Quickstart

End-to-end with the example math reward.