Skip to content

fix(prompts): stop compile() crashing on literal $ in templates - #772

Open
DevMello wants to merge 1 commit into
JudgmentLabs:stagingfrom
DevMello:fix/prompt-compile-literal-dollar
Open

fix(prompts): stop compile() crashing on literal $ in templates#772
DevMello wants to merge 1 commit into
JudgmentLabs:stagingfrom
DevMello:fix/prompt-compile-literal-dollar

Conversation

@DevMello

@DevMello DevMello commented Jul 30, 2026

Copy link
Copy Markdown

Prompt.compile() raised ValueError('Invalid placeholder in string') for any prompt containing a literal dollar sign (for example "costs $5") or a spaced placeholder like {{ name }}, because the {{var}} to $var rewrite feeds string.Template unescaped. This change escapes literal dollars before the rewrite and lets the placeholder regex absorb surrounding whitespace, so those templates now compile instead of crashing, while templates that already compiled produce identical output. Adds unit tests covering literal $, $$, ${x}, ${{amount}}, spaced placeholders, and the existing missing-variable error.

Example:

prompt = Prompt(
    name="pricing",
    prompt="Our plan costs $99/month. Write a pitch for {{ audience }}.",
    created_at="2024-01-01",
    tags=[],
    commit_id="c1",
)

prompt.compile(audience="developers")
# before: ValueError: Invalid placeholder in string: line 1, col 16
# after:  'Our plan costs $99/month. Write a pitch for developers.'

string.Template treats every $ as a placeholder marker, so any stored
prompt containing a dollar sign ("costs $5", JS ${...} snippets)
raised ValueError('Invalid placeholder') at compile time in production
rather than at authoring time. Spaced placeholders like {{ name }} hit
the same crash because the rewrite preserved the whitespace, producing
an invalid placeholder. Escape literal dollars before the {{var}} ->
$var rewrite and let the placeholder regex absorb surrounding
whitespace.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment on lines +45 to +47
template_str = re.sub(
r"\{\{\s*([^}]+?)\s*\}\}", r"$\1", self.prompt.replace("$", "$$")
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Prompts whose placeholder is immediately followed by letters or digits fail to fill in

A placeholder is rewritten into a bare marker without delimiting braces (r"$\1" at src/judgeval/prompts/prompt.py:46), so when a placeholder is directly followed by letters, digits or an underscore the name runs into the following text and filling in the prompt fails with a "missing variable" error.
Impact: Prompts like "Hello {{name}}san" or "{{count}}x" can never be compiled, even when every variable is supplied.

Mechanism: bare $name placeholder swallows adjacent identifier characters

re.sub(r"\{\{\s*([^}]+?)\s*\}\}", r"$\1", ...) turns Hello {{name}}san into Hello $namesan. string.Template parses the identifier greedily, so substitute(name="Ada") raises KeyError('namesan'), which compile() converts to ValueError: Missing required variable: namesan (src/judgeval/prompts/prompt.py:70-74).

Now that literal dollars are escaped up front (self.prompt.replace("$", "$$")), emitting the braced form ${name} is safe and fixes the adjacency case; ${name} is unambiguous to Template.

Suggested change
template_str = re.sub(
r"\{\{\s*([^}]+?)\s*\}\}", r"$\1", self.prompt.replace("$", "$$")
)
template_str = re.sub(
r"\{\{\s*([^}]+?)\s*\}\}", r"${\1}", self.prompt.replace("$", "$$")
)
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant