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