Skip to content

fix(llm): render missing {} and formatted template placeholders as empty - #7783

Merged
longcw merged 1 commit into
livekit:mainfrom
ANDROIDHASSAN:fix/safe-render-missing-fields
Oct 19, 2026
Merged

longcw merged 1 commit into
livekit:mainfrom
ANDROIDHASSAN:fix/safe-render-missing-fields

Conversation

@ANDROIDHASSAN

Copy link
Copy Markdown
Contributor

Problem: safe_render's docstring says missing keys become empty strings, and Instructions.render(data=...) repeats that. Three kinds of placeholder raise instead:

Instructions("Hello {name}, total {}").render(data={"name": "Alex"})
# IndexError: tuple index out of range
Instructions("Hello {name}, item {0}").render(data={"name": "Alex"})
# IndexError: tuple index out of range
Instructions("Hello {name}, price {price:.2f}").render(data={"name": "Alex"})
# ValueError: Unknown format code 'f' for object of type 'str'

{} and {0} look up positional args, which are always empty here, so get_field raises IndexError; it only caught KeyError, AttributeError and TypeError. For {price:.2f} the lookup is caught, but the "" stand-in is then formatted with .2f.

Fix: catch IndexError too, and return None for a missing field instead of "". format_field already renders None as empty before applying the spec. convert_field passes None through, so {x!s} stays empty instead of becoming "None".

Tests

test_render_missing_placeholders_become_empty in tests/test_chat_ctx.py, through the public Instructions.render. On main:

FAILED ...[Hello {name}, total {}-Hello Alex, total ]        IndexError: tuple index out of range
FAILED ...[Hello {name}, item {0}-Hello Alex, item ]          IndexError: tuple index out of range
FAILED ...[Hello {name}, price {price:.2f}-Hello Alex, price ] ValueError: Unknown format code 'f' for object of type 'str'
3 failed, 1 passed

The fourth case ({caller!s}) passes on main and is there so returning None can't leak "None" into a prompt.

ruff format, ruff check, make type-check pass. uv run pytest --unit --audio_eot -q: 5045 passed, 5 skipped.

Scope: safe_render only. Placeholders that resolve to a value render as before. One side effect: an explicit None value with !s/!r now renders empty rather than "None", matching the docstring's "None values also become empty strings".

safe_render promises that a placeholder with no value becomes an empty string,
but Instructions.render(data=...) raised instead for three kinds of placeholder:
auto-numbered {} and indexed {0} raised IndexError, which get_field did not catch,
and a format spec such as {price:.2f} raised ValueError because the empty-string
stand-in was formatted with it. Return None for a missing field so format_field
renders it empty without applying the spec, and keep None through convert_field
so {x!s} does not render "None".
@ANDROIDHASSAN
ANDROIDHASSAN requested a review from a team as a code owner October 13, 2026 10:19

@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: 1 flag

Not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

@xianshijing-lk

Copy link
Copy Markdown

I think the PR addressed the problems it found, lgtm in general.

Adding @longcw or @theomonnom for a second look to double check if it already covers all the use cases that we need.

@longcw
longcw merged commit 125361b into livekit:main Oct 19, 2026
17 checks passed
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.

3 participants