fix: properly anchored the role frontmatter regex so it doesn't catch in-instruction separators
This commit is contained in:
1 file changed
+44
-3
+44
-3
@@ -21,7 +21,7 @@ pub const INPUT_PLACEHOLDER: &str = "__INPUT__";
|
|||||||
struct RolesAsset;
|
struct RolesAsset;
|
||||||
|
|
||||||
static RE_METADATA: LazyLock<Regex> =
|
static RE_METADATA: LazyLock<Regex> =
|
||||||
LazyLock::new(|| Regex::new(r"(?s)-{3,}\s*(.*?)\s*-{3,}\s*(.*)").unwrap());
|
LazyLock::new(|| Regex::new(r"(?s)\A\s*-{3,}\s*(.*?)\s*-{3,}\s*(.*)").unwrap());
|
||||||
|
|
||||||
pub trait RoleLike {
|
pub trait RoleLike {
|
||||||
fn to_role(&self) -> Role;
|
fn to_role(&self) -> Role;
|
||||||
@@ -111,8 +111,15 @@ impl Role {
|
|||||||
if let Ok(Some(caps)) = RE_METADATA.captures(content)
|
if let Ok(Some(caps)) = RE_METADATA.captures(content)
|
||||||
&& let (Some(metadata_value), Some(prompt_value)) = (caps.get(1), caps.get(2))
|
&& let (Some(metadata_value), Some(prompt_value)) = (caps.get(1), caps.get(2))
|
||||||
{
|
{
|
||||||
metadata = metadata_value.as_str().trim();
|
let candidate = metadata_value.as_str().trim();
|
||||||
prompt = prompt_value.as_str().trim();
|
if candidate.is_empty()
|
||||||
|
|| serde_yaml::from_str::<Value>(candidate)
|
||||||
|
.map(|v| v.is_object())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
metadata = candidate;
|
||||||
|
prompt = prompt_value.as_str().trim();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let mut prompt = prompt.to_string();
|
let mut prompt = prompt.to_string();
|
||||||
interpolate_variables(&mut prompt);
|
interpolate_variables(&mut prompt);
|
||||||
@@ -645,6 +652,40 @@ mod tests {
|
|||||||
assert_eq!(role.enabled_mcp_servers(), None);
|
assert_eq!(role.enabled_mcp_servers(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn role_new_keeps_prompt_containing_markdown_table_separator() {
|
||||||
|
let content = "You review PRs.\nThe PR: <url>\n\n| Lane | Verdict |\n|------|---------|\n| code-reviewer | PASS |";
|
||||||
|
|
||||||
|
let role = Role::new("test", content);
|
||||||
|
|
||||||
|
assert_eq!(role.prompt(), content);
|
||||||
|
assert_eq!(role.model_id(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn role_new_parses_metadata_with_table_separator_in_prompt() {
|
||||||
|
let content =
|
||||||
|
"---\nmodel: openai:gpt-4\n---\nIntro kept\n\n| a | b |\n|---|---|\n| 1 | 2 |";
|
||||||
|
|
||||||
|
let role = Role::new("test", content);
|
||||||
|
|
||||||
|
assert_eq!(role.model_id(), Some("openai:gpt-4"));
|
||||||
|
assert_eq!(
|
||||||
|
role.prompt(),
|
||||||
|
"Intro kept\n\n| a | b |\n|---|---|\n| 1 | 2 |"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn role_new_leading_dash_decoration_is_not_metadata() {
|
||||||
|
let content = "--- IMPORTANT ---\nDo the thing";
|
||||||
|
|
||||||
|
let role = Role::new("test", content);
|
||||||
|
|
||||||
|
assert_eq!(role.prompt(), content);
|
||||||
|
assert_eq!(role.model_id(), None);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn role_new_enabled_macros_absent_is_none() {
|
fn role_new_enabled_macros_absent_is_none() {
|
||||||
let role = Role::new("test", "---\ntemperature: 0.5\n---\nPrompt");
|
let role = Role::new("test", "---\ntemperature: 0.5\n---\nPrompt");
|
||||||
|
|||||||
Reference in new issue
Block a user