In some evals, the teacher produced hallucinations, seemingly due to context rot. This makes the zeta prompt crate's budgeted rendering usable by the teacher, so that it can truncate the list of excerpts. I've also cleaned up the implementation of zeta_prompt's `format_related_files_within_budget`, and changed the behavior so that it filters the the excerpts by priority but renders the files in their original order. Release Notes: - N/A
130 lines
3.9 KiB
Rust
130 lines
3.9 KiB
Rust
use anyhow::{Context as _, Result};
|
|
use collections::HashMap;
|
|
use futures::lock::{Mutex, OwnedMutexGuard};
|
|
use std::{
|
|
cell::RefCell,
|
|
path::{Path, PathBuf},
|
|
sync::Arc,
|
|
};
|
|
|
|
use crate::paths::REPOS_DIR;
|
|
|
|
thread_local! {
|
|
static REPO_LOCKS: RefCell<HashMap<PathBuf, Arc<Mutex<()>>>> = RefCell::new(HashMap::default());
|
|
}
|
|
|
|
#[must_use]
|
|
pub async fn lock_repo(path: impl AsRef<Path>) -> OwnedMutexGuard<()> {
|
|
REPO_LOCKS
|
|
.with(|cell| {
|
|
cell.borrow_mut()
|
|
.entry(path.as_ref().to_path_buf())
|
|
.or_default()
|
|
.clone()
|
|
})
|
|
.lock_owned()
|
|
.await
|
|
}
|
|
|
|
pub async fn run_git(repo_path: &Path, args: &[&str]) -> Result<String> {
|
|
let output = smol::process::Command::new("git")
|
|
.current_dir(repo_path)
|
|
.args(args)
|
|
.output()
|
|
.await?;
|
|
|
|
anyhow::ensure!(
|
|
output.status.success(),
|
|
"`git {}` within `{}` failed with status: {}\nstderr:\n{}\nstdout:\n{}",
|
|
args.join(" "),
|
|
repo_path.display(),
|
|
output.status,
|
|
String::from_utf8_lossy(&output.stderr),
|
|
String::from_utf8_lossy(&output.stdout),
|
|
);
|
|
Ok(String::from_utf8(output.stdout)?.trim().to_string())
|
|
}
|
|
|
|
pub fn parse_repo_url(url: &str) -> Result<(String, String)> {
|
|
if url.contains('@') {
|
|
let (_, path) = url.split_once(':').context("expected : in git url")?;
|
|
let (owner, repo) = path.split_once('/').context("expected / in git url")?;
|
|
Ok((owner.to_string(), repo.trim_end_matches(".git").to_string()))
|
|
} else {
|
|
let parsed = http_client::Url::parse(url)?;
|
|
let mut segments = parsed.path_segments().context("empty http url")?;
|
|
let owner = segments.next().context("expected owner")?;
|
|
let repo = segments.next().context("expected repo")?;
|
|
Ok((owner.to_string(), repo.trim_end_matches(".git").to_string()))
|
|
}
|
|
}
|
|
|
|
pub fn repo_path_for_url(url: &str) -> Result<PathBuf> {
|
|
let (owner, name) = parse_repo_url(url)?;
|
|
Ok(REPOS_DIR.join(&owner).join(&name))
|
|
}
|
|
|
|
pub async fn ensure_repo_cloned(repo_url: &str) -> Result<PathBuf> {
|
|
let repo_path = repo_path_for_url(repo_url)?;
|
|
let _lock = lock_repo(&repo_path).await;
|
|
|
|
// Validate existing repo has correct origin, otherwise remove and re-init.
|
|
let mut git_repo_exists = false;
|
|
if repo_path.is_dir() {
|
|
if run_git(&repo_path, &["remote", "get-url", "origin"])
|
|
.await
|
|
.map_or(false, |origin| origin.trim() == repo_url)
|
|
{
|
|
git_repo_exists = true;
|
|
} else {
|
|
std::fs::remove_dir_all(&repo_path).ok();
|
|
}
|
|
}
|
|
|
|
if !git_repo_exists {
|
|
log::info!("Cloning {} into {:?}", repo_url, repo_path);
|
|
std::fs::create_dir_all(&repo_path)?;
|
|
run_git(&repo_path, &["init"]).await?;
|
|
run_git(&repo_path, &["remote", "add", "origin", repo_url])
|
|
.await
|
|
.ok();
|
|
}
|
|
|
|
// Always fetch to get latest commits
|
|
run_git(&repo_path, &["fetch", "--depth", "1000", "origin"]).await?;
|
|
|
|
// Check if we have a valid HEAD, if not checkout FETCH_HEAD
|
|
let has_head = run_git(&repo_path, &["rev-parse", "HEAD"]).await.is_ok();
|
|
if !has_head {
|
|
// Use reset to set HEAD without needing a branch
|
|
run_git(&repo_path, &["reset", "--hard", "FETCH_HEAD"]).await?;
|
|
}
|
|
|
|
Ok(repo_path)
|
|
}
|
|
|
|
pub async fn fetch_if_needed(repo_path: &Path, revision: &str) -> Result<String> {
|
|
let resolved = run_git(
|
|
repo_path,
|
|
&["rev-parse", &format!("{}^{{commit}}", revision)],
|
|
)
|
|
.await;
|
|
|
|
if let Ok(sha) = resolved {
|
|
return Ok(sha);
|
|
}
|
|
|
|
if run_git(repo_path, &["fetch", "--depth", "1", "origin", revision])
|
|
.await
|
|
.is_err()
|
|
{
|
|
run_git(repo_path, &["fetch", "origin"]).await?;
|
|
}
|
|
|
|
run_git(
|
|
repo_path,
|
|
&["rev-parse", &format!("{}^{{commit}}", revision)],
|
|
)
|
|
.await
|
|
}
|