🛠️ Work in progress · Local evaluation and remote model inference
A Gemma-powered coding agent that explores repositories, drafts bug fixes, and validates patches in Docker using the competition evaluation harness.
Competition · How to run · Presentation
The Gemma 4 Developer Agent Competition aims to bring autonomous coding assistance to consumer hardware. Participants post-train Gemma 4 to understand unfamiliar repositories, diagnose software issues, and generate correct patches using fine-tuning, reinforcement learning, prompts, and tools. Code graphs and embeddings support repository navigation; scoring measures the percentage of issues resolved through validation tests.
All agents must use gemma-4-31b-it-qat-w4a16-ct. Submissions include an agent.yaml configuration, supporting prompts, tools, skills, and optional LoRA adapters in submission.zip.
docs/context/ Competition overview, data, harness notes, and commits log
data/
assets/ Local snapshots, graphs, embeddings, and task wheels (gitignored)
train/ dev/ val/ Split manifests and task lists, with symlinks to their assets
docker/ sandbox/ Evaluation container and sandbox setup
src/ Agent submission: YAML, prompts, tools, skills, and adapters
scripts/ tasks/ (splits, wheels), runs/ (pipeline, eval), traces/ (viewer, Langfuse), sft_export/
logs/remote/
base/<split>/<task_id>/ Baseline artifacts; train includes failure analysis.md
<collaborator>/itr-<n>/<split>/<task_id>/ Per-task experiment artifacts
notebooks/ Kaggle inference notebook
training/ LoRA SFT trainer and its tests
viewer/ GitHub Pages trace viewer, presentation, and eval_ui reports
server/ vLLM serving (RunPod, local tunnel)
docs/ Reports, assets, and reading resources
The trace viewer catalog is generated from directory structure, not a manually
maintained task list. After adding traces anywhere below logs/remote/ using
the layout above, run python3 scripts/traces/build_trace_index.py. The viewer's
Trace directory control then lets you switch between every discovered
directory (for example, base, akshay/itr-1, or rishav/itr-1).
The split folders reference shared files in data/assets/, which are local downloads and are not committed. Run scripts/tasks/plan_observed_task_splits.py --materialize after restoring the Kaggle data to rebuild those references.
The competition provides a public training/development set for agent training, prompt design, and local validation. During scoring, it is replaced by a hidden test set. Both sets use the same graph-generation methods and verification standards. See the dataset description for the full schema.
The public set contains 129 Python bug-fixing and feature-request tasks from fastapi/fastapi, Textualize/rich, psf/requests, and encode/httpx.
| Asset | What it contains |
|---|---|
tasks.jsonl |
Task ID, repository, base commit, problem statement, optional hints, creation timestamp, reference solution (patch), and verification tests (test_patch). |
snapshots/ |
A Git repository archive for each task, frozen before the fix. Forward commit history is removed to prevent access to future solutions. |
graphs/ |
Python AST call and dependency graphs containing code symbols, source definitions, and relationships for structural navigation. |
embeddings/ |
256-dimensional float32 vectors for code symbols, used for semantic code search. |
Reference fixes and verification tests are available for training and offline analysis. They must remain separate from the task input when measuring the agent's ability to solve an issue.
The hidden test set has about 120 tasks from private repositories, split between public and private leaderboard evaluations. Reference fixes and grading tests are withheld.
The competition harness compiles the YAML submission, provides repository tools to the agent, and verifies its patch in a fresh sandbox. The diagram below is reproduced from the competition harness guide.
flowchart TB
subgraph Submission["Competitor Submission (submission.zip / agent_dir)"]
YAML["agent.yaml + sub_agents/*.yaml"]
EvalCfg["eval_config.yaml (Optional Budgets)"]
Prompts["prompts/*.md, configs/*.yaml"]
Adapters["adapters/* (Optional LoRA / Weights)"]
end
subgraph Host["Harness Process (swegemma + adk-submission + adk-eval-core)"]
Validator["validate_directory & validate_single_declared_model"]
Server["Local Inference Server (vLLM / Transformers :8000)"]
Compiler["compile_submission() -> ADK Runner"]
Tools["SwegemmaContext (9 Bound Tools + Budget Gate)"]
end
subgraph Sandboxes["Isolated Per-Task Sandboxes"]
ContA["Container A: Agent Sandbox (/workspace)\nSnapshot + Editable Install + Baseline Commit"]
ContB["Container B: Verification Sandbox (/workspace)\nFresh Snapshot + agent_patch + test_patch + pytest"]
end
Submission --> Validator
EvalCfg --> Host
Adapters --> Server
Validator --> Compiler
Server <-->|"OpenAI-compatible /v1 API"| Compiler
Compiler <-->|"Tool Calls & JSON Responses"| Tools
Tools <-->|"docker exec / subprocess"| ContA
ContA -->|"git add -N . && git diff --binary"| ContB
ContB -->|"exit_code == 0 & JUnit XML valid"| Score["Resolution Rate [0.0, 1.0]"]
| Tool | Purpose |
|---|---|
run_command |
Run shell commands in the task workspace. |
read_file |
Read workspace files. |
edit_file / write_file |
Modify or create workspace files. |
get_status |
Check remaining budget and patch status. |
submit_patch |
Submit the generated Git diff. |
get_code_neighbors |
Find related symbols and callers. |
search_similar_code |
Find semantically similar code. |
get_code_subgraph |
Inspect relationships among code symbols. |
load_skill_resource |
Read a knowledge file from a skill attached to the agent. |
run_skill_script |
Run an attached skill's script inside the task sandbox. |
The submission configures the repo-navigation skill. Its instructions are available through the harness skill tools.
- Python 3 and
uvinstalled (uvxsupplies the Kaggle CLI automatically). - A Kaggle account with the competition rules accepted and access to the Gemma 4 model and wheelhouse dataset.
- A Kaggle API token available as
KAGGLE_API_TOKENin the root.envfile or shell environment. - A Langfuse project if you want automatic post-run trace upload. Langfuse runs locally after evaluation; the Kaggle notebook remains offline.
- In
notebooks/kernel-metadata.json, setidto your Kaggle username and a notebook slug, such asyour-kaggle-name/gemma-agent-run. Keepmachine_shapeset toNvidiaL4; the run uses Kaggle's L4 x4 competition accelerator with notebook internet disabled.
Copy the environment template and fill in the services you use:
cp .env.example .envKAGGLE_API_TOKEN="your-kaggle-api-token"
VLLM_API_KEY="your-vllm-key"
LANGFUSE_PUBLIC_KEY="pk-lf-your-public-key"
LANGFUSE_SECRET_KEY="sk-lf-your-secret-key"
LANGFUSE_BASE_URL="https://us.cloud.langfuse.com"Use the Langfuse base URL for your project region. The launchers detect whether all three Langfuse settings are present. When they are absent, evaluation still runs and trace upload is skipped with a message.
The launchers fetch Kaggle CLI 2.2.4 with uvx, and the post-run importer runs Langfuse 4.x in an isolated uv environment. You do not need either package in the project virtual environment. Your local machine needs internet to contact Kaggle and Langfuse; the notebook run itself has internet disabled.
Python 3.12 is needed for local evaluation. Restore the environment from the lockfile:
uv venv --python 3.12.8 .venv
uv pip install --python .venv/bin/python -r requirements.lock.txtrequirements.in lists the direct local requirements; requirements.lock.txt pins their resolved dependencies. Use this project .venv for local agent and harness commands. The organizer's swegemma package requires some model libraries even when inference runs on a remote server; these dependencies are included, but no local model server or GPU runtime is configured.
Start Docker Desktop and build the sandbox image, pinned to Python 3.13.15:
docker build --platform linux/amd64 -t swebench-sandbox:latest -f data/docker/Dockerfile.sandbox data/dockerThe amd64 image matches the supplied Linux wheels. On Apple Silicon, Docker uses emulation, so run times may differ. Task datasets and organizer wheels must be obtained separately; they are excluded from Git. The checked local setup currently targets fastapi_11194.
Keep physical assets in one place under data/assets/: snapshots, graphs, embeddings, and task-specific wheel caches. Split folders contain task metadata and symlinks to only the assets their tasks use. Wheel caches are keyed by task ID, so changing split assignments does not require rebuilding them.
python3 scripts/tasks/plan_observed_task_splits.py --materialize
bash scripts/tasks/build_split_wheels.shThe planner accepts the organizer's graphs/ folder or the older root-level asset folders and consolidates their files under data/assets/. Wheel caches are stored in data/assets/task_wheels/<task_id>/; the builder replaces each cache from scratch using the task's declared dependencies.
Task wheels target Python 3.13 on Linux amd64, matching the sandbox; the host environment uses Python 3.12. The builder repairs incompatible cached wheels before skipping existing caches. To repair caches independently, preserving existing files:
docker run --platform linux/amd64 --rm \
-v "$PWD:/workspace" -w /workspace \
python:3.13-slim python scripts/tasks/repair_task_wheels.pyAfter repair, verify a saved agent patch without calling the model again:
.venv/bin/python scripts/runs/evaluate.py --task-id fastapi_14786 \
--patch-file logs/remote/base/train/fastapi_14786/results/fastapi_14786.patchThe standalone evaluator saves reverification results at logs/remote/<split>/<task_id>/checks/reverification/result.json. Pipeline artifacts are organized separately under logs/remote/base/ and logs/remote/<collaborator>/itr-<n>/.
This runs without a model and confirms the local evaluation setup:
.venv/bin/python scripts/runs/evaluate.py --reference-checkThe local inference server must expose the competition model and support automatic tool calling. Start it, then set the endpoint and run:
LOCAL_INFERENCE_URL=http://localhost:8000/v1 .venv/bin/python scripts/runs/evaluate.pyThis runner uses fastapi_11194, allows 50 tool calls and 30 minutes, and writes under logs/remote/val/fastapi_11194/checks/baseline/. Reference patches are used only with --reference-check.
The runner uses LiteLLM. Its default remains the remote vLLM server; select a
hosted provider with --provider and --model. Store the matching key in the
root .env file:
VLLM_API_KEY=your-key-here
OPENAI_API_KEY=your-openai-key
ANTHROPIC_API_KEY=your-anthropic-key
GEMINI_API_KEY=your-gemini-keyRun the base-model pipeline (or pass --api-base for a different endpoint):
.venv/bin/python scripts/runs/task_pipeline.py --val --task-id fastapi_11194 \
--submission src/rishav/itr-1 \
--api-base https://your-model-server/v1 --model gemma4Examples for hosted models:
.venv/bin/python scripts/runs/task_pipeline.py --train --task-id fastapi_11355 \
--submission src/rishav/itr-1 --provider openai --model gpt-4.1
.venv/bin/python scripts/runs/task_pipeline.py --train --task-id fastapi_11355 \
--submission src/rishav/itr-1 --provider anthropic --model claude-sonnet-4-5
.venv/bin/python scripts/runs/task_pipeline.py --train --task-id fastapi_11355 \
--submission src/rishav/itr-1 --provider gemini --model gemini-2.5-proChoose a split with --train, --dev, or --val (or --split train, --split dev, or --split val) and select an agent iteration with --submission src/<collaborator>/itr-<n>. The task must belong to that split. Its task list and asset paths are resolved from the split manifest. Each task writes to logs/remote/<collaborator>/itr-<n>/<split>/<task_id>/: traces/ holds its ATIF trace, logs/ holds console output, and results/ holds its patch and evaluation JSON. For example, src/rishav/itr-1 writes to logs/remote/rishav/itr-1/train/fastapi_14258/. Tasks can run in parallel terminals without creating batch folders. Rerunning a task within the same collaborator iteration replaces that task's previous artifacts. If Langfuse is configured, each completed task trace and evaluation result is uploaded under the same vllm-<split>-<timestamp> session. Pass --skip-langfuse to keep the artifacts local, or --langfuse-session-id ID to choose the session ID.
Run an entire split or select several task IDs; comma-separated IDs also work:
.venv/bin/python scripts/runs/task_pipeline.py --train --all --submission src/rishav/itr-1
.venv/bin/python scripts/runs/task_pipeline.py --dev --all --submission src/rishav/itr-1
.venv/bin/python scripts/runs/task_pipeline.py --dev --task-ids <task-id-1> <task-id-2> --submission src/akshay/itr-1
.venv/bin/python scripts/runs/task_pipeline.py --val --task-ids <task-id-1>,<task-id-2> --submission src/sanyam/itr-1Defaults allow 25 tool calls, 10 minutes, and 4096 output tokens. For other tasks, provide matching task, snapshot, graph, embedding, and wheel paths.
Run the same balanced 20 validation tasks using src/rishav/inspiration-one:
bash scripts/runs/run_haiku20.sh
bash scripts/runs/run_luna20.shRun one model command at a time. --parallel N controls the number of new,
independent task_pipeline.py processes per task, then the runner moves to the next task.
The Haiku script defaults to two new attempts plus its existing single run via
--baseline-run haiku-5p5-inspiration-one-20-20261017. The existing run is read only
and must have valid results for all selected tasks before starting. Luna defaults
to three fresh attempts. For example, bash scripts/runs/run_luna20.sh --parallel 2
runs two new attempts per task (pass@2 unless a baseline is also supplied). Submission
evaluation limits and token settings are inherited without command-line overrides;
the existing Haiku API compatibility conversion still applies. Luna uses
OPENAI_API_KEY_NEW when set, otherwise OPENAI_API_KEY.
Every new attempt has its own run directory ending in -attempt-N, including
its patches, results, agent traces and model traces.
The parent run directory contains per-attempt console logs and summary.json.
Pass@k counts a task as solved when at least one of its k attempts passes. Errors and missing
results are reported separately from test failures, and mark the summary incomplete.
Haiku runs 40 new attempts; Luna runs 60 by default. Langfuse uploads are disabled for these scripts.
Use --resume to skip recorded passes/failures and retry errors or missing results.
Use a different --run-name NAME for a fresh experiment; existing runs are protected
against accidental replacement. Do not run two resumptions of the same run together.
Run the local trace viewer and open http://127.0.0.1:8765/ to inspect model turns, tool calls, and results:
.venv/bin/python scripts/traces/trace_viewer.pyThe GitHub Pages UI reads the committed viewer/trace-index.json. Regenerate it after adding train traces:
.venv/bin/python scripts/traces/trace_viewer.py --write-indexThe static index includes train traces only by default, matching the artifacts committed under logs/remote/base/train/.
To convert raw Anthropic task folders into Gemma 4 SFT examples with token-level
loss masks, use
scripts/sft_export/export_sft.py. See the
SFT export guide for commands, output format, and
cited template/training references.
Training scripts and run history live in
training/gemma4_e4b_qlora/; the full step-by-step
record, including failures and their causes, is in
LEARNING_NOTES.md.
- Data: 216 tokenized rows from 190 resolved Haiku trajectories (69 tasks). The
conversations export has 220 rows from 192 trajectories;
rich_4076andrich_3894(4 rows) are rejected because the template would strip their retained reasoning. These rows mix train, dev and val tasks, so dev/val are no longer held out. - Trainer:
unsloth_train/train_unsloth.py, one Kaggle L4, offline. Dependencies are bundled as Kaggle datasets: Unsloth 2026.10.6,trl1.15.0,bitsandbytes0.50.2, and an xformers 0.0.35 wheel built from source for the image's Python 3.13 / torch 2.11 cu128 (PyPI has no cp313 wheel). - Hyperparameters: 1 epoch, lr 1e-4 cosine, 5% warmup, global batch 4 (1 x grad accum 4), LoRA r=16 / alpha=32 / dropout 0.05 on language-model projections, 4-bit NF4.
- Status: v5 trained (54 steps, train_loss 1.093); the adapter is published as
akshayggupta1/gemma4-e4b-unsloth-qlora-adapter-v5. A one-task harness run onrich_3052(e2e_kernel/) runs end to end. v15 is the first valid run: tool calls are clean, but it timed out after 3 model calls because our Unsloth server generates at ~4.5 tok/s versus ~75–82 tok/s for the vLLM-served E4B baseline. No fair task-solving number exists for the adapter yet. - Serving (changing): because of that speed gap, the adapter will be served with the
competition's vLLM 0.19.1 as a LoRA on
gemma-4-e4b-it, on the competition's pinned Python 3.12 image. Probe v1 passed (vLLM starts on the pinned image, loads the adapter, and parses tool calls over two turns) at 23.2 tok/s, still below the baseline's 75–82, so the speed gap is not closed. The Unsloth server (unsloth_serve/unsloth_serve.py, parser tests intest_gemma_output.py) is kept for reference only.
uvx --from kaggle==2.2.4 kaggle kernels push -p training/gemma4_e4b_qlora/unsloth_train --accelerator NvidiaL4
uvx --from kaggle==2.2.4 kaggle kernels push -p training/gemma4_e4b_qlora/e2e_kernel --accelerator NvidiaL4
uvx --from kaggle==2.2.4 kaggle kernels status akshayggupta1/gemma4-e2e-one-task-rich3052From the repository root, push and monitor the offline competition notebook:
python3 scripts/runs/run_kaggle_notebook.pyWhen the Langfuse settings are configured, the command performs the full chain:
- Push the notebook with GPU enabled and internet disabled.
- Follow its status and console output until the run reaches a terminal state.
- Download the latest notebook output to a new directory under
/tmp. - Validate and stitch its ATIF files into one Langfuse trace.
- Assign a content-derived session ID so the same output cannot be imported silently twice.
To follow and import the latest existing run without launching another version:
python3 scripts/runs/run_kaggle_notebook.py --follow-onlyUse --skip-langfuse to stop after monitoring, or --langfuse-session-id ID to override the generated session ID. Kaggle CLI downloads the latest run for the notebook handle. To import an already downloaded historical version, use its artifact directory directly:
uv run --with 'langfuse>=4,<5' --python 3.12 \
python scripts/traces/stitch_langfuse_trace.py \
--artifact-dir /tmp/kaggle-run-output-353660769You can also download and import the latest Kaggle output without launching or following the notebook:
uv run --with 'langfuse>=4,<5' --python 3.12 \
python scripts/traces/stitch_langfuse_trace.py \
--kaggle-kernel your-kaggle-name/gemma-agent-runConsole logs are used locally to recover task outcomes but are not uploaded as Langfuse observations by default. Add --include-console-log only when the complete notebook log is useful for debugging. In Langfuse's Tracing table, filter Is Root Observation to True to show one row per run; open that row to inspect the nested agent steps and tool calls.
The automatic download covers the completed notebook's output artifacts and log. Kaggle attaches the competition data, wheelhouse, and Gemma model to the notebook from competition_sources, dataset_sources, and model_sources in notebooks/kernel-metadata.json; those inputs are not copied to your computer. The local vLLM path uses the task snapshot, graph, embeddings, and wheels already under data/.
├── src/ Agent configuration, prompts, and tool reference code
├── scripts/
│ ├── tasks/ Split planning, dependency collection, wheel building and repair
│ ├── runs/ Task pipeline, pass@3 and batch runners, evaluation, Kaggle notebook runs
│ ├── traces/ vLLM trace proxy, trace builder and viewer, Langfuse upload and stitching
│ ├── sft_export/ SFT dataset export and thought rationalization
│ └── tests/ Script tests
├── notebooks/ Kaggle notebook and account-specific metadata
├── training/ LoRA SFT trainer, tests, and offline wheels (ignored)
├── viewer/ GitHub Pages trace viewer, presentation, and eval_ui reports
├── docs/ context/ (competition notes, commits log), reports, assets, resources
├── requirements.lock.txt Python dependencies
├── data/ Local task assets (ignored)
├── wheelhouse/ Organizer wheels (ignored)
├── logs/remote/
├── base/ Original train, dev, and val artifacts
│ └── train/failure analysis.md
└── <collaborator>/itr-<n>/ Pipeline artifacts for each experiment
└── viewer/trace-index.json GitHub Pages trace catalogue (train only)

