Add LLM-friendly formatting helpers to clients#1311
Conversation
|
Changes from Annotated to Field directly are to satisfy the type checker. There is no need to use Annotated. |
Signed-off-by: Edwin Yu <edwinyyyu@gmail.com>
2dd531c to
ed8f1fd
Compare
There was a problem hiding this comment.
Pull request overview
Adds client-side helpers to render episodic + semantic memory results into a compact, LLM-friendly string/JSON format, mirroring server-side formatting so consumers can trim context usage.
Changes:
- Added TS + Python formatting helpers for episodic episodes, semantic features, and combined search results.
- Exported the new helpers from each client’s public API surface.
- Added unit tests for both clients’ formatting behavior; updated TS list typing surface (new
ListMemoriesResult+list()return type).
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/ts-client/src/memory/format.ts | New TS formatting helpers for episodes/semantic/search results |
| packages/ts-client/src/memory/index.ts | Re-export formatting helpers + new list result type |
| packages/ts-client/src/memory/memmachine-memory.ts | list() now returns ListMemoriesResult |
| packages/ts-client/src/memory/memmachine-memory.types.ts | Introduces ListMemoriesResult type |
| packages/ts-client/tests/format.spec.ts | Jest coverage for TS formatting helpers |
| packages/client/src/memmachine_client/format.py | New Python formatting helpers |
| packages/client/src/memmachine_client/init.py | Exposes formatting helpers in package exports |
| packages/client/client_tests/test_format.py | Pytest coverage for Python formatting helpers |
| packages/common/src/memmachine_common/api/spec.py | Refactors selected API spec model field declarations |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export interface ListMemoriesResult { | ||
| status: number | ||
| content: { | ||
| episodic_memory?: EpisodicMemory[] | ||
| semantic_memory?: SemanticMemory[] | ||
| } |
There was a problem hiding this comment.
ListMemoriesResult.content.episodic_memory is typed as EpisodicMemory[], but /memories/list returns Episode objects (no score, and includes fields like session_key, sequence_num, content_type, etc., per memmachine_common.api.spec.ListResultContent). This makes the TS client types inaccurate and can mislead consumers. Define a dedicated list-episode type (or reuse a generated Episode type) and use it here instead of EpisodicMemory.
| export function formatEpisodes(episodes: EpisodicMemory[]): string { | ||
| let result = '' | ||
| for (const episode of episodes) { | ||
| const date = new Date(episode.created_at) | ||
| const dateStr = _formatDate(date) | ||
| const timeStr = _formatTime(date) | ||
| result += `[${dateStr} at ${timeStr}] ${episode.producer_id}: ${JSON.stringify(episode.content)}\n` | ||
| } |
There was a problem hiding this comment.
formatEpisodes only accepts EpisodicMemory[], but list responses return a different episode shape (and even search responses may have optional/nullable score). Since this formatter only needs created_at, producer_id, and content, consider changing the parameter type to a minimal structural type (or a union of search/list episode types) so it can be used safely with both search() and list() results.
| export function formatSearchResult(result: SearchMemoriesResult): string { | ||
| const sections: string[] = [] | ||
|
|
||
| if (result.content.episodic_memory) { | ||
| const episodes = [ | ||
| ...result.content.episodic_memory.long_term_memory.episodes, | ||
| ...result.content.episodic_memory.short_term_memory.episodes | ||
| ] | ||
| if (episodes.length > 0) { | ||
| sections.push(`[Episodic Memory]\n${formatEpisodes(episodes)}`) | ||
| } | ||
| } | ||
|
|
||
| if (result.content.semantic_memory && result.content.semantic_memory.length > 0) { | ||
| sections.push(`[Semantic Memory]\n${formatSemanticMemories(result.content.semantic_memory)}`) |
There was a problem hiding this comment.
formatSearchResult defensively treats episodic_memory/semantic_memory as optional, but the exported TS type SearchMemoriesResult currently requires both to be present. The server spec allows these fields to be null/omitted (e.g., when searching only one memory type, and FastAPI uses response_model_exclude_none). Updating the TS SearchMemoriesResult definition to reflect nullable/optional fields will make this helper and the client’s runtime behavior consistent.
| describe('formatSearchResult', () => { | ||
| it('returns empty string for empty result', () => { | ||
| const result: SearchMemoriesResult = { | ||
| status: 0, | ||
| content: { |
There was a problem hiding this comment.
formatSearchResult has branches for missing/null episodic_memory and semantic_memory, but the test suite doesn’t cover those cases. Add a test where semantic_memory is null/undefined and/or episodic_memory is null to ensure this behavior stays correct (server responses can omit these fields when searching a single type).
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Edwin Yu <92917168+edwinyyyu@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Edwin Yu <92917168+edwinyyyu@users.noreply.github.com>
Signed-off-by: Edwin Yu <edwinyyyu@gmail.com>
* Add LLM-friendly formatting helpers to clients Signed-off-by: Edwin Yu <edwinyyyu@gmail.com> * Update packages/ts-client/src/memory/memmachine-memory.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Edwin Yu <92917168+edwinyyyu@users.noreply.github.com> * Update packages/ts-client/src/memory/memmachine-memory.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Edwin Yu <92917168+edwinyyyu@users.noreply.github.com> * Claude fixes Signed-off-by: Edwin Yu <edwinyyyu@gmail.com> --------- Signed-off-by: Edwin Yu <edwinyyyu@gmail.com> Signed-off-by: Edwin Yu <92917168+edwinyyyu@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Add three small features to the Python client and its LangGraph wrapper so callers can pass structured filter expressions and either-enum-or-string episode types directly. 1. `Memory.search(filter=...)` and `Memory.list(filter=...)` Accept an optional raw filter string alongside `filter_dict`. When both are provided, the two are combined with `AND`. The raw filter is passed through to the v2 `SearchMemoriesSpec.filter` / `ListMemoriesSpec.filter` fields unchanged. 2. `MemMachineTools.search_memory(filter=...)` Pipes the same raw filter through the LangGraph search-memory tool. 3. `MemMachineTools.add_memory(episode_type=...)` Accept either an `EpisodeType` enum or its string value (e.g. `"message"`), normalizing strings via `EpisodeType(...)` before delegating to `Memory.add`. The factory tool's return-type annotation was widened to match. The `filter` parameter shadows the Python builtin, which is the same trade-off `memmachine_common.api.SearchMemoriesSpec` already made for its `filter:` field — keeping the parameter name aligned with the API field. `# noqa: A002` is applied at the three call sites with a comment pointing at the API spec. This commit consolidates the substantive work from haosenwang1018's 9-commit stack (MemMachine#1341 → MemMachine#1349) into a single rebased+linted commit against current `main`. The original stack's prefix-style doc and test changes have been omitted because they have already landed on `main` via MemMachine#1352 and MemMachine#1311. The original commits authored by haosenwang1018: - 921b55f feat(client): support raw filter strings - e0849bb feat(langgraph): support raw filter strings - 53b489e fix(langgraph): normalize episode type strings Closes MemMachine#1341, MemMachine#1342, MemMachine#1343, MemMachine#1344, MemMachine#1345, MemMachine#1346, MemMachine#1347, MemMachine#1348, MemMachine#1349 Co-authored-by: Steve Scargall <steve.scargall@gmail.com> Signed-off-by: Steve Scargall <37674041+sscargal@users.noreply.github.com>
…ion (#1403) * feat(client+langgraph): raw filter strings and EpisodeType normalization Add three small features to the Python client and its LangGraph wrapper so callers can pass structured filter expressions and either-enum-or-string episode types directly. 1. `Memory.search(filter=...)` and `Memory.list(filter=...)` Accept an optional raw filter string alongside `filter_dict`. When both are provided, the two are combined with `AND`. The raw filter is passed through to the v2 `SearchMemoriesSpec.filter` / `ListMemoriesSpec.filter` fields unchanged. 2. `MemMachineTools.search_memory(filter=...)` Pipes the same raw filter through the LangGraph search-memory tool. 3. `MemMachineTools.add_memory(episode_type=...)` Accept either an `EpisodeType` enum or its string value (e.g. `"message"`), normalizing strings via `EpisodeType(...)` before delegating to `Memory.add`. The factory tool's return-type annotation was widened to match. The `filter` parameter shadows the Python builtin, which is the same trade-off `memmachine_common.api.SearchMemoriesSpec` already made for its `filter:` field — keeping the parameter name aligned with the API field. `# noqa: A002` is applied at the three call sites with a comment pointing at the API spec. This commit consolidates the substantive work from haosenwang1018's 9-commit stack (#1341 → #1349) into a single rebased+linted commit against current `main`. The original stack's prefix-style doc and test changes have been omitted because they have already landed on `main` via #1352 and #1311. The original commits authored by haosenwang1018: - 921b55f feat(client): support raw filter strings - e0849bb feat(langgraph): support raw filter strings - 53b489e fix(langgraph): normalize episode type strings Closes #1341, #1342, #1343, #1344, #1345, #1346, #1347, #1348, #1349 Co-authored-by: Steve Scargall <steve.scargall@gmail.com> Signed-off-by: Steve Scargall <37674041+sscargal@users.noreply.github.com> * docs(langgraph): document filter and episode type support --------- Signed-off-by: Steve Scargall <37674041+sscargal@users.noreply.github.com> Co-authored-by: haosenwang1018 <haosenwang1018@users.noreply.github.com> Co-authored-by: Shu Wang <33640803+malatewang@users.noreply.github.com>
Purpose of the change
Both clients currently have no way of formatting memories in LLM-friendly/context-efficient format.
Description
Based on the logic of server's string_from_episode_context and internal formatting for semantic memories, add functions to do the same thing in clients.
Written entirely by Claude Code.
Fixes/Closes
Addresses client/server form of #1278, but does not solve for MCP, which needs a redesign.
Type of change
How Has This Been Tested?
Checklist
Maintainer Checklist