Add LLM-friendly formatting helpers to clients#1311
Add LLM-friendly formatting helpers to clients#1311sscargal merged 4 commits intoMemMachine:mainfrom
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>
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