ep_cli: Allow dynamically loading prompts (#48046)
Closes #ISSUE Release Notes: - N/A *or* Added/Fixed/Improved ...
This commit is contained in:
1 parent
b029238af2
commit
3a2a43ab84
7 files changed
+106
-14
No files matched your search
@@ -62,13 +62,16 @@ rand.workspace = true
|
||||
similar = "2.7.0"
|
||||
flate2 = "1.1.8"
|
||||
toml.workspace = true
|
||||
rust-embed.workspace = true
|
||||
rust-embed = { workspace = true, features = ["debug-embed"] }
|
||||
|
||||
# Wasmtime is included as a dependency in order to enable the same
|
||||
# features that are enabled in Zed.
|
||||
#
|
||||
# If we don't enable these features we get crashes when creating
|
||||
# a Tree-sitter WasmStore.
|
||||
[features]
|
||||
dynamic_prompts = []
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["wasmtime"]
|
||||
|
||||
|
||||
@@ -18,17 +18,24 @@
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use clap::Args;
|
||||
use collections::HashMap;
|
||||
use rust_embed::RustEmbed;
|
||||
use serde::Deserialize;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufRead, BufReader, BufWriter, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "../languages/src/"]
|
||||
#[include = "*/config.toml"]
|
||||
struct LanguageConfigs;
|
||||
#[cfg(not(feature = "dynamic_prompts"))]
|
||||
mod language_configs_embedded {
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "../languages/src/"]
|
||||
#[include = "*/config.toml"]
|
||||
pub struct LanguageConfigs;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "dynamic_prompts"))]
|
||||
use language_configs_embedded::LanguageConfigs;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct LanguageConfig {
|
||||
@@ -89,6 +96,7 @@ pub struct FilterLanguagesArgs {
|
||||
pub show_top_excluded: Option<usize>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "dynamic_prompts"))]
|
||||
fn build_extension_to_language_map() -> HashMap<String, String> {
|
||||
let mut map = HashMap::default();
|
||||
|
||||
@@ -113,6 +121,42 @@ fn build_extension_to_language_map() -> HashMap<String, String> {
|
||||
map
|
||||
}
|
||||
|
||||
#[cfg(feature = "dynamic_prompts")]
|
||||
fn build_extension_to_language_map() -> HashMap<String, String> {
|
||||
const LANGUAGES_SRC_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../languages/src");
|
||||
|
||||
let mut map = HashMap::default();
|
||||
|
||||
let languages_dir = Path::new(LANGUAGES_SRC_DIR);
|
||||
let entries = match std::fs::read_dir(languages_dir) {
|
||||
Ok(e) => e,
|
||||
Err(_) => return map,
|
||||
};
|
||||
|
||||
for entry in entries.flatten() {
|
||||
let config_path = entry.path().join("config.toml");
|
||||
if !config_path.exists() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let content_str = match std::fs::read_to_string(&config_path) {
|
||||
Ok(s) => s,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let config: LanguageConfig = match toml::from_str(&content_str) {
|
||||
Ok(c) => c,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
for suffix in &config.path_suffixes {
|
||||
map.insert(suffix.to_lowercase(), config.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
fn get_all_languages(extension_map: &HashMap<String, String>) -> Vec<(String, Vec<String>)> {
|
||||
let mut language_to_extensions: HashMap<String, Vec<String>> = HashMap::default();
|
||||
|
||||
|
||||
@@ -164,7 +164,6 @@ pub fn zeta2_output_for_patch(
|
||||
pub struct TeacherPrompt;
|
||||
|
||||
impl TeacherPrompt {
|
||||
const PROMPT: &str = include_str!("prompts/teacher.md");
|
||||
pub(crate) const EDITABLE_REGION_START: &str = "<|editable_region_start|>\n";
|
||||
pub(crate) const EDITABLE_REGION_END: &str = "\n<|editable_region_end|>";
|
||||
pub(crate) const USER_CURSOR_MARKER: &str = "<|user_cursor|>";
|
||||
@@ -182,7 +181,8 @@ impl TeacherPrompt {
|
||||
let context = Self::format_context(example);
|
||||
let cursor_excerpt = Self::format_cursor_excerpt(example, editable_range, context_range);
|
||||
|
||||
let prompt = Self::PROMPT
|
||||
let prompt_template = crate::prompt_assets::get_prompt("teacher.md");
|
||||
let prompt = prompt_template
|
||||
.replace("{{context}}", &context)
|
||||
.replace("{{edit_history}}", &edit_history)
|
||||
.replace("{{cursor_excerpt}}", &cursor_excerpt);
|
||||
|
||||
@@ -12,6 +12,7 @@ mod parse_output;
|
||||
mod paths;
|
||||
mod predict;
|
||||
mod progress;
|
||||
mod prompt_assets;
|
||||
mod pull_examples;
|
||||
mod qa;
|
||||
mod reorder_patch;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[cfg(feature = "dynamic_prompts")]
|
||||
pub fn get_prompt(name: &'static str) -> Cow<'static, str> {
|
||||
use anyhow::Context;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::sync::{LazyLock, RwLock};
|
||||
|
||||
const PROMPTS_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/src/prompts");
|
||||
|
||||
static PROMPT_CACHE: LazyLock<RwLock<HashMap<&'static str, &'static str>>> =
|
||||
LazyLock::new(|| RwLock::new(HashMap::default()));
|
||||
|
||||
let filesystem_path = Path::new(PROMPTS_DIR).join(name);
|
||||
if let Some(cached_contents) = PROMPT_CACHE.read().unwrap().get(name) {
|
||||
return Cow::Borrowed(cached_contents);
|
||||
}
|
||||
let contents = std::fs::read_to_string(&filesystem_path)
|
||||
.context(name)
|
||||
.expect("Failed to read prompt");
|
||||
let leaked = contents.leak();
|
||||
PROMPT_CACHE.write().unwrap().insert(name, leaked);
|
||||
return Cow::Borrowed(leaked);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "dynamic_prompts"))]
|
||||
pub fn get_prompt(name: &'static str) -> Cow<'static, str> {
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "src/prompts"]
|
||||
struct EmbeddedPrompts;
|
||||
|
||||
match EmbeddedPrompts::get(name) {
|
||||
Some(file) => match file.data {
|
||||
Cow::Borrowed(bytes) => {
|
||||
Cow::Borrowed(std::str::from_utf8(bytes).expect("prompt file is not valid UTF-8"))
|
||||
}
|
||||
Cow::Owned(bytes) => {
|
||||
Cow::Owned(String::from_utf8(bytes).expect("prompt file is not valid UTF-8"))
|
||||
}
|
||||
},
|
||||
None => panic!("prompt file not found: {name}"),
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,6 @@ use serde::{Deserialize, Serialize};
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
const PROMPT_TEMPLATE: &str = include_str!("prompts/qa.md");
|
||||
|
||||
/// Arguments for the QA command.
|
||||
#[derive(Debug, Clone, clap::Args)]
|
||||
pub struct QaArgs {
|
||||
@@ -94,8 +92,9 @@ pub fn build_prompt(example: &Example) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
let prompt_template = crate::prompt_assets::get_prompt("qa.md");
|
||||
Some(
|
||||
PROMPT_TEMPLATE
|
||||
prompt_template
|
||||
.replace("{edit_history}", &edit_history)
|
||||
.replace("{cursor_excerpt}", &cursor_excerpt)
|
||||
.replace("{actual_patch_word_diff}", &actual_patch_word_diff),
|
||||
|
||||
@@ -16,8 +16,6 @@ use anyhow::Result;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
const PROMPT_TEMPLATE: &str = include_str!("prompts/repair.md");
|
||||
|
||||
/// Arguments for the repair command.
|
||||
#[derive(Debug, Clone, clap::Args)]
|
||||
pub struct RepairArgs {
|
||||
@@ -91,8 +89,9 @@ pub fn build_repair_prompt(example: &Example) -> Option<String> {
|
||||
.confidence
|
||||
.map_or("unknown".to_string(), |v| v.to_string());
|
||||
|
||||
let prompt_template = crate::prompt_assets::get_prompt("repair.md");
|
||||
Some(
|
||||
PROMPT_TEMPLATE
|
||||
prompt_template
|
||||
.replace("{edit_history}", &edit_history)
|
||||
.replace("{context}", &context)
|
||||
.replace("{cursor_excerpt}", &cursor_excerpt)
|
||||
|
||||
Reference in new issue
Block a user