feat(intelligence): add Connection Path finder#2946
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a Connection Path feature: deterministic shortest-path engine, RPC graph loader, React tab and panel UI, Intelligence page wiring, tests, and i18n strings across 14 locales. ChangesConnection Path Feature
Sequence Diagram — Tab lifecycle and data flow: sequenceDiagram
participant User
participant ConnectionPathTab
participant connectionPathApi
participant findConnectionPath
participant ConnectionPathPanel
User->>ConnectionPathTab: mount, set namespace, enter source/target
ConnectionPathTab->>connectionPathApi: loadNamespaces(), loadGraph(namespace)
connectionPathApi-->>ConnectionPathTab: {entities, relations}
ConnectionPathTab->>findConnectionPath: findConnectionPath(relations, source, target)
findConnectionPath-->>ConnectionPathTab: ConnectionPathResult
ConnectionPathTab->>ConnectionPathPanel: render(result, hasGraph, loading, error, onRetry)
ConnectionPathPanel-->>User: display path or status message
Estimated code review effort: Possibly related PRs:
Suggested reviewers:
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. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
app/src/components/intelligence/ConnectionPathTab.tsx (1)
60-65: Path recompute rebuilds adjacency on every keystroke.
findConnectionPathreconstructs the full adjacency map and sorts neighbours on each call, and thisuseMemore-runs whenevertrimmedSource/trimmedTargetchange (i.e. per keystroke). For small graphs this is negligible, but if the namespace graph is large this synchronous main-thread work can introduce typing lag. If graphs can grow large, consider precomputing the adjacency once pergraph.relations(memoized separately) or debouncing the endpoint inputs.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/components/intelligence/ConnectionPathTab.tsx` around lines 60 - 65, The path recompute builds adjacency on every keystroke; fix by precomputing and memoizing the adjacency once per graph.relations and/or debouncing endpoint inputs: add a memoized adjacency (e.g. useMemo(() => buildAdjacency(graph.relations), [graph.relations])) and change ConnectionPathTab to call findConnectionPath with the precomputed adjacency (or refactor findConnectionPath into two functions: buildAdjacency and findPath) so trimmedSource/trimmedTarget changes only trigger the search, not adjacency reconstruction; alternatively add a short debounce for trimmedSource/trimmedTarget before invoking the search to reduce per-keystroke work.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@app/src/components/intelligence/ConnectionPathTab.tsx`:
- Around line 60-65: The path recompute builds adjacency on every keystroke; fix
by precomputing and memoizing the adjacency once per graph.relations and/or
debouncing endpoint inputs: add a memoized adjacency (e.g. useMemo(() =>
buildAdjacency(graph.relations), [graph.relations])) and change
ConnectionPathTab to call findConnectionPath with the precomputed adjacency (or
refactor findConnectionPath into two functions: buildAdjacency and findPath) so
trimmedSource/trimmedTarget changes only trigger the search, not adjacency
reconstruction; alternatively add a short debounce for
trimmedSource/trimmedTarget before invoking the search to reduce per-keystroke
work.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6278eb1b-23e7-4d21-89de-43e6e4944ba7
📒 Files selected for processing (23)
app/src/components/intelligence/ConnectionPathPanel.test.tsxapp/src/components/intelligence/ConnectionPathPanel.tsxapp/src/components/intelligence/ConnectionPathTab.test.tsxapp/src/components/intelligence/ConnectionPathTab.tsxapp/src/lib/i18n/chunks/ar-1.tsapp/src/lib/i18n/chunks/bn-1.tsapp/src/lib/i18n/chunks/de-1.tsapp/src/lib/i18n/chunks/en-1.tsapp/src/lib/i18n/chunks/es-1.tsapp/src/lib/i18n/chunks/fr-1.tsapp/src/lib/i18n/chunks/hi-1.tsapp/src/lib/i18n/chunks/id-1.tsapp/src/lib/i18n/chunks/it-1.tsapp/src/lib/i18n/chunks/ko-1.tsapp/src/lib/i18n/chunks/pt-1.tsapp/src/lib/i18n/chunks/ru-1.tsapp/src/lib/i18n/chunks/zh-CN-1.tsapp/src/lib/i18n/en.tsapp/src/lib/memory/connectionPath.test.tsapp/src/lib/memory/connectionPath.tsapp/src/pages/Intelligence.tsxapp/src/services/api/connectionPathApi.test.tsapp/src/services/api/connectionPathApi.ts
graycyrus
left a comment
There was a problem hiding this comment.
@aashir-athar hey! the code looks good to me, but CI is still pending (Frontend Unit Tests, Rust Core Tests, e2e, Build Tauri App). once those are green, i'll come back and approve this. let me know if you need any help!
One minor thing while I was in here: ConnectionPathPanel.tsx lines 199-207 use manual .replace('{entity}', ...) / .replace('{source}', ...) for i18n interpolation. This is fragile — if any translation omits a placeholder, the replacement silently no-ops. If useT supports parameterized keys (e.g. t('key', { entity })), use that instead. If not, not a blocker, but worth a follow-up.
1107f3b to
aceb211
Compare
aceb211 to
5c79862
Compare
A new read-only "Paths" tab that traces the shortest chain of relations linking any two entities — answering "how is A connected to B?" with the actual hop-by- hop path, something a direct-edge lookup can't show. - Pure deterministic engine (lib/memory/connectionPath.ts): BFS shortest path over the UNDIRECTED graph (a relation connects its endpoints regardless of arrow direction), but each hop records the predicate and whether it was traversed forward or backward so the chain reads naturally. No clock, no RNG; ties broken by a fixed sorted neighbour order, so the same graph + endpoints always yield the same path. Self-loops ignored; distinct reasons for same / missing-endpoint / no-path. - Zero new core surface: reuses memoryGraphQuery + memoryListNamespaces. The graph is fetched once; path-finding runs in the pure sync engine via useMemo, so tracing is instant with no extra round-trips. Read-only. - Container guards the graph load with a request token; two datalist (type- ahead) entity pickers; the panel renders the path as a node chain with per-hop predicate + direction. i18n across all 13 locales. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
5c79862 to
2298816
Compare
# Conflicts: # app/src/lib/i18n/chunks/ar-1.ts # app/src/lib/i18n/chunks/bn-1.ts # app/src/lib/i18n/chunks/de-1.ts # app/src/lib/i18n/chunks/en-1.ts # app/src/lib/i18n/chunks/es-1.ts # app/src/lib/i18n/chunks/fr-1.ts # app/src/lib/i18n/chunks/hi-1.ts # app/src/lib/i18n/chunks/id-1.ts # app/src/lib/i18n/chunks/it-1.ts # app/src/lib/i18n/chunks/ko-1.ts # app/src/lib/i18n/chunks/pt-1.ts # app/src/lib/i18n/chunks/ru-1.ts # app/src/lib/i18n/chunks/zh-CN-1.ts
Resolve the merge of main (flat-locale i18n layout) with the PR by
adding the 21 Connection Path keys (connectionPath.* + memory.tab.path)
to all 13 non-English locale files. en.ts already carried them via the
PR; main retired the chunks/<locale>-N.ts layout, so the keys move into
the flat per-locale files. Placeholder tokens ({length}/{entity}/{source}/
{target}) preserved. pnpm i18n:check and i18n:english:check both pass.
…derabbitai) - connectionPath.ts: short-circuit self-paths (source===target) and validate both nodes exist in the adjacency before BFS, returning explicit status codes (same / missing-source / missing-target / no-path / found) so the UI can show the right message instead of silently running BFS on absent nodes. - ConnectionPathPanel.tsx: memoize adjacency build and BFS (useMemo) so typing in either entity field no longer rebuilds the whole graph every render; map the new status codes to the existing connectionPath.* i18n messages. - ConnectionPathTab.tsx: guard the namespace fetch effect with a cancelled flag in cleanup so rapid namespace switches can't apply out-of-order results. - Tests: cover self-path, missing-source, missing-target, and multi-hop found.
| } | ||
|
|
||
| /** Fetch the graph for a namespace (or all) and derive the sorted entity list. */ | ||
| export async function loadGraph(namespace?: string): Promise<GraphData> { |
There was a problem hiding this comment.
Re: CodeRabbit's debounce/race suggestion for this module — addressed at the caller instead of here. connectionPathApi.ts is a thin stateless fetch wrapper, so adding an AbortController would change its exported signature for all callers. The actual race (rapid namespace switches applying out-of-order results) is now guarded in ConnectionPathTab.tsx via a cancelled flag in the effect cleanup, so stale responses are dropped. Flagging here for visibility.
Pre-push hook (prettier) wrapped long connectionPath.intro lines across the 13 locale files and normalized ConnectionPathPanel.test.tsx. No content changes; formatting only.
# Conflicts: # app/src/pages/Intelligence.tsx
# Conflicts: # app/src/lib/i18n/ar.ts # app/src/lib/i18n/bn.ts # app/src/lib/i18n/de.ts # app/src/lib/i18n/en.ts # app/src/lib/i18n/es.ts # app/src/lib/i18n/fr.ts # app/src/lib/i18n/hi.ts # app/src/lib/i18n/id.ts # app/src/lib/i18n/it.ts # app/src/lib/i18n/ko.ts # app/src/lib/i18n/pl.ts # app/src/lib/i18n/pt.ts # app/src/lib/i18n/ru.ts # app/src/lib/i18n/zh-CN.ts # app/src/pages/Intelligence.tsx
The prior upstream merge commit left unresolved conflict markers in Intelligence.tsx (the Edit lost a race with the merge). Resolve by keeping BOTH the upstream Knowledge Freshness tab and this PR's Connection Path tab: union the IntelligenceTab type, the tab list, and the render switch so both 'freshness' and 'path' tabs render. Typecheck clean, i18n gates pass, all 28 connection-path tests pass.
The mount effect calls load(''), which synchronously sets loading/error.
Since loading initializes to true and error to null, that is a no-op on the
first render with no cascading re-render. Annotate the intentional
fetch-on-mount with a scoped eslint-disable + rationale rather than
restructuring the shared loader.
# Conflicts: # app/src/pages/Intelligence.tsx
Summary
Adds a new read-only "Paths" tab that traces the shortest chain of relations linking any two entities — answering "how is A connected to B?" with the actual hop-by-hop path, something a single direct-edge lookup can't show.
Pick two entities (type-ahead pickers populated from the graph) and the tab renders the path as a node chain, e.g.
You —[works_at]→ Acme ←[founded_by]— Dana, with each hop's predicate and direction.Design
lib/memory/connectionPath.ts): BFS shortest path over the undirected graph (a relation connects its endpoints regardless of arrow direction), but each hop records the predicate and whether it was traversed forward or backward, so the chain reads naturally. No clock, no RNG — ties are broken by a fixed sorted neighbour order, so the same graph + endpoints always yield the same path. Self-loops are ignored, and distinctreasons cover same-entity / missing-endpoint / no-path.memoryGraphQueryandmemoryListNamespaceswrappers. The graph is fetched once per namespace; path-finding itself runs in the pure synchronous engine viauseMemo, so tracing endpoints is instant with no extra round-trips. Read-only.<datalist>(type-ahead) entity pickers handle large graphs gracefully; namespace selector. i18n across all 13 locales.Edge cases (tested)
Forward chain, reversed-direction hops, direct one-hop link, same-entity, missing source/target, disconnected (no path), shortest-path tie-break determinism + relation-order invariance, self-loop ignored, malformed rows dropped.
Test plan
vitest— 24 tests (engine: chains/direction/same/missing/no-path/tie-break/self-loop/direct/malformed; api facade; panel states; container load + trace + error)tsc --noEmit— cleaneslint— 0 errorsprettier --check— cleanconnectionPath.*keys🤖 Generated with Claude Code
Summary by CodeRabbit