Repository navigation
feat(memory): integrate Hippocampus SDK for enhanced user memory mana… - #839
Conversation
|
@adarshjuspay is attempting to deploy a commit to the Sachin Sharma's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThis PR integrates Hippocampus, an embedded memory engine, into NeuroLink. It adds environment configuration, npm dependency, an initialization module, and modifies the core NeuroLink class to retrieve memory before generation and store conversation turns asynchronously after responses complete. Changes
Sequence DiagramsequenceDiagram
participant Client
participant NeuroLink
participant Hippocampus
participant Storage as Memory Storage
Client->>NeuroLink: generate() or stream()
NeuroLink->>NeuroLink: ensureHippocampusReady()
NeuroLink->>Hippocampus: retrieve(inputText, userId)
Hippocampus->>Storage: fetch stored context
Storage-->>Hippocampus: return context
Hippocampus-->>NeuroLink: condensed memory
NeuroLink->>NeuroLink: augment prompt with memory
NeuroLink->>NeuroLink: generate response
NeuroLink-->>Client: return response
NeuroLink->>Hippocampus: store(prompt, response, userId) [async, non-blocking]
Hippocampus->>Storage: save new turn
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly Related PRs
Suggested Reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). 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.
Pull request overview
This pull request integrates the Hippocampus SDK to provide embedded, condensed key-value memory management per user in NeuroLink. Hippocampus offers an alternative to the existing mem0 cloud-based memory solution by providing a lightweight, embedded SDK that condenses conversation history into compact memory representations.
Changes:
- Added Hippocampus SDK dependency (
@juspay/hippocampus@^0.1.2) - Implemented lazy initialization pattern for Hippocampus memory engine
- Integrated memory retrieval and storage hooks in both
generate()andstream()methods
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/lib/memory/hippocampusInitializer.ts |
New initializer module for Hippocampus SDK, follows same pattern as mem0Initializer |
src/lib/types/conversation.ts |
Added type definitions for Hippocampus configuration options |
src/lib/neurolink.ts |
Integrated Hippocampus memory retrieval/storage in generate and stream methods with lazy initialization |
package.json |
Added @juspay/hippocampus dependency |
pnpm-lock.yaml |
Lock file updates for new dependency and its transitive dependencies |
.env.example |
Added environment variable documentation for Hippocampus configuration |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (3)
src/lib/neurolink.ts:1086
- The new Hippocampus memory integration lacks test coverage. Looking at the test suite in
test/continuous-test-suite-memory.ts, there's a test for mem0 integration (testMem0Integration) but no equivalent test for Hippocampus. Consider adding a similar test that:
- Initializes NeuroLink with hippocampusEnabled: true
- Generates a response with user context
- Verifies memory is stored and retrieved correctly in subsequent requests
This would match the testing pattern established for mem0 and ensure the integration works as expected.
private storeHippocampusMemoryInBackground(
originalPrompt: string,
responseContent: string,
userId: string,
): void {
setImmediate(async () => {
try {
const client = await this.ensureHippocampusReady();
if (client) {
const content = `User: ${originalPrompt}\nAssistant: ${responseContent}`;
await client.add(userId, content);
}
} catch (error) {
logger.warn("Hippocampus memory storage failed:", error);
}
});
}
src/lib/types/conversation.ts:72
- The Hippocampus integration lacks documentation. Looking at the codebase, mem0 integration has extensive documentation in
docs/mem0-integration.mdanddocs/advanced/memory-integration.md, but there's no equivalent documentation for Hippocampus. Consider adding:
-
A dedicated documentation page (e.g.,
docs/hippocampus-integration.md) explaining:- What Hippocampus is and how it differs from mem0
- Configuration options (storage backends, condensation settings, etc.)
- Usage examples
- Best practices
-
Update
docs/configuration.mdto include Hippocampus configuration examples -
Add examples in the
examples/directory demonstrating Hippocampus usage
This would help users understand when to use Hippocampus vs mem0 and how to configure it properly.
/** Enable Hippocampus embedded memory SDK (condensed key-value memory per user) */
hippocampusEnabled?: boolean;
/** Configuration for Hippocampus embedded memory SDK (storage backend, condensation LLM, etc.) */
hippocampusConfig?: HippocampusConfig;
package.json:185
- The
@juspay/hippocampuspackage has a peer dependency on@juspay/neurolink(>= 9.0.0), which creates a circular dependency since NeuroLink is now depending on Hippocampus. While npm/pnpm can handle peer dependencies, this design pattern can lead to:
- Maintenance complexity: Updates to either package may require coordinated releases
- Bundle size concerns: Both packages may include shared code
- Version conflicts: If NeuroLink and Hippocampus versions get out of sync
Consider whether this circular dependency is necessary. If Hippocampus genuinely needs NeuroLink functionality, it might be better to:
- Extract shared types/interfaces into a separate package
- Use dependency injection to avoid tight coupling
- Document the version compatibility matrix clearly
If this is intentional (e.g., Hippocampus uses NeuroLink's AI capabilities for condensation), please add comments explaining the design decision.
"@juspay/hippocampus": "^0.1.2",
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| inputText: string, | ||
| userId: string, | ||
| ): Promise<string> { | ||
| const client = await this.ensureHippocampusReady(); |
There was a problem hiding this comment.
The ensureHippocampusReady() method is declared as synchronous (returns Hippocampus | null) on line 477, but it's being called with await here. This is incorrect usage - you should either:
- Remove the
awaitkeyword since the method is synchronous, OR - Make
ensureHippocampusReady()async if asynchronous initialization is needed (likeensureMem0Ready()on line 445).
Given that the initializeHippocampus() function is synchronous, option 1 (removing await) is the correct fix.
| const client = await this.ensureHippocampusReady(); | |
| const client = this.ensureHippocampusReady(); |
| ): void { | ||
| setImmediate(async () => { | ||
| try { | ||
| const client = await this.ensureHippocampusReady(); |
There was a problem hiding this comment.
The ensureHippocampusReady() method is declared as synchronous (returns Hippocampus | null) on line 477, but it's being called with await here. This is incorrect - you should remove the await keyword since the method returns a synchronous value, not a Promise.
3957f15 to
348cd3e
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
src/lib/types/conversation.ts (1)
37-37: Decouple types from runtime initializer modules.
Line 37 importsHippocampusConfigfrom a runtime memory initializer path; prefer exporting this type from a types-domain module to reduce cross-layer coupling and circular-dependency risk.As per coding guidelines,
src/lib/types/*.ts: Organize types by domain (providers, generation, streaming, MCP, etc.) to avoid circular dependencies.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/types/conversation.ts` at line 37, The file imports the runtime initializer type HippocampusConfig from a memory initializer which couples types to runtime; create a dedicated types-domain file (e.g., export HippocampusConfig from a new or existing src/lib/types/memory or hippocampus-types module), move or re-export the type there, and update the import in conversation.ts to import HippocampusConfig as a type-only import from that types module (ensure the new module exports only types to avoid runtime imports and break circular dependencies).src/lib/memory/hippocampusInitializer.ts (2)
12-28: UsetransformParamsForLogging()for logger payloads.
Line 12 and Line 23 pass ad-hoc objects directly; route payloads throughtransformParamsForLogging()for consistent redaction/safe logging behavior.As per coding guidelines,
src/**/*.ts: Use transformParamsForLogging() for safe parameter logging without exposing sensitive data.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/memory/hippocampusInitializer.ts` around lines 12 - 28, The logger calls in the hippocampus initializer (the logger.info after successful init and the logger.warn in the catch) are passing raw payload objects; wrap those payload objects with transformParamsForLogging() so the info and warn calls use transformParamsForLogging({ storageType: config.storage?.type || "sqlite", maxWords: config.maxWords || 50, hasCustomPrompt: !!config.prompt }) and transformParamsForLogging({ error: error instanceof Error ? error.message : String(error) }) respectively; locate the calls around the Hippocampus initialization/return and update logger.info and logger.warn to pass the transformed payloads for safe/redacted logging.
15-16: Align storage fallback assumptions with project memory policy.
Line 15 implies"sqlite"fallback in Hippocampus initialization context; prefer enforcing/reporting the project-supported modes (memoryfor development,redisfor distributed) to avoid backend drift.As per coding guidelines,
src/lib/memory/**/*.ts: Conversational memory uses Redis for distributed systems and in-memory store for development.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/memory/hippocampusInitializer.ts` around lines 15 - 16, The code currently falls back to "sqlite" for storageType in the Hippocampus initializer; change this to enforce the project's supported modes by validating config.storage?.type and selecting "memory" for local/dev and "redis" for distributed deployments (or derive from an env flag like NODE_ENV/IS_DISTRIBUTED); update the Hippocampus initialization (where storageType is read) to default to "memory" when in development, default to "redis" when the app is running in distributed mode, and throw/log a clear error if an unsupported storage type is provided so drift to "sqlite" cannot occur.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/neurolink.ts`:
- Around line 1048-1063: The Hippocampus SDK calls (e.g., memory = await
client.get(userId) inside retrieveHippocampusMemory and the corresponding write
call in the hippocampus store routine) are unguarded and can hang; wrap these
client calls with the withTimeout utility (e.g., replace await
client.get(userId) with await withTimeout(client.get(userId), HIPPO_TIMEOUT_MS))
and likewise wrap the store call in the storeHippocampusMemory path, using a
shared timeout constant (HIPPO_TIMEOUT_MS) and ensuring you still handle timeout
rejections the same way you handle a missing client (use ensureHippocampusReady
to bail early). Ensure imports/definitions for withTimeout and the timeout
constant are present.
- Around line 2269-2283: Remove the redundant "as string" casts after the truthy
checks on options.context?.userId; since the guard if (options.context?.userId)
already narrows userId to string, call retrieveHippocampusMemory and other
functions (e.g., any calls that pass options.context.userId) using
options.context.userId directly without casting, and update all similar sites
(the other retrieve-like call sites referenced) to use the explicit guarded
value rather than "as string".
---
Nitpick comments:
In `@src/lib/memory/hippocampusInitializer.ts`:
- Around line 12-28: The logger calls in the hippocampus initializer (the
logger.info after successful init and the logger.warn in the catch) are passing
raw payload objects; wrap those payload objects with transformParamsForLogging()
so the info and warn calls use transformParamsForLogging({ storageType:
config.storage?.type || "sqlite", maxWords: config.maxWords || 50,
hasCustomPrompt: !!config.prompt }) and transformParamsForLogging({ error: error
instanceof Error ? error.message : String(error) }) respectively; locate the
calls around the Hippocampus initialization/return and update logger.info and
logger.warn to pass the transformed payloads for safe/redacted logging.
- Around line 15-16: The code currently falls back to "sqlite" for storageType
in the Hippocampus initializer; change this to enforce the project's supported
modes by validating config.storage?.type and selecting "memory" for local/dev
and "redis" for distributed deployments (or derive from an env flag like
NODE_ENV/IS_DISTRIBUTED); update the Hippocampus initialization (where
storageType is read) to default to "memory" when in development, default to
"redis" when the app is running in distributed mode, and throw/log a clear error
if an unsupported storage type is provided so drift to "sqlite" cannot occur.
In `@src/lib/types/conversation.ts`:
- Line 37: The file imports the runtime initializer type HippocampusConfig from
a memory initializer which couples types to runtime; create a dedicated
types-domain file (e.g., export HippocampusConfig from a new or existing
src/lib/types/memory or hippocampus-types module), move or re-export the type
there, and update the import in conversation.ts to import HippocampusConfig as a
type-only import from that types module (ensure the new module exports only
types to avoid runtime imports and break circular dependencies).
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (5)
.env.examplepackage.jsonsrc/lib/memory/hippocampusInitializer.tssrc/lib/neurolink.tssrc/lib/types/conversation.ts
| private async retrieveHippocampusMemory( | ||
| inputText: string, | ||
| userId: string, | ||
| ): Promise<string> { | ||
| const client = this.ensureHippocampusReady(); | ||
| if (!client) { | ||
| return inputText; | ||
| } | ||
|
|
||
| const memory = await client.get(userId); | ||
| if (!memory) { | ||
| return inputText; | ||
| } | ||
|
|
||
| return this.formatMemoryContext(memory, inputText); | ||
| } |
There was a problem hiding this comment.
Add timeout guards around Hippocampus SDK calls.
Line 1057 and Line 1079 call external memory operations without withTimeout; a stalled SDK call can block request flow (retrieve) or leave background tasks hanging (store). As per coding guidelines, "Wrap async operations with withTimeout utility to prevent indefinite hangs".
⚙️ Proposed fix
private async retrieveHippocampusMemory(
inputText: string,
userId: string,
): Promise<string> {
+ const hippocampusTimeoutMs = 3000;
const client = this.ensureHippocampusReady();
if (!client) {
return inputText;
}
- const memory = await client.get(userId);
+ const memory = await withTimeout(
+ client.get(userId),
+ hippocampusTimeoutMs,
+ );
if (!memory) {
return inputText;
}
return this.formatMemoryContext(memory, inputText);
}
@@
private storeHippocampusMemoryInBackground(
originalPrompt: string,
responseContent: string,
userId: string,
): void {
+ const hippocampusTimeoutMs = 3000;
setImmediate(async () => {
try {
const client = this.ensureHippocampusReady();
if (client) {
const content = `User: ${originalPrompt}\nAssistant: ${responseContent}`;
- await client.add(userId, content);
+ await withTimeout(
+ client.add(userId, content),
+ hippocampusTimeoutMs,
+ );
}
} catch (error) {
logger.warn("Hippocampus memory storage failed:", error);
}
});
}Also applies to: 1069-1085
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/neurolink.ts` around lines 1048 - 1063, The Hippocampus SDK calls
(e.g., memory = await client.get(userId) inside retrieveHippocampusMemory and
the corresponding write call in the hippocampus store routine) are unguarded and
can hang; wrap these client calls with the withTimeout utility (e.g., replace
await client.get(userId) with await withTimeout(client.get(userId),
HIPPO_TIMEOUT_MS)) and likewise wrap the store call in the
storeHippocampusMemory path, using a shared timeout constant (HIPPO_TIMEOUT_MS)
and ensuring you still handle timeout rejections the same way you handle a
missing client (use ensureHippocampusReady to bail early). Ensure
imports/definitions for withTimeout and the timeout constant are present.
| // Hippocampus memory retrieval | ||
| if ( | ||
| this.conversationMemoryConfig?.conversationMemory?.hippocampusEnabled && | ||
| options.context?.userId | ||
| ) { | ||
| try { | ||
| options.input.text = await this.retrieveHippocampusMemory( | ||
| options.input.text, | ||
| options.context.userId as string, | ||
| ); | ||
| logger.debug("Hippocampus memory retrieval successful"); | ||
| } catch (error) { | ||
| logger.warn("Hippocampus memory retrieval failed:", error); | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for context type definitions
echo "=== Searching for context type definition ==="
rg -n --type=ts 'context\s*[:\?].*userId' src/lib/types -A 2 -B 2
echo ""
echo "=== Searching for RequestContext or similar interfaces ==="
rg -n --type=ts 'interface.*Context|type.*Context' src/lib/types -A 5 | head -60Repository: juspay/neurolink
Length of output: 3570
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Look at the four specific lines mentioned
echo "=== Line 2277 context ==="
sed -n '2270,2290p' src/lib/neurolink.ts
echo ""
echo "=== Line 2585 context ==="
sed -n '2575,2595p' src/lib/neurolink.ts
echo ""
echo "=== Line 4396 context ==="
sed -n '4388,4410p' src/lib/neurolink.ts
echo ""
echo "=== Line 4798 context ==="
sed -n '4790,4810p' src/lib/neurolink.tsRepository: juspay/neurolink
Length of output: 3057
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for all occurrences of context.userId with as string pattern
echo "=== All 'as string' casts related to userId ==="
rg -n --type=ts 'context\?.?userId\s+as\s+string' src/lib/neurolink.ts
echo ""
echo "=== All patterns using context.userId ==="
rg -n --type=ts 'context\.userId|context\?\.userId' src/lib/neurolink.ts | head -20Repository: juspay/neurolink
Length of output: 1294
Remove unnecessary as string casts after truthy checks on userId.
The type definitions consistently declare userId?: string across all context types. After the truthy check if (options.context?.userId), TypeScript correctly narrows the type to string, making the subsequent as string casts redundant. This applies to lines 2277, 2585, 4396, and 4798.
While not a type-safety issue (the code is already safe), replacing these casts with explicit type guards improves clarity and consistency:
🛡️ Suggested pattern (apply in all call sites)
- if (
- this.conversationMemoryConfig?.conversationMemory?.hippocampusEnabled &&
- options.context?.userId
- ) {
+ const hippocampusUserId =
+ typeof options.context?.userId === "string"
+ ? options.context.userId
+ : undefined;
+ if (
+ this.conversationMemoryConfig?.conversationMemory?.hippocampusEnabled &&
+ hippocampusUserId
+ ) {
try {
options.input.text = await this.retrieveHippocampusMemory(
options.input.text,
- options.context.userId as string,
+ hippocampusUserId,
);
logger.debug("Hippocampus memory retrieval successful");
} catch (error) {
logger.warn("Hippocampus memory retrieval failed:", error);
}
}Also applies to: 2576-2587, 4388-4402, 4790-4800 (and line 5132).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/neurolink.ts` around lines 2269 - 2283, Remove the redundant "as
string" casts after the truthy checks on options.context?.userId; since the
guard if (options.context?.userId) already narrows userId to string, call
retrieveHippocampusMemory and other functions (e.g., any calls that pass
options.context.userId) using options.context.userId directly without casting,
and update all similar sites (the other retrieve-like call sites referenced) to
use the explicit guarded value rather than "as string".
4a008e6 to
d48506b
Compare
| /** Configuration for mem0 cloud API integration */ | ||
| mem0Config?: Mem0Config; | ||
|
|
||
| memoryConfig?: MemoryConfig; |
There was a problem hiding this comment.
Call this memory. Update all the documentation, you might have to update multiple documentation and add an entry in the readme.md file.
d48506b to
bd536bb
Compare
…gement - Added Hippocampus initializer to handle memory instance creation and configuration. - Updated NeuroLink class to support lazy initialization of Hippocampus memory.
bd536bb to
49567ab
Compare
|
🎉 This PR is included in version 9.13.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
…gement
Pull Request
Description
What does this PR do?
A clear and concise description of the changes in this pull request.
Related Issues
Does this PR close any issues?
Fixes #(issue number)
Closes #(issue number)
Relates to #(issue number)
Type of Change
Please select the type of change:
Motivation and Context
Why is this change needed? What problem does it solve?
Provide context for reviewers:
Changes Made
What specific changes were made?
Provide a bullet-point list of the key changes:
Breaking Changes
Does this PR introduce breaking changes?
If yes, describe:
Testing
How has this been tested?
Please describe the tests you ran and their results:
Test Coverage
Manual Testing Steps
Provide steps for manual testing:
Code Quality
Have you followed code quality standards?
Documentation
Have you updated documentation?
Commit Message Format
Does your commit follow semantic commit conventions?
type(scope): descriptionExample:
feat(providers): add support for LiteLLM proxyDependencies
Does this PR add, update, or remove dependencies?
If yes, list dependencies and justification:
Performance Impact
Does this change affect performance?
If applicable, provide benchmark results:
Security Considerations
Are there any security implications?
If applicable, describe:
Deployment Notes
Special deployment instructions?
Screenshots / Videos
If applicable, add screenshots or videos to demonstrate changes:
[Add screenshots or videos here]
Reviewer Checklist
For reviewers:
Additional Notes
Any additional information for reviewers:
[Add any extra context, concerns, or questions here]
Pre-submission Checklist
Before submitting, ensure you have:
pnpm testpnpm buildpnpm run validate:alland all checks passThank you for contributing to NeuroLink!