feat(memory): surface entity types in recall/query API and UI (#207)#302
Conversation
The upstream whisper-rs-sys builds whisper.cpp via CMake which defaults to /MD (dynamic CRT), but Rust and all other C deps use /MT (static CRT). This causes LNK2038/LNK1169 linker errors on Windows. Patch whisper-rs-sys from tinyhumansai/whisper-rs-sys fork which adds config.static_crt(true) and overrides all per-config CMake flags (Debug/Release/MinSizeRel/RelWithDebInfo) from /MD to /MT. Closes tinyhumansai#273
Entity types extracted by GLiNER (person, project, organization, etc.) were stored in graph attrs but not rendered in LLM context text. Relations now display as Alice (PERSON) -[OWNS]-> Atlas (PROJECT) instead of Alice -[OWNS]-> Atlas. Closes tinyhumansai#207 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New MemoryTextWithEntities component with colour-coded type badges - MemoryWorkspace + MemoryDebugPanel pass structured entity data - MemoryGraphMap shows entity types below node labels - MemoryInsights shows EntityTypeBadge for subject/object types - tauriCommands returns typed MemoryQueryResult with entities - Updated useConsciousItems and tests for new return types Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe PR extends memory recall and query API responses to include entity type information alongside text content. Backend modifications extract entity types from relation attributes and format them in LLM context output. Frontend updates introduce new data structures, update return types, and add UI components to display entity type badges throughout the interface. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
app/src/components/intelligence/MemoryWorkspace.tsx (1)
1-576:⚠️ Potential issue | 🟡 MinorRun Prettier on this file before merge.
This file is included in the current
prettier --checkfailures.As per coding guidelines "
app/src/**/*.{ts,tsx}: Run Prettier, ESLint, andtsc --noEmitbefore merging changes toapp/code".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/intelligence/MemoryWorkspace.tsx` around lines 1 - 576, The file fails the repository Prettier check; run Prettier to format this file before merging. Open the MemoryWorkspace component (function MemoryWorkspace) and the helper functions (normalizeMemoryDocuments, extractTimestamp, estimateContentSize, etc.), run your project's configured Prettier command (e.g., prettier --write) or the repo-level formatting script against app/src/components/intelligence/MemoryWorkspace.tsx, re-run prettier --check to confirm the file is formatted, and include the formatted changes in the PR.app/src/components/settings/panels/MemoryDebugPanel.tsx (1)
1-337:⚠️ Potential issue | 🟡 MinorRun Prettier on this file before merge.
CI is failing
format:checkand explicitly flags this file. Please run Prettier--writeso the formatting gate passes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/settings/panels/MemoryDebugPanel.tsx` around lines 1 - 337, The file MemoryDebugPanel (component MemoryDebugPanel) is failing the formatting check; run the project's formatter on this file (e.g. run your repo's format script such as npm/yarn/pnpm run format or run Prettier directly: prettier --write src/components/settings/panels/MemoryDebugPanel.tsx) and stage the updated file so the CI `format:check` passes before merging.
🧹 Nitpick comments (1)
app/src/components/intelligence/__tests__/MemoryWorkspace.test.tsx (1)
32-33: Add one behavior assertion for typed query/recall rendering.Great contract update on the mocks, but this change set doesn’t yet assert the new user-visible behavior (entity-type badges/chips) from structured
{ text, entities }responses. Adding one UI-level assertion here would guard the#207objective against regressions.Based on learnings: "Prefer testing behavior over implementation details; use existing helpers from
app/src/test/(test-utils.tsx, shared mock backend) before adding new harness code".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/intelligence/__tests__/MemoryWorkspace.test.tsx` around lines 32 - 33, The test updates the mocks (memoryQueryNamespace and memoryRecallNamespace) to return structured { text, entities } but lacks a UI-level assertion for rendering entity-type badges/chips; update MemoryWorkspace.test.tsx to mock one of those calls (memoryQueryNamespace or memoryRecallNamespace) with an entities array that includes at least one item with a type (e.g., { name: 'Alice', type: 'person' }), render the component using the existing test-utils helpers, and add an assertion that the corresponding entity-type badge/chip is present in the DOM (use the existing query helpers like getByText/getByRole from the test utilities rather than adding new harness code) to ensure the typed response displays correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/utils/tauriCommands.ts`:
- Around line 279-361: This file fails Prettier checks; run Prettier and
reformat the file containing
MemoryRetrievalEntity/MemoryRetrievalContext/MemoryQueryResult interfaces and
the functions unwrapMemoryQueryResult, memoryQueryNamespace, and
memoryRecallNamespace so the AST/spacing matches project style (e.g., run
prettier --write on app/src/utils/tauriCommands.ts or your editor's Prettier
formatter), then commit the updated formatting so CI prettier --check passes.
In `@src/openhuman/memory/store/unified/query.rs`:
- Around line 1244-1247: The code block calling
Self::entity_label_with_type(&relation.subject, &relation.attrs, "subject") and
Self::entity_label_with_type(&relation.object, &relation.attrs, "object") is
failing CI formatting; run cargo fmt to reformat this block (and the file) so it
matches rustfmt style. Locate the calls in query.rs (the subject_label and
object_label assignments) and run `cargo fmt` (or your IDE's Rust formatter)
then re-run CI/cargo fmt --check to ensure the formatting diff is resolved.
---
Outside diff comments:
In `@app/src/components/intelligence/MemoryWorkspace.tsx`:
- Around line 1-576: The file fails the repository Prettier check; run Prettier
to format this file before merging. Open the MemoryWorkspace component (function
MemoryWorkspace) and the helper functions (normalizeMemoryDocuments,
extractTimestamp, estimateContentSize, etc.), run your project's configured
Prettier command (e.g., prettier --write) or the repo-level formatting script
against app/src/components/intelligence/MemoryWorkspace.tsx, re-run prettier
--check to confirm the file is formatted, and include the formatted changes in
the PR.
In `@app/src/components/settings/panels/MemoryDebugPanel.tsx`:
- Around line 1-337: The file MemoryDebugPanel (component MemoryDebugPanel) is
failing the formatting check; run the project's formatter on this file (e.g. run
your repo's format script such as npm/yarn/pnpm run format or run Prettier
directly: prettier --write src/components/settings/panels/MemoryDebugPanel.tsx)
and stage the updated file so the CI `format:check` passes before merging.
---
Nitpick comments:
In `@app/src/components/intelligence/__tests__/MemoryWorkspace.test.tsx`:
- Around line 32-33: The test updates the mocks (memoryQueryNamespace and
memoryRecallNamespace) to return structured { text, entities } but lacks a
UI-level assertion for rendering entity-type badges/chips; update
MemoryWorkspace.test.tsx to mock one of those calls (memoryQueryNamespace or
memoryRecallNamespace) with an entities array that includes at least one item
with a type (e.g., { name: 'Alice', type: 'person' }), render the component
using the existing test-utils helpers, and add an assertion that the
corresponding entity-type badge/chip is present in the DOM (use the existing
query helpers like getByText/getByRole from the test utilities rather than
adding new harness code) to ensure the typed response displays correctly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9f0ba7f3-48af-48cd-83ea-0ddddc61a7b6
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (11)
Cargo.tomlapp/src/components/intelligence/MemoryGraphMap.tsxapp/src/components/intelligence/MemoryInsights.tsxapp/src/components/intelligence/MemoryTextWithEntities.tsxapp/src/components/intelligence/MemoryWorkspace.tsxapp/src/components/intelligence/__tests__/MemoryWorkspace.test.tsxapp/src/components/settings/panels/MemoryDebugPanel.tsxapp/src/hooks/useConsciousItems.tsapp/src/utils/tauriCommands.tssrc/openhuman/memory/ops.rssrc/openhuman/memory/store/unified/query.rs
Avoids react-hooks/immutability ESLint error by using a non-global regex for the .test() check instead of resetting ENTITY_TYPE_RE.lastIndex. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/openhuman/memory/store/unified/query.rs (2)
1492-1596: Add one regression for the untyped fallback.These tests cover the annotated path well, but they never exercise the documented fallback when
entity_types.subject/entity_types.objectis absent. A single relation withoutentity_typeswould lock in the non-breakingALICE -[OWNS]-> ATLASbehavior and protectentity_label_with_type()from regressing to empty()output.Also applies to: 1598-1652
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/memory/store/unified/query.rs` around lines 1492 - 1596, Add a regression that exercises the untyped fallback by modifying or adding to the existing test query_supporting_relations_contain_entity_types: insert at least one graph relation via graph_upsert_namespace where the attrs JSON does NOT include the "entity_types" key (e.g., omit entity_types for the OWNS relation), then after query_namespace_hits / recall_namespace_memories assert that supporting_relations still contain the expected subject/object labels (or that entity_label_with_type() produces a non-empty fallback like "ALICE" / "ATLAS") rather than an empty tuple; locate uses of graph_upsert_namespace, supporting_relations, and entity_label_with_type() in the test to add these assertions.
1206-1216: Extract theentity_typeslookup into a shared helper.This repeats the same
attrs["entity_types"][role]traversal already implemented insrc/openhuman/memory/ops.rs:1-12. Keeping two copies of that schema logic makes the query/recall formatting path easy to drift from the other memory surfaces the next time the attrs shape changes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/memory/store/unified/query.rs` around lines 1206 - 1216, The inline attrs["entity_types"][role] traversal inside entity_label_with_type should be removed and replaced with a call to the existing shared helper that performs this lookup from the ops module; extract the lookup into that helper (or reuse the already-implemented function in ops.rs) and import it into this file, then have entity_label_with_type call that helper to get the optional entity type and format the label accordingly so the schema logic is centralized and not duplicated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/openhuman/memory/store/unified/query.rs`:
- Around line 1492-1596: Add a regression that exercises the untyped fallback by
modifying or adding to the existing test
query_supporting_relations_contain_entity_types: insert at least one graph
relation via graph_upsert_namespace where the attrs JSON does NOT include the
"entity_types" key (e.g., omit entity_types for the OWNS relation), then after
query_namespace_hits / recall_namespace_memories assert that
supporting_relations still contain the expected subject/object labels (or that
entity_label_with_type() produces a non-empty fallback like "ALICE" / "ATLAS")
rather than an empty tuple; locate uses of graph_upsert_namespace,
supporting_relations, and entity_label_with_type() in the test to add these
assertions.
- Around line 1206-1216: The inline attrs["entity_types"][role] traversal inside
entity_label_with_type should be removed and replaced with a call to the
existing shared helper that performs this lookup from the ops module; extract
the lookup into that helper (or reuse the already-implemented function in
ops.rs) and import it into this file, then have entity_label_with_type call that
helper to get the optional entity type and format the label accordingly so the
schema logic is centralized and not duplicated.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 08cb759e-6fb9-44c9-b579-929f758c4a25
📒 Files selected for processing (5)
app/src/components/intelligence/MemoryTextWithEntities.tsxapp/src/components/intelligence/MemoryWorkspace.tsxapp/src/components/settings/panels/MemoryDebugPanel.tsxapp/src/utils/tauriCommands.tssrc/openhuman/memory/store/unified/query.rs
✅ Files skipped from review due to trivial changes (1)
- app/src/components/intelligence/MemoryWorkspace.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- app/src/components/intelligence/MemoryTextWithEntities.tsx
- app/src/utils/tauriCommands.ts
Keep both additions: memoryClearNamespace() from upstream and entity type interfaces from tinyhumansai#207 branch. No functional overlap. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (2)
app/src/utils/tauriCommands.ts (1)
331-331: Prefer aconstarrow for this new helper.Small consistency nit, but this helper is new code and can follow the app’s TS convention.
As per coding guidelines "Prefer arrow functions over function declarations".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/utils/tauriCommands.ts` at line 331, The function declaration unwrapMemoryQueryResult should be converted to a const arrow function to follow project TS conventions; replace the function declaration "function unwrapMemoryQueryResult(resp: unknown): MemoryQueryResult { ... }" with a const arrow assigned version "const unwrapMemoryQueryResult = (resp: unknown): MemoryQueryResult => { ... }", preserving its implementation and exported/used name so all callers remain unchanged.app/src/components/settings/panels/MemoryDebugPanel.tsx (1)
416-425: Keep an empty response state visible.
MemoryTextWithEntitiesreturnsnullwhen bothtextandentitiesare empty (app/src/components/intelligence/MemoryTextWithEntities.tsx:91-106), so these panes collapse on first load, after reset, and for genuinely empty responses. In a debug view that makes “no response” hard to distinguish from “nothing rendered”.One way to keep the shell mounted
- <MemoryTextWithEntities - text={queryResult?.text ?? ''} - entities={queryResult?.entities} - className="rounded border border-stone-700 bg-black/20 p-2 overflow-auto text-[11px] leading-5 min-h-16 whitespace-pre-wrap" - /> + <div className="rounded border border-stone-700 bg-black/20 p-2 min-h-16"> + <MemoryTextWithEntities + text={queryResult?.text ?? ''} + entities={queryResult?.entities} + className="overflow-auto text-[11px] leading-5 whitespace-pre-wrap" + /> + {(!queryResult || (!queryResult.text && queryResult.entities.length === 0)) && ( + <div className="text-[11px] leading-5 text-stone-500">No response.</div> + )} + </div>Apply the same wrapper pattern to the recall box.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/settings/panels/MemoryDebugPanel.tsx` around lines 416 - 425, MemoryTextWithEntities returns null when both text and entities are empty, causing the recall pane to collapse; wrap the recall MemoryTextWithEntities the same way the query pane is wrapped so the shell remains mounted even if the child returns null. Update the JSX around the recall MemoryTextWithEntities (the instance using recallResult?.text and recallResult?.entities) to use the same outer wrapper/container used for the queryResult pane (preserving the className, border, sizing and the "Recall response" label) so the empty state still renders an empty boxed shell instead of removing the element.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@app/src/components/settings/panels/MemoryDebugPanel.tsx`:
- Around line 416-425: MemoryTextWithEntities returns null when both text and
entities are empty, causing the recall pane to collapse; wrap the recall
MemoryTextWithEntities the same way the query pane is wrapped so the shell
remains mounted even if the child returns null. Update the JSX around the recall
MemoryTextWithEntities (the instance using recallResult?.text and
recallResult?.entities) to use the same outer wrapper/container used for the
queryResult pane (preserving the className, border, sizing and the "Recall
response" label) so the empty state still renders an empty boxed shell instead
of removing the element.
In `@app/src/utils/tauriCommands.ts`:
- Line 331: The function declaration unwrapMemoryQueryResult should be converted
to a const arrow function to follow project TS conventions; replace the function
declaration "function unwrapMemoryQueryResult(resp: unknown): MemoryQueryResult
{ ... }" with a const arrow assigned version "const unwrapMemoryQueryResult =
(resp: unknown): MemoryQueryResult => { ... }", preserving its implementation
and exported/used name so all callers remain unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 917c2e72-be26-42ad-b063-0aeef73b6a8f
📒 Files selected for processing (4)
app/src/components/intelligence/__tests__/MemoryWorkspace.test.tsxapp/src/components/settings/panels/MemoryDebugPanel.tsxapp/src/utils/tauriCommands.tssrc/openhuman/memory/ops.rs
🚧 Files skipped from review as they are similar to previous changes (2)
- app/src/components/intelligence/tests/MemoryWorkspace.test.tsx
- src/openhuman/memory/ops.rs
Summary
format_llm_context_messageandformat_context_textnow annotate entities with their GLiNER-extracted types (e.g.,Alice (PERSON) -[OWNS]-> Atlas (PROJECT))MemoryTextWithEntitiescomponent renders colour-coded entity type badges inlinetauriCommands.tsreturns typedMemoryQueryResultwith structuredentities[]dataProblem
Entity types extracted via GLiNER during ingestion were stored in graph relation attrs but never surfaced in the recall/query APIs or UI. Users had to use
memoryGraphQueryseparately to see entity types, making the main memory views less informative. (#207)Solution
Backend: Added
entity_label_with_type()helpers in bothops.rsandquery.rsthat extractentity_types.{role}from relation attrs and format asName (TYPE). Falls back to plain name when types are absent.Frontend: New
MemoryTextWithEntitiescomponent parses(TYPE)annotations from text and renders them as colour-coded badges (PERSON=sky, ORG=amber, PROJECT=emerald, etc.). Also renders structured entity chips fromcontext.entities[]. UpdatedmemoryQueryNamespaceandmemoryRecallNamespaceto returnMemoryQueryResultwith bothtextandentitiesfields.Submission Checklist
format_llm_context_message, query integration tests. TS: updated MemoryWorkspace test mocks.MemoryRetrievalEntity,MemoryQueryResult,MemoryTextWithEntitiesImpact
memoryQueryNamespaceandmemoryRecallNamespacereturn type changed fromstringtoMemoryQueryResult— callers updated in this PR.Related
🤖 Generated with Claude Code
Summary by CodeRabbit