Skip to content

feat(memory): implement Vertex AI Memory Bank service with tests#291

Open
AmaadMartin wants to merge 19 commits intogoogle:mainfrom
AmaadMartin:feat/vertex-ai-memory-services
Open

feat(memory): implement Vertex AI Memory Bank service with tests#291
AmaadMartin wants to merge 19 commits intogoogle:mainfrom
AmaadMartin:feat/vertex-ai-memory-services

Conversation

@AmaadMartin
Copy link
Copy Markdown
Collaborator

Link to Issue or Description of Change

Or, if no issue exists, describe the change:

Problem:
The adk-js repository lacked an implementation for VertexAiMemoryBankService to interact with Vertex AI Memory Bank for storage and retrieval of semantic memories, which was already available in the Python implementation.

Solution:

  • Implemented VertexAiMemoryBankService: Created core/src/memory/vertex_ai_memory_bank_service.ts leveraging the Memories client from @google-cloud/vertexai to support adding sessions, adding explicit memories, and searching memories.
  • Comprehensive Testing:
    • Added unit tests in core/test/memory/vertex_ai_memory_bank_service_test.ts achieving 94.78% line coverage.
    • Added integration tests in tests/integration/memory/vertex_ai_memory_bank_service_test.ts to verify interaction with the Runner and LOAD_MEMORY tool.

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Summary of passed results:

  • Unit tests for VertexAiMemoryBankService pass with high coverage (94.78%), covering initialization, adding sessions, adding memories, searching, and metadata conversion.

Manual End-to-End (E2E) Tests:

  • Verified behavior via integration tests in tests/integration/memory/vertex_ai_memory_bank_service_test.ts simulating the full flow with a mock client, Runner, and the LOAD_MEMORY tool.

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.

@kalenkevich kalenkevich self-assigned this Apr 24, 2026
@kalenkevich kalenkevich self-requested a review April 24, 2026 17:39
Copy link
Copy Markdown
Collaborator

@kalenkevich kalenkevich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is partial review
I will do the rest during the day

Thanks!

Comment on lines +7 to +17
import {Client} from '@google-cloud/vertexai/build/src/genai/client.js';
import {Memories} from '@google-cloud/vertexai/build/src/genai/memories.js';
import {
AgentEngineMemoryConfig,
CreateAgentEngineMemoryRequestParameters,
GenerateAgentEngineMemoriesConfig,
GenerateAgentEngineMemoriesRequestParameters,
GenerateMemoriesRequestDirectContentsSourceEvent,
MemoryMetadataValue,
RetrieveAgentEngineMemoriesRequestParameters,
} from '@google-cloud/vertexai/build/src/genai/types.js';
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we import everything just from @google-cloud/vertexai?

const retrievedMemoriesResponse =
await this.memories.retrieveInternal(params);

logger.info('Search memory response received.');
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need this to be info level, debug is enough.

Comment on lines +122 to +123
await this._addMemoriesViaGenerateDirectMemoriesSource(request);
return;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could be just

return this._addMemoriesViaGenerateDirectMemoriesSource(request);

Comment on lines +153 to +157
const part: Part = {text: retrievedMemory.memory.fact};
const content: Content = {
parts: [part],
role: 'user',
};
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use const content = createUserContent(retrievedMemory.memory.fact) util function from @google/genai

Comment on lines +285 to +300
private _shouldFilterOutEvent(content?: Content): boolean {
if (!content || !content.parts) {
return true;
}
for (const part of content.parts) {
const explicitPart: Part = part;
if (
explicitPart.text ||
explicitPart.inlineData ||
explicitPart.fileData
) {
return false;
}
}
return true;
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and below:
this could be just a util function no need to be part of the class.

Check where this. is not called in the methods and make them as util functions. It can happen that after doing that you will see that other methods now also could be util functions as they used this just to call a method which became an util function.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private _shouldFilterOutEvent(content?: Content): boolean {
if (!content || !content.parts) {
return true;
}
for (const part of content.parts) {
const explicitPart: Part = part;
if (
explicitPart.text ||
explicitPart.inlineData ||
explicitPart.fileData
) {
return false;
}
}
return true;
}
function shouldFilterOutEvent(content?: Content): boolean {
return !(content?.parts || []).some(p => p.text || p.inlineData || p.fileData);
}

if (this._shouldFilterOutEvent(event.content)) {
continue;
}
if (event.content) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this check does not make sence as you just checked that in _shouldFilterOutEvent function

config: config,
};
const operation = await this.memories.generateInternal(params);
logger.info('Generate memory response received.');
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make it debug as well

return vertexMetadata;
}

private _toVertexMetadataValue(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be just an util function

Comment on lines +343 to +352
const knownFields = [
'disableConsolidation',
'waitForCompletion',
'revisionLabels',
'revisionExpireTime',
'revisionTtl',
'disableMemoryRevisions',
'metadataMergeStrategy',
'allowedTopics',
];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and in all other similar places

move this on the file level as you don't need to create this string list every time with exact same values, you can do it only once.

Comment on lines +517 to +519
interface MemoryEntryWithMetadata extends MemoryEntry {
customMetadata?: Record<string, unknown>;
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this on file level

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants