Conversation
…ssages `utils.py` in cartesia-sdk-python and pipecat-sdk-python are copies of the same file. supermemoryai#1434 hardened two spots in the pipecat copy and missed cartesia. `unique_search` calls `_field(r, ...)` on every search result. For a plain string that finds no attribute, falls back to `""`, and the entry is dropped before it reaches the prompt. `format_memories_to_text` in the same file has an `isinstance(item, str)` branch for search results, so that branch is currently unreachable. `get_last_user_message` indexed `msg["role"]` and `msg["content"]` positionally, so a tool-call turn or provider event missing either key raised KeyError instead of being skipped, and non-string content was returned verbatim despite the `str | None` annotation. Both hunks now match the pipecat implementation.
`_field` returns the first value that is not None. A v4 hybrid-search hit
shaped `{"memory": "", "chunk": "..."}` therefore resolves to the empty
string and never falls through to `chunk` — the exact fallback the call
site's comment says it is there for.
The consequences are in both directions. In `deduplicate_memories` the hit
produces an empty comparison key and is dropped, so the memory never reaches
the prompt. When such an item does survive, `format_memories_to_text` renders
it through the same `_field` call and emits a bare `- [16 hrs ago] `.
The TypeScript `getMemoryText` takes the first non-empty field instead, and
`agent-framework-python` and `openai-sdk-python` both already match it; the
two voice SDKs were the only implementations that disagreed. Add a
`_memory_text` helper mirroring the TypeScript semantics and use it on both
the dedup and the render path.
Also add tests/conftest.py to pipecat so the import stubs are installed
regardless of collection order — test_utils.py cannot import the package
on its own otherwise.
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.
Stacked on #1683. This branch has that PR's commit as its parent, so the diff below shows both. Only the second commit belongs to this PR:
3d19805. Happy to rebase ontomainonce #1683 lands.Summary
_field()returns the first value that is not None. A v4 hybrid-search hit shaped{"memory": "", "chunk": "..."}therefore resolves to the empty string and never falls through tochunk— the exact fallback the call site's own comment says it is there for:It bites on both paths:
deduplicate_memories— the empty text produces an empty comparison key, so the hit is dropped and the memory never reaches the prompt.format_memories_to_text— when such an item does survive, the same_fieldcall renders it as a bare- [16 hrs ago].Why this is the odd one out
The TypeScript
getMemoryTextinpackages/tools/src/tools-shared.tstakes the first non-empty field. I differential-tested all four Python SDKs against it:{"memory": "", "chunk": "text"}['text']['text']['text'][][]{"memory": " ", "chunk": "text"}['text']['text']['text'][][]SimpleNamespace(memory="", chunk="text")['text']['text']['text'][][]{"memory": None, "chunk": "text"}['text']['text']['text']['text']['text']{"chunk": "text"}['text']['text']['text']['text']['text']{"memory": "mem", "chunk": "chk"}['mem']['mem']['mem']['mem']['mem']agent-framework-pythonandopenai-sdk-pythonalready match the reference — they filter onisinstance(value, str) and value.strip(). The two voice SDKs were the only implementations that disagreed, which is why #1683 (a cartesia↔pipecat parity fix) did not surface it.Changes
_memory_text()in both voice SDKs, mirroring the TypeScriptgetMemoryTextsemantics, used on the dedup and the render path. Keeping both on one helper is what stops a rescued hit from rendering as an empty bullet.packages/pipecat-sdk-python/tests/conftest.py— pipecat's import stubs lived insidetest_empty_profile.py, so a second test module could only import the package when collection happened to run that file first. Moving them toconftest.pymakestests/test_utils.pyimportable on its own.test_empty_profile.pyis untouched; its own guarded copy becomes a no-op.Test plan
14 passed, pipecat8 passed. Pipecat verified isolated and in reverse collection order.test_memory_still_wins_over_chunkandtest_entry_with_no_usable_text_is_still_droppedpin the behaviour that should not change.