Repository navigation
fix(memory): query prompt and seed metadata whose keys are not SQL identifiers - #3125
Open
Chen Yufeiyang (feiiiiii5) wants to merge 1 commit into
Open
Chen Yufeiyang (feiiiiii5) wants to merge 1 commit into
Chen Yufeiyang (feiiiiii5) wants to merge 1 commit into
Conversation
`prompt_metadata` is documented as a free-form dictionary, but the SQLite
and Azure SQL backends spelled each key into the JSON path and reused it
as the bind-parameter name:
JSON_EXTRACT(prompt_metadata, '$.{key}') = :{key}
Only `[A-Za-z0-9_]` survives that. Anything else raises before the query
runs, so the filter cannot be expressed at all:
ArgumentError: This text() construct doesn't define a bound
parameter named 'attack.id'
`get_message_pieces_async(prompt_metadata={"attack.id": "v"})` against
SQLiteMemory raises that today. A dot was also read as a path step, so a
key that happened to survive would have looked up a nested object.
Both backends now bind the path with the key quoted into it (`'$."{key}"'`)
and give each condition positional parameter names, matching how microsoft#3063
fixed the same interpolation in the label conditions:
JSON_EXTRACT(prompt_metadata, :pm_path_0) = :pm_value_0
Binding the path also keeps the value out of the statement text entirely.
Seed metadata filtering goes through the same two code paths
(`_get_seed_metadata_conditions`), so it is fixed as well.
Regression tests query four keys holding a dot, hyphen, space and colon,
plus one query naming three keys at once. Against the parent commit they
fail with the ArgumentError above; the plain-key cases the suite already
covered still pass.
Not addressed: a key containing a double quote cannot be expressed in a
SQLite or T-SQL JSON path, so it matches nothing rather than raising.
That is the same residual microsoft#3063 left in the label path.
Validation at this head: tests/unit/memory/memory_interface/,
tests/unit/memory/test_sqlite_memory.py, tests/unit/memory/test_azure_sql_memory.py
-- 648 passed, 1 skipped; ruff check and format clean.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the free-form metadata keys that no backend can filter on.
Description
MemoryInterfacedocumentsprompt_metadataand seedmetadataas "a free-form dictionary for tagging prompts with custom metadata". Both SQL backends spelled each key into the JSON path and reused it as the bind-parameter name:Only
[A-Za-z0-9_]survives that. Every other character raises before the query runs:So the filter cannot be expressed at all for a dotted, hyphenated or spaced key — and a dot was additionally read as a path step, meaning a key that did survive was looking up a nested object. The existing coverage uses only identifier-safe keys (
finish_reason,status,incomplete_reason), which is why this went unnoticed.The same two code paths serve seed metadata (
_get_seed_metadata_conditions), so seed filtering has the same problem.Changes
sqlite_memory.py:_get_message_pieces_prompt_metadata_conditionsand_get_seed_metadata_conditionsnow bind the JSON path with the key quoted into it ('$."{key}"') and give each condition positional parameter names.azure_sql_memory.py: the same in_get_metadata_conditions, which backs both prompt and seed metadata there.This is how #3063 fixed the identical interpolation in the label conditions, so the three paths now agree.
Binding the path also keeps keys and values out of the statement text entirely.
Tests
tests/unit/memory/memory_interface/test_interface_prompts.pyqueries four keys holding a dot, hyphen, space and colon, and one query naming three keys at once. Against this branch's parent they fail with theArgumentErrorabove. The plain-key cases the suite already covered still pass.No documentation change:
MemoryInterfacealready documents these dictionaries as free-form, and this PR makes that true rather than narrowing it.Tests and Documentation
pytest tests/unit/memory/memory_interface/ tests/unit/memory/test_sqlite_memory.py \ tests/unit/memory/test_azure_sql_memory.py -q # 648 passed, 1 skipped ruff check # All checks passed! ruff format --check # 3 files already formattedJupyText not run: no code samples or documentation changed.
Not addressed
A key containing a double quote cannot be expressed in a SQLite or T-SQL JSON path, so after this change it matches nothing rather than raising. That is the same residual #3063 left in the label path; closing it would need a different lookup strategy than JSON paths, which is more than this fix should take on.
🤖 Generated with Claude Code