feat(compaction): support prompt file override

Co-authored-by: philau2512 <dplau25122002@gmail.com>
This commit is contained in:
leookun
2026-08-27 01:22:58 +08:00
co-authored by philau2512
parent 995a12df44
commit cfc333dde4
4 changed files with 63 additions and 9 deletions
+4 -1
View File
@@ -35,7 +35,10 @@ impl App {
.set_port(store.port_settings().await?.service_port);
}
let assets = PromptAssets::embedded()?;
let compiler = PromptCompiler::new(assets);
let compiler = PromptCompiler::with_compaction_prompt_path(
assets,
crate::config::compaction_prompt_path()?,
);
let provider = std::sync::Arc::new(ProviderRouter::new(
store.clone(),
config.provider_request_timeout,
+36
View File
@@ -9,6 +9,7 @@ const DATA_DIR_NAME: &str = ".cursor-byok-v3";
const DATABASE_FILE_NAME: &str = "cursor-byok.db";
const V0049_DATA_DIR_NAME: &str = ".cursor-local-assistant-v2";
const V0049_CONFIG_FILE_NAME: &str = "config.yaml";
const COMPACTION_PROMPT_PATH: &str = "prompts/compaction.md";
pub fn managed_data_dir() -> Result<PathBuf> {
let home_dir = dirs::home_dir()
@@ -28,6 +29,26 @@ pub fn v0049_config_path() -> Result<PathBuf> {
.join(V0049_CONFIG_FILE_NAME))
}
pub fn compaction_prompt_path() -> Result<PathBuf> {
Ok(managed_data_dir()?.join(COMPACTION_PROMPT_PATH))
}
pub fn compaction_prompt_override() -> Result<Option<String>> {
compaction_prompt_override_at(&compaction_prompt_path()?)
}
pub(crate) fn compaction_prompt_override_at(path: &std::path::Path) -> Result<Option<String>> {
match fs::read_to_string(path) {
Ok(prompt) if prompt.trim().is_empty() => Ok(None),
Ok(prompt) => Ok(Some(prompt)),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(error) => Err(Error::Config(format!(
"cannot read compaction prompt at {}: {error}",
path.display()
))),
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ProviderKind {
OpenAiChat,
@@ -156,6 +177,21 @@ fn database_url_for_dir(data_dir: &std::path::Path) -> Result<String> {
mod tests {
use super::*;
#[test]
fn compaction_prompt_override_is_optional_and_reloaded() {
let directory = tempfile::tempdir().unwrap();
let path = directory.path().join("compaction.md");
assert_eq!(compaction_prompt_override_at(&path).unwrap(), None);
fs::write(&path, " \n").unwrap();
assert_eq!(compaction_prompt_override_at(&path).unwrap(), None);
fs::write(&path, "custom prompt").unwrap();
assert_eq!(
compaction_prompt_override_at(&path).unwrap().as_deref(),
Some("custom prompt")
);
}
#[tokio::test]
async fn managed_database_supports_home_paths_with_spaces() {
let directory = tempfile::tempdir().unwrap();
+19 -7
View File
@@ -1,4 +1,4 @@
use std::collections::BTreeMap;
use std::{collections::BTreeMap, path::PathBuf};
use crate::{
model::{ModelSpec, PromptSpec, ToolDefinition},
@@ -10,11 +10,22 @@ use super::{assets::runtime_expression, Mode, PromptAssets};
#[derive(Clone)]
pub struct PromptCompiler {
assets: PromptAssets,
compaction_prompt_path: Option<PathBuf>,
}
impl PromptCompiler {
pub fn new(assets: PromptAssets) -> Self {
Self { assets }
Self {
assets,
compaction_prompt_path: None,
}
}
pub fn with_compaction_prompt_path(assets: PromptAssets, path: PathBuf) -> Self {
Self {
assets,
compaction_prompt_path: Some(path),
}
}
pub fn runtime_message(&self, mode: Mode, values: &BTreeMap<&str, String>) -> Result<String> {
@@ -39,12 +50,13 @@ impl PromptCompiler {
.display_name
.as_deref()
.unwrap_or(model.model_id.as_str());
let prompt = match (mode, &self.compaction_prompt_path) {
(Mode::Compaction, Some(path)) => crate::config::compaction_prompt_override_at(path)?
.unwrap_or_else(|| self.assets.mode(mode).prompt.clone()),
_ => self.assets.mode(mode).prompt.clone(),
};
Ok(PromptSpec {
instructions: self
.assets
.mode(mode)
.prompt
.replace("{{FAKE_MODEL_NAME}}", fake_model_name),
instructions: prompt.replace("{{FAKE_MODEL_NAME}}", fake_model_name),
tools,
})
}
+4 -1
View File
@@ -541,6 +541,9 @@ impl RunEngine {
model.max_output_tokens = Some(COMPACTION_OUTPUT_TOKENS);
model.reasoning.enabled = false;
model.reasoning.effort = None;
let instructions = crate::config::compaction_prompt_override()
.map_err(|error| RunOutcome::Failed(error.into()))?
.unwrap_or_else(|| COMPACTION_INSTRUCTIONS.into());
let invocation = crate::model::ModelInvocation {
call_id: format!("{}:{provider_call_index}", prepared.run_id),
run_id: prepared.run_id.to_string(),
@@ -548,7 +551,7 @@ impl RunEngine {
provider_call_index,
request: crate::model::ModelRequest {
prompt: crate::model::PromptSpec {
instructions: COMPACTION_INSTRUCTIONS.into(),
instructions,
tools: Vec::new(),
},
model,