prompt: close whitespace-control include validation gap
This commit is contained in:
Generated
+1
-1
@@ -4795,7 +4795,6 @@ dependencies = [
|
||||
"fs4",
|
||||
"llm-engine",
|
||||
"manifest",
|
||||
"minijinja",
|
||||
"protocol",
|
||||
"pulldown-cmark",
|
||||
"ratatui",
|
||||
@@ -4809,6 +4808,7 @@ dependencies = [
|
||||
"toml",
|
||||
"unicode-width",
|
||||
"uuid",
|
||||
"worker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -1372,6 +1372,8 @@ fn parse_static_template_includes(template: &str, source: &str) -> Result<Vec<St
|
||||
break;
|
||||
};
|
||||
let body = after_open[..close].trim();
|
||||
let body = body.strip_prefix('-').unwrap_or(body).trim_start();
|
||||
let body = body.strip_suffix('-').unwrap_or(body).trim_end();
|
||||
if body.starts_with("include") {
|
||||
let argument = body["include".len()..].trim();
|
||||
let bytes = argument.as_bytes();
|
||||
|
||||
@@ -25,7 +25,7 @@ session-store = { workspace = true }
|
||||
fs4 = { workspace = true }
|
||||
ticket = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
minijinja = "2.19.0"
|
||||
worker = { path = "../worker" }
|
||||
pulldown-cmark = { version = "0.13.3", default-features = false }
|
||||
llm-engine.workspace = true
|
||||
|
||||
|
||||
@@ -69,8 +69,7 @@ use render::{PanelListRow, row_hit_boxes};
|
||||
|
||||
const MAX_ENTRIES: usize = 50;
|
||||
const CLOSED_VISIBLE_ROWS: usize = 3;
|
||||
const ORCHESTRATOR_IDLE_QUEUE_NOTICE_TEMPLATE: &str =
|
||||
include_str!("../../../../resources/prompts/panel/orchestrator_idle_queue_notice.md");
|
||||
const ORCHESTRATOR_IDLE_QUEUE_NOTICE_PROMPT: &str = "panel.orchestrator_idle_queue_notice";
|
||||
const ORCHESTRATOR_QUEUE_ATTENTION_MAX_TICKETS: usize = 6;
|
||||
const ORCHESTRATOR_QUEUE_ATTENTION_MAX_TEXT_CHARS: usize = 120;
|
||||
const ORCHESTRATOR_QUEUE_ATTENTION_MAX_MESSAGE_CHARS: usize = 2_400;
|
||||
@@ -3791,15 +3790,9 @@ fn orchestrator_queue_template_ticket(
|
||||
|
||||
fn render_orchestrator_queue_attention_template(
|
||||
context: &OrchestratorQueueTemplateContext,
|
||||
) -> Result<String, minijinja::Error> {
|
||||
let mut env = minijinja::Environment::new();
|
||||
env.set_undefined_behavior(minijinja::UndefinedBehavior::Strict);
|
||||
env.add_template(
|
||||
"orchestrator_idle_queue_notice",
|
||||
ORCHESTRATOR_IDLE_QUEUE_NOTICE_TEMPLATE,
|
||||
)?;
|
||||
env.get_template("orchestrator_idle_queue_notice")?
|
||||
.render(context)
|
||||
) -> Result<String, worker::CatalogError> {
|
||||
worker::PromptCatalog::builtins_only()?
|
||||
.render_serializable(ORCHESTRATOR_IDLE_QUEUE_NOTICE_PROMPT, context)
|
||||
}
|
||||
|
||||
fn orchestrator_work_set_detail(
|
||||
|
||||
@@ -230,6 +230,14 @@ impl PromptCatalog {
|
||||
self.projection.templates.contains_key(key)
|
||||
}
|
||||
|
||||
pub fn render_serializable<T: Serialize + ?Sized>(
|
||||
&self,
|
||||
key: &str,
|
||||
context: &T,
|
||||
) -> Result<String, CatalogError> {
|
||||
self.render_name(key, Value::from_serialize(context))
|
||||
}
|
||||
|
||||
pub fn render_name(&self, key: &str, ctx: Value) -> Result<String, CatalogError> {
|
||||
let template = self
|
||||
.env
|
||||
@@ -538,7 +546,7 @@ mod tests {
|
||||
#[test]
|
||||
fn graph_rejects_dynamic_legacy_missing_and_cycles() {
|
||||
let invalid = BTreeMap::from([
|
||||
("a".into(), "{% include target %}".into()),
|
||||
("a".into(), "{%- include target -%}".into()),
|
||||
("target".into(), "ok".into()),
|
||||
]);
|
||||
assert!(validate_prompt_templates(&invalid).is_err());
|
||||
@@ -546,7 +554,7 @@ mod tests {
|
||||
let legacy = BTreeMap::from([("a".into(), "{% include \"legacy/default\" %}".into())]);
|
||||
assert!(validate_prompt_templates(&legacy).is_err());
|
||||
|
||||
let missing = BTreeMap::from([("a".into(), "{% include \"missing\" %}".into())]);
|
||||
let missing = BTreeMap::from([("a".into(), "{%- include \"missing\" -%}".into())]);
|
||||
assert!(validate_prompt_templates(&missing).is_err());
|
||||
|
||||
let cycle = BTreeMap::from([
|
||||
|
||||
@@ -130,8 +130,8 @@ mod tests {
|
||||
.unwrap()])
|
||||
.unwrap();
|
||||
for source in [
|
||||
r#"{ prompts = { common = { language = "{% include target %}"; }; }; }"#,
|
||||
r#"{ prompts = { common = { language = "{% include \"missing\" %}"; }; }; }"#,
|
||||
r#"{ prompts = { common = { language = "{%- include target -%}"; }; }; }"#,
|
||||
r#"{ prompts = { common = { language = "{%- include \"missing\" -%}"; }; }; }"#,
|
||||
r#"{ prompts = { common = { language = "{% include \"common.workspace\" %}"; workspace = "{% include \"common.language\" %}"; }; }; }"#,
|
||||
] {
|
||||
let snapshot = ConfigTreeSnapshot::from_entries(
|
||||
|
||||
@@ -240,10 +240,7 @@ impl ServerConfig {
|
||||
}
|
||||
|
||||
const ORCHESTRATOR_ATTENTION_TICKET_LIMIT: usize = 20;
|
||||
const ORCHESTRATOR_ATTENTION_PROMPT: &str = include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/../../resources/prompts/internal/workspace_orchestrator_queue_attention.md"
|
||||
));
|
||||
const ORCHESTRATOR_ATTENTION_PROMPT_NAME: &str = "internal.workspace_orchestrator_queue_attention";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WorkspaceApi {
|
||||
@@ -4811,10 +4808,31 @@ fn dispatch_orchestrator_queue_attention(api: &WorkspaceApi) {
|
||||
} else {
|
||||
format!("Additional queued Tickets omitted from this notice: {omitted}\n")
|
||||
};
|
||||
let content = ORCHESTRATOR_ATTENTION_PROMPT
|
||||
.replace("{{omitted_line}}", &omitted_line)
|
||||
.replace("{{workspace_id}}", &api.config.workspace_id)
|
||||
.replace("{{ticket_lines}}", &shown);
|
||||
let Ok(Some(config_state)) = api
|
||||
.config_store
|
||||
.load_workspace_config(&api.config.workspace_id)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Ok(projection) =
|
||||
crate::prompt_settings::project_prompts_from_workspace_config(&config_state)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Ok(catalog) = worker::PromptCatalog::from_projection(projection) else {
|
||||
return;
|
||||
};
|
||||
let content = match catalog.render_serializable(
|
||||
ORCHESTRATOR_ATTENTION_PROMPT_NAME,
|
||||
&BTreeMap::from([
|
||||
("omitted_line", omitted_line.as_str()),
|
||||
("workspace_id", api.config.workspace_id.as_str()),
|
||||
("ticket_lines", shown.as_str()),
|
||||
]),
|
||||
) {
|
||||
Ok(content) => content,
|
||||
Err(_) => return,
|
||||
};
|
||||
let accepted = api
|
||||
.runtime
|
||||
.send_input(
|
||||
|
||||
Reference in New Issue
Block a user