zeta eval: --repeat flag (#42569)

Adds a `--repeat` flag to the zeta eval that runs each example as many
times as specified. Also makes the output nicer in a few ways.

Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
Co-authored-by: Michael <michael@zed.dev>
This commit is contained in:
Agus Zubiaga
2025-11-12 16:58:22 -05:00
committed by GitHub
co-authored by Ben Kunkle Michael
parent f2a1eb9963
commit b0700a4625
7 changed files with 342 additions and 145 deletions
+55 -67
View File
@@ -12,7 +12,6 @@ use gpui::{AppContext, AsyncApp, Entity};
use language::{Anchor, Buffer, Point};
use project::Project;
use serde::Deserialize;
use std::cell::Cell;
use std::fs;
use std::io::Write;
use std::ops::Range;
@@ -20,7 +19,7 @@ use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::{Duration, Instant};
use zeta2::{EvalCache, EvalCacheEntryKind, EvalCacheKey};
use zeta2::{EvalCache, EvalCacheEntryKind, EvalCacheKey, Zeta};
#[derive(Debug, Args)]
pub struct PredictArguments {
@@ -35,10 +34,12 @@ pub struct PredictArguments {
cache: CacheMode,
}
#[derive(Debug, ValueEnum, Default, Clone, Copy)]
#[derive(Debug, ValueEnum, Default, Clone, Copy, PartialEq)]
pub enum CacheMode {
/// Use cached LLM requests and responses, based on the hash of the prompt and the endpoint.
/// Use cached LLM requests and responses, except when multiple repetitions are requested
#[default]
Auto,
/// Use cached LLM requests and responses, based on the hash of the prompt and the endpoint.
#[value(alias = "request")]
Requests,
/// Ignore existing cache entries for both LLM and search.
@@ -50,12 +51,22 @@ pub enum CacheMode {
impl CacheMode {
fn use_cached_llm_responses(&self) -> bool {
self.assert_not_auto();
matches!(self, CacheMode::Requests | CacheMode::Force)
}
fn use_cached_search_results(&self) -> bool {
self.assert_not_auto();
matches!(self, CacheMode::Force)
}
fn assert_not_auto(&self) {
assert_ne!(
*self,
CacheMode::Auto,
"Cache mode should not be auto at this point!"
);
}
}
#[derive(clap::ValueEnum, Debug, Clone)]
@@ -71,72 +82,49 @@ pub async fn run_zeta2_predict(
cx: &mut AsyncApp,
) {
let example = NamedExample::load(args.example_path).unwrap();
let (project, mut zetas, _edited_buffers) =
example.setup_project(app_state, 1, cx).await.unwrap();
let result = zeta2_predict(
example,
project,
zetas.remove(0),
None,
args.prompt_format,
args.use_expected_context,
args.cache,
&app_state,
cx,
)
.await
.unwrap();
result.write(args.format, std::io::stdout()).unwrap();
print_run_data_dir();
}
thread_local! {
static AUTHENTICATED: Cell<bool> = const { Cell::new(false) };
print_run_data_dir(true);
}
pub async fn zeta2_predict(
example: NamedExample,
project: Entity<Project>,
zeta: Entity<Zeta>,
repetition_ix: Option<u16>,
prompt_format: PromptFormat,
use_expected_context: bool,
cache_mode: CacheMode,
app_state: &Arc<ZetaCliAppState>,
mut cache_mode: CacheMode,
cx: &mut AsyncApp,
) -> Result<PredictionDetails> {
let worktree_path = example.setup_worktree().await?;
if !AUTHENTICATED.get() {
AUTHENTICATED.set(true);
app_state
.client
.sign_in_with_optional_connect(true, cx)
.await?;
if repetition_ix.is_some() {
if cache_mode != CacheMode::Auto && cache_mode != CacheMode::Skip {
panic!("Repetitions are not supported in Auto cache mode");
} else {
cache_mode = CacheMode::Skip;
}
} else if cache_mode == CacheMode::Auto {
cache_mode = CacheMode::Requests;
}
let project = cx.update(|cx| {
Project::local(
app_state.client.clone(),
app_state.node_runtime.clone(),
app_state.user_store.clone(),
app_state.languages.clone(),
app_state.fs.clone(),
None,
cx,
)
})?;
let buffer_store = project.read_with(cx, |project, _| project.buffer_store().clone())?;
let worktree = project
.update(cx, |project, cx| {
project.create_worktree(&worktree_path, true, cx)
})?
.await?;
worktree
.read_with(cx, |worktree, _cx| {
worktree.as_local().unwrap().scan_complete()
})?
.await;
let zeta = cx.update(|cx| zeta2::Zeta::global(&app_state.client, &app_state.user_store, cx))?;
let example_run_dir = RUN_DIR.join(&example.file_name());
let mut example_run_dir = RUN_DIR.join(&example.file_name());
if let Some(repetition_ix) = repetition_ix {
example_run_dir = example_run_dir.join(format!("{:03}", repetition_ix));
}
fs::create_dir_all(&example_run_dir)?;
if LATEST_EXAMPLE_RUN_DIR.exists() {
fs::remove_file(&*LATEST_EXAMPLE_RUN_DIR)?;
@@ -157,23 +145,9 @@ pub async fn zeta2_predict(
}));
})?;
cx.subscribe(&buffer_store, {
let project = project.clone();
move |_, event, cx| match event {
project::buffer_store::BufferStoreEvent::BufferAdded(buffer) => {
zeta2::Zeta::try_global(cx)
.unwrap()
.update(cx, |zeta, cx| zeta.register_buffer(&buffer, &project, cx));
}
_ => {}
}
})?
.detach();
let _edited_buffers = example.apply_edit_history(&project, cx).await?;
let (cursor_buffer, cursor_anchor) = example.cursor_position(&project, cx).await?;
let result = Arc::new(Mutex::new(PredictionDetails::default()));
let result = Arc::new(Mutex::new(PredictionDetails::new(example_run_dir.clone())));
let mut debug_rx = zeta.update(cx, |zeta, _| zeta.debug_info())?;
let debug_task = cx.background_spawn({
@@ -397,7 +371,7 @@ impl EvalCache for RunCache {
self.link_to_run(&key);
Some(fs::read_to_string(path).unwrap())
} else {
log::info!("Skipping cached entry: {}", path.display());
log::trace!("Skipping cached entry: {}", path.display());
None
}
} else if matches!(self.cache_mode, CacheMode::Force) {
@@ -417,14 +391,14 @@ impl EvalCache for RunCache {
fs::write(&input_path, input).unwrap();
let output_path = RunCache::output_cache_path(&key);
log::info!("Writing cache entry: {}", output_path.display());
log::trace!("Writing cache entry: {}", output_path.display());
fs::write(&output_path, output).unwrap();
self.link_to_run(&key);
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PredictionDetails {
pub diff: String,
pub excerpts: Vec<ActualExcerpt>,
@@ -433,9 +407,23 @@ pub struct PredictionDetails {
pub running_search_time: Option<Duration>,
pub prediction_time: Duration,
pub total_time: Duration,
pub run_example_dir: PathBuf,
}
impl PredictionDetails {
pub fn new(run_example_dir: PathBuf) -> Self {
Self {
diff: Default::default(),
excerpts: Default::default(),
excerpts_text: Default::default(),
planning_search_time: Default::default(),
running_search_time: Default::default(),
prediction_time: Default::default(),
total_time: Default::default(),
run_example_dir,
}
}
pub fn write(&self, format: PredictionsOutputFormat, mut out: impl Write) -> Result<()> {
let formatted = match format {
PredictionsOutputFormat::Md => self.to_markdown(),