Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contributing/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

134 changes: 134 additions & 0 deletions contributing/samples/open_memory/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# OpenMemory Sample

This sample demonstrates how to use OpenMemory as a self-hosted memory backend
for ADK agents using the community package.

## Prerequisites

- Python 3.9+ (Python 3.11+ recommended)
- Docker (for running OpenMemory)
- ADK and ADK Community installed

## Setup

### 1. Install Dependencies

```bash
pip install google-adk google-adk-community
```

### 2. Set Up OpenMemory Server

Follow the [OpenMemory Quick Start Guide](https://openmemory.cavira.app/docs/quick-start) to install and configure your OpenMemory server.

Once OpenMemory is running, you'll need:
- The OpenMemory server URL (default: `http://localhost:8080`)
- An API key for authentication by setting OM_API_KEY=<your-secret-api-key> (configured in your OpenMemory server)

### 3. Configure Environment Variables for adk

Create a `.env` file in this directory :

```bash
# Required: Google API key for the agent
GOOGLE_API_KEY=your-google-api-key

# Required: OpenMemory API key for authentication
OPENMEMORY_API_KEY=your-openmemory-api-key

# Optional: OpenMemory base URL (defaults to http://localhost:8080)
OPENMEMORY_BASE_URL=http://localhost:8080
```

**Note:** `OPENMEMORY_API_KEY` is required for OpenMemory authentication.

## Usage

The sample provides an agent definition that you can use programmatically. The agent includes memory tools and auto-save functionality.

```python
from google.adk_community.memory import OpenMemoryService, OpenMemoryServiceConfig
from google.adk.runners import Runner

# Create OpenMemory service with API key (required)
memory_service = OpenMemoryService(
base_url="http://localhost:8080", # Adjust to match your OpenMemory server URL
api_key="your-api-key" # Required - get this from your OpenMemory server configuration
)

# Use with runner
runner = Runner(
app_name="my_app",
agent=root_agent,
memory_service=memory_service
)
```

### Advanced Configuration

```python
from google.adk_community.memory import OpenMemoryService, OpenMemoryServiceConfig

# Custom configuration
config = OpenMemoryServiceConfig(
search_top_k=20, # Retrieve more memories per query
timeout=10.0, # Faster timeout for production
user_content_salience=0.9, # Higher importance for user messages
model_content_salience=0.75, # Medium importance for model responses
enable_metadata_tags=True # Add tags (session, app, author) for filtering
# When enabled, memories are tagged with session ID, app name,
# and author, allowing search queries to filter by app name
)

memory_service = OpenMemoryService(
base_url="http://localhost:8080", # Adjust to match your OpenMemory server URL
api_key="your-api-key", # Required - get this from your OpenMemory server configuration
config=config
)
```

## Sample Agent

The sample agent (`agent.py`) includes:
- Memory tools (`load_memory_tool`, `preload_memory_tool`) for retrieving past conversations
- Auto-save callback that saves sessions to memory after each agent turn
- Time context for the agent to use current time in responses

Copy link
Collaborator

Choose a reason for hiding this comment

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

could you provide one sample query or script to show case how this agent works with memory?

Copy link
Collaborator

Choose a reason for hiding this comment

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

addressed.

## Sample Query
- hello my name is Amy and i love gaming
- I really love fps games like GameA and GameB, but my favourite is GameC. my favourite snack is doritos.

Then in a new session
- tell me everything you know about me
(Agent recalled prior details ( my name, love for gaming and mentioned games ,snacks etc))

## Configuration Options

### OpenMemoryServiceConfig

- `search_top_k` (int, default: 10): Maximum memories to retrieve per search
- `timeout` (float, default: 30.0): HTTP request timeout in seconds
- `user_content_salience` (float, default: 0.8): Importance for user messages
- `model_content_salience` (float, default: 0.7): Importance for model responses
- `default_salience` (float, default: 0.6): Fallback importance value
- `enable_metadata_tags` (bool, default: True): Include tags for filtering. When enabled,
memories are tagged with `session:{session_id}`, `app:{app_name}`, and `author:{author}`.
These tags are used to filter search results by app name, improving isolation between
different applications using the same OpenMemory instance.

## Features

OpenMemory provides:

- **Multi-sector embeddings**: Factual, emotional, temporal, relational memory
- **Graceful decay curves**: Automatic reinforcement keeps relevant context sharp
- **Self-hosted**: Full data ownership, no vendor lock-in
- **High performance**: 2-3× faster than hosted alternatives
- **Cost-effective**: 6-10× cheaper than SaaS memory APIs

## Learn More

- [OpenMemory Documentation](https://openmemory.cavira.app/)
- [OpenMemory API Reference](https://openmemory.cavira.app/docs/api/add-memory)
- [ADK Memory Documentation](https://google.github.io/adk-docs)

16 changes: 16 additions & 0 deletions contributing/samples/open_memory/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from . import agent

52 changes: 52 additions & 0 deletions contributing/samples/open_memory/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from datetime import datetime

from google.adk import Agent
from google.adk.agents.callback_context import CallbackContext
from google.adk.tools.load_memory_tool import load_memory_tool
from google.adk.tools.preload_memory_tool import preload_memory_tool


def update_current_time(callback_context: CallbackContext):
callback_context.state['_time'] = datetime.now().isoformat()


async def auto_save_session_to_memory_callback(callback_context: CallbackContext):
"""Auto-saves the current session to memory after each agent turn.

Since there's no automatic save (saves only happen when the PATCH /memory endpoint
is called), this callback is a simple workaround for testing that saves memories
after each agent turn.
"""
if callback_context._invocation_context.memory_service:
await callback_context._invocation_context.memory_service.add_session_to_memory(
callback_context._invocation_context.session)


root_agent = Agent(
model='gemini-2.5-flash',
name='open_memory_agent',
description='agent that has access to memory tools with OpenMemory.',
before_agent_callback=update_current_time,
after_agent_callback=auto_save_session_to_memory_callback,
instruction=(
'You are an agent that helps user answer questions. You have access to memory tools.\n'
'You can use the memory tools to look up the information in the memory. Current time: {_time}'
),
tools=[load_memory_tool, preload_memory_tool],
)

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ dependencies = [
# go/keep-sorted start
"google-genai>=1.21.1, <2.0.0", # Google GenAI SDK
"google-adk", # Google ADK
"httpx>=0.27.0, <1.0.0", # For OpenMemory service
"redis>=5.0.0, <6.0.0", # Redis for session storage
# go/keep-sorted end
"orjson>=3.11.3",
Expand Down
2 changes: 2 additions & 0 deletions src/google/adk_community/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from . import memory
from . import sessions
from . import version
__version__ = version.__version__
24 changes: 24 additions & 0 deletions src/google/adk_community/memory/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Community memory services for ADK."""

from .open_memory_service import OpenMemoryService
Copy link
Collaborator

Choose a reason for hiding this comment

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

let's do lazy loading here. @wuliang229 to help provide some guidance on what's the proper way.

Copy link
Collaborator

Choose a reason for hiding this comment

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

let's do it in a separate PR.

from .open_memory_service import OpenMemoryServiceConfig

__all__ = [
"OpenMemoryService",
"OpenMemoryServiceConfig",
]

Loading