feat(caretaker-evals): add Cloud Run job entrypoint for eval runner (#28727)

This commit is contained in:
Chad
2026-08-07 14:46:30 -05:00
committed by GitHub
parent 1b53dfea2b
commit cd5ac173cf
3 changed files with 119 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
# ==============================================================================
# Caretaker Triage Evaluation Runner Container (Cloud Run Job)
#
# Placed at repository root to allow `gcloud run jobs deploy --source .` to:
# 1. Automatically detect this Dockerfile without separate build steps.
# 2. Access both /cloudrun/triage-worker and /evals inside the root build context.
# ==============================================================================
FROM python:3.13-slim
WORKDIR /app
ENV PYTHONUNBUFFERED=1
RUN apt-get update && apt-get install -y git curl && rm -rf /var/lib/apt/lists/*
# 1. Pre-bake target gemini-cli repo clone into container image
RUN git clone https://github.com/google-gemini/gemini-cli.git /app/evals/triage/target_repo
# 2. Copy living local application code from root build context
COPY cloudrun/triage-worker /app/cloudrun/triage-worker
COPY evals /app/evals
RUN pip install --no-cache-dir -r /app/cloudrun/triage-worker/requirements.txt \
&& pip install --no-cache-dir -r /app/evals/triage/requirements.txt
WORKDIR /app/evals/triage
ENV PYTHONPATH=/app
CMD ["python3", "cloud_runner.py"]
@@ -0,0 +1,39 @@
"""
Cloud Run Job Entrypoint for Gemini CLI Triage Evaluation Suite.
Reads EVAL_CONFIG JSON environment variable, invokes run_suite(), and syncs results to GCS.
"""
import os
import json
from evals.triage.runner import run_suite
from evals.triage.helpers.sync_to_gcs import sync_results_to_gcs
def main() -> None:
config_str = os.environ.get("EVAL_CONFIG", "{}")
try:
cfg = json.loads(config_str) if config_str else {}
if not isinstance(cfg, dict):
raise ValueError(f"EVAL_CONFIG must be a JSON object, got {type(cfg).__name__}")
except json.JSONDecodeError as e:
raise ValueError(f"Invalid EVAL_CONFIG JSON: {e}") from e
print("========================================================")
print(" 🚀 Running Gemini CLI Triage Evaluation Suite (Cloud Run)")
print("========================================================")
if cfg:
print(f"[EVAL_CONFIG] Loaded configuration: {cfg}")
# 1. Execute benchmark suite directly via run_suite()
run_suite(
filter_issues=cfg.get("issues"),
concurrency=cfg.get("concurrency", 5),
note=cfg.get("note")
)
# 2. Sync evaluation run results to GCS bucket
sync_results_to_gcs()
if __name__ == "__main__":
main()
@@ -0,0 +1,52 @@
"""
Helper script to sync evaluation run results from local container disk to GCS bucket.
"""
import os
from google.cloud import storage
def sync_results_to_gcs() -> None:
bucket_name = os.environ.get("EVAL_RESULTS_BUCKET", "triage-eval-results")
runs_dir = "results/runs"
if not os.path.exists(runs_dir):
print(f"⚠️ Warning: No '{runs_dir}' directory found to sync to GCS.")
return
print("\n========================================================")
print(" 📤 Syncing evaluation run results to GCS")
print(f" Bucket: gs://{bucket_name}/runs/")
print("========================================================")
try:
client = storage.Client()
bucket = client.bucket(bucket_name)
count = 0
run_folders = [d for d in os.listdir(runs_dir) if os.path.isdir(os.path.join(runs_dir, d))]
run_dest = f"gs://{bucket_name}/runs/{run_folders[0]}/" if run_folders else f"gs://{bucket_name}/runs/"
for root, _, files in os.walk(runs_dir):
for file in files:
local_path = os.path.join(root, file)
rel_path = os.path.relpath(local_path, runs_dir)
blob_path = f"runs/{rel_path}"
blob = bucket.blob(blob_path)
# Set explicit charset=utf-8 on GCS blobs so web browsers and Caretaker Dashboard render markdown emojis cleanly.
if file.endswith(".md"):
blob.upload_from_filename(local_path, content_type="text/markdown; charset=utf-8")
elif file.endswith(".json"):
blob.upload_from_filename(local_path, content_type="application/json; charset=utf-8")
else:
blob.upload_from_filename(local_path)
count += 1
print(f"✅ Successfully uploaded {count} result artifact(s) to {run_dest}\n")
except Exception as e:
print(f"❌ Error: Failed to upload evaluation results to GCS: {e}")
raise
if __name__ == "__main__":
sync_results_to_gcs()