From fb26d5e954339cbafff57d568c80407345bb0216 Mon Sep 17 00:00:00 2001 From: adarshpandey-cs21 Date: Fri, 27 Mar 2026 16:42:28 +0530 Subject: [PATCH] feat(memory): add per-call memory control options (read/write) for generate and stream in NeuroLink - add per-call memory control options (read/write) for generate and stream in NeuroLink --- docs/features/memory.md | 72 +++++++++++++++++++++++++++++++++- src/lib/neurolink.ts | 69 ++++++++++++++++++++++++++++---- src/lib/types/generateTypes.ts | 17 ++++++++ src/lib/types/streamTypes.ts | 16 ++++++++ 4 files changed, 166 insertions(+), 8 deletions(-) diff --git a/docs/features/memory.md b/docs/features/memory.md index 5f391f1af3..f3760cf033 100644 --- a/docs/features/memory.md +++ b/docs/features/memory.md @@ -279,7 +279,77 @@ For memory to activate on a call, all three conditions must be met: 1. `memory.enabled` is `true` in the config 2. `options.context.userId` is provided in the generate/stream call -3. The response has non-empty content (for storage) +3. The response has non-empty content (for write) + +### Per-Call Memory Control + +When memory is globally enabled, it is active for every `generate()` and `stream()` call by default. You can override this behavior on a **per-call basis** using the `memory` option without changing the global config. + +**Available flags:** + +| Flag | Type | Default | Description | +| --------- | ------- | ------- | ------------------------------------------------------------------ | +| `enabled` | boolean | `true` | Master toggle — when `false`, both read and write are skipped | +| `read` | boolean | `true` | Whether to read past memory and prepend it to the prompt | +| `write` | boolean | `true` | Whether to write this conversation turn into memory after the call | + +> **Note:** These flags only take effect when the global memory SDK is enabled. If global memory is disabled, per-call flags have no effect. + +**Precedence:** + +1. **Global config** — Is memory enabled globally? If not, per-call flags are ignored. +2. **`enabled`** — Master per-call toggle. If `false`, both read and write are skipped regardless of individual flags. +3. **`read` / `write`** — Fine-grained control over individual operations. + +#### Read memory but don't write + +Use when you want past context but don't want this call stored — e.g., code review where you'll store a curated summary later. + +```typescript +const result = await neurolink.generate({ + input: { text: "Review this pull request for security issues" }, + memory: { read: true, write: false }, + context: { userId: "user-123" }, +}); +``` + +#### Write memory but don't read + +Use for onboarding or seeding memory without injecting past context into the prompt. + +```typescript +const result = await neurolink.generate({ + input: { + text: "My name is Alice. I work on the payments team and use Python.", + }, + memory: { read: false, write: true }, + context: { userId: "user-123" }, +}); +``` + +#### Skip memory entirely + +Use for operational or utility calls where memory adds noise. + +```typescript +const result = await neurolink.generate({ + input: { text: "Fetch the latest PR comments from GitHub" }, + memory: { enabled: false }, + context: { userId: "user-123" }, +}); +``` + +#### Per-call control with stream() + +The same `memory` option works identically in `stream()`. + +```typescript +const stream = await neurolink.stream({ + input: { text: "Summarize today's standup notes" }, + memory: { read: true, write: false }, + context: { userId: "user-123" }, +}); +``` ## Environment Variables diff --git a/src/lib/neurolink.ts b/src/lib/neurolink.ts index 1b8860a5ae..1fe8e23822 100644 --- a/src/lib/neurolink.ts +++ b/src/lib/neurolink.ts @@ -1292,6 +1292,56 @@ ${memoryContext} Current user's request: ${currentInput}`; } + /** + * Determine whether memory should be read (retrieved) for this call. + * Respects both the global memory SDK config and per-call overrides. + */ + private shouldReadMemory( + perCallMemory: { enabled?: boolean; read?: boolean } | undefined, + userId: unknown, + ): boolean { + if ( + !this.conversationMemoryConfig?.conversationMemory?.memory?.enabled || + !userId + ) { + return false; + } + if (perCallMemory?.enabled === false) { + return false; + } + if (perCallMemory?.read === false) { + return false; + } + return true; + } + + /** + * Determine whether memory should be written (stored) for this call. + * Respects both the global memory SDK config and per-call overrides. + */ + private shouldWriteMemory( + perCallMemory: { enabled?: boolean; write?: boolean } | undefined, + userId: unknown, + content: string | undefined | null, + ): boolean { + if ( + !this.conversationMemoryConfig?.conversationMemory?.memory?.enabled || + !userId + ) { + return false; + } + if (!content?.trim()) { + return false; + } + if (perCallMemory?.enabled === false) { + return false; + } + if (perCallMemory?.write === false) { + return false; + } + return true; + } + /** * Retrieve condensed memory for a user. * Returns the input text enhanced with memory context, or unchanged if no memory. @@ -3546,9 +3596,12 @@ Current user's request: ${currentInput}`; ): void { // Memory storage if ( - this.conversationMemoryConfig?.conversationMemory?.memory?.enabled && - options.context?.userId && - generateResult.content?.trim() + this.shouldWriteMemory( + options.memory, + options.context?.userId, + generateResult.content, + ) && + options.context?.userId ) { this.storeMemoryInBackground( originalPrompt ?? "", @@ -6110,7 +6163,7 @@ Current user's request: ${currentInput}`; // Memory retrieval if ( - this.conversationMemoryConfig?.conversationMemory?.memory?.enabled && + this.shouldReadMemory(options.memory, options.context?.userId) && options.context?.userId ) { try { @@ -6575,9 +6628,11 @@ Current user's request: ${currentInput}`; } if ( - this.conversationMemoryConfig?.conversationMemory?.memory?.enabled && - enhancedOptions.context?.userId && - accumulatedContent?.trim() + this.shouldWriteMemory( + enhancedOptions.memory, + enhancedOptions.context?.userId, + accumulatedContent, + ) ) { this.storeMemoryInBackground( originalPrompt ?? "", diff --git a/src/lib/types/generateTypes.ts b/src/lib/types/generateTypes.ts index 61bac7a58b..4d8e10a8e2 100644 --- a/src/lib/types/generateTypes.ts +++ b/src/lib/types/generateTypes.ts @@ -463,6 +463,23 @@ export type GenerateOptions = { /** Raw auth token — validated by configured auth provider */ auth?: { token: string }; + + /** + * Per-call memory control. + * + * Override the global memory SDK behavior for this specific call. + * All flags default to `true` when the global memory SDK is enabled. + * If the global memory SDK is disabled, these flags have no effect. + * + */ + memory?: { + /** Master toggle for this call. When false, both read and write are skipped. Defaults to true. */ + enabled?: boolean; + /** Whether to read condensed memory and prepend to prompt. Defaults to true. */ + read?: boolean; + /** Whether to write (add/condense) the conversation into memory after completion. Defaults to true. */ + write?: boolean; + }; }; /** diff --git a/src/lib/types/streamTypes.ts b/src/lib/types/streamTypes.ts index 102109100d..b952aa48a0 100644 --- a/src/lib/types/streamTypes.ts +++ b/src/lib/types/streamTypes.ts @@ -496,6 +496,22 @@ export type StreamOptions = { /** Raw auth token — validated by configured auth provider */ auth?: { token: string }; + + /** + * Per-call memory control. + * + * Override the global memory SDK behavior for this specific call. + * All flags default to `true` when the global memory SDK is enabled. + * If the global memory SDK is disabled, these flags have no effect. + */ + memory?: { + /** Master toggle for this call. When false, both read and write are skipped. Defaults to true. */ + enabled?: boolean; + /** Whether to read condensed memory and prepend to prompt. Defaults to true. */ + read?: boolean; + /** Whether to write (add/condense) the conversation into memory after completion. Defaults to true. */ + write?: boolean; + }; }; /**