Skip to content

Memory Playground #96

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
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
5 changes: 2 additions & 3 deletions packages/evals/benchmark_memory_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@

sys.path.append(os.path.join(os.path.dirname(__file__), ".."))

from evals.helpers import Dataset, DatasetItem, load_dataset, setup_mlflow
Copy link
Collaborator

Choose a reason for hiding this comment

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

TODO for later: unit tests. Same for the playground.

from evals.metrics import string_check_metric
from memory_module.config import LLMConfig, MemoryModuleConfig
from memory_module.core.memory_module import MemoryModule
from memory_module.interfaces.types import AssistantMessage, UserMessage

from evals.helpers import Dataset, DatasetItem, load_dataset, setup_mlflow
from evals.metrics import string_check_metric

setup_mlflow(experiment_name="memory_module")


Expand Down
1 change: 1 addition & 0 deletions packages/memory-server/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
18 changes: 18 additions & 0 deletions packages/memory-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Memory Server
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't want to spend too much extra time on this tool, but it would be worth considering how we could combine the efforts in the sample and this. (i.e. the sample has a web component that is really simple to show a user's memories). Just a consideration, not actionable.

FastAPI wrapper around the memory module.
Copy link
Collaborator

Choose a reason for hiding this comment

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

In the future, do we want to consider having the wrapper as separate package so people don't need to download the related dependencies if they want to?

(Can file for discussion and close this comment if anyone agrees)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure what you mean since the wrapper is already a separate package. It's under packages/memory-server while the memory module is under packages/memory-module.

Copy link
Collaborator

Choose a reason for hiding this comment

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

as in, if we were publishing it, don't publish it with the rest of the code (bloat). but I'm still unclear on the publishing bit haha.



## Setup Instructions

To set up and run the Memory Server, follow these steps:

1. Set up the dependencies by doing `uv sync` in the root folder.
1. Ensure the virtual environment activated in your terminal. See the root README.md file for details.
1. Configure the memory module in the `memory_service.py` file.
1. Navigate to this folder and run the following command:

```sh
uvicorn main:app
```

This will start the FastAPI server on port 8000.
17 changes: 17 additions & 0 deletions packages/memory-server/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import routes
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
Copy link
Collaborator

Choose a reason for hiding this comment

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

hmmmmmm fastapi is a very old package and not maintained. We should find a better solution for v next


app = FastAPI(title="Chat API")

# CORS configuration
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173"], # Frontend URL
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

# Include routers
app.include_router(routes.router, tags=["memory"])
39 changes: 39 additions & 0 deletions packages/memory-server/memory_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import os
import sys
import uuid
from datetime import datetime
from typing import Optional

sys.path.append(os.path.join(os.path.dirname(__file__), "../"))

from memory_module import LLMConfig, MemoryModule, MemoryModuleConfig
from memory_module.interfaces.types import AssistantMessageInput, MessageInput, UserMessageInput


class MemoryService:
def __init__(self, openai_api_key: str):
# Initialize memory module
config = MemoryModuleConfig(
db_path=os.path.join(os.path.dirname(__file__), "../memory_module/data/memory.db"),
llm=LLMConfig(model="gpt-4o", embedding_model="text-embedding-3-small", api_key=openai_api_key),
)
self._memory = MemoryModule(config)

async def add_message(self, type: str, content: str):
message: MessageInput
if type == "assistant":
message = AssistantMessageInput(id=str(uuid.uuid4()), content=content, author_id="1", conversation_ref="1")
elif type == "user":
message = UserMessageInput(
id=str(uuid.uuid4()), content=content, author_id="1", conversation_ref="1", created_at=datetime.now()
)
else:
raise ValueError("Invalid message type")

await self._memory.add_message(message)

async def retrieve_memories(self, query: str, user_id: str, limit: Optional[int] = None):
return await self._memory.retrieve_memories(query, user_id=user_id, limit=limit)

async def get_all_memories(self, user_id: str):
return await self._memory.get_user_memories(user_id=user_id)
19 changes: 19 additions & 0 deletions packages/memory-server/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[project]
name = "memory-server"
version = "0.1.0"
Copy link
Collaborator

Choose a reason for hiding this comment

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

version must be directly below [project]. Just did a PR that fixed this elsewhere :D

description = "Add your description here"
Copy link
Collaborator

Choose a reason for hiding this comment

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

description

readme = "README.md"
requires-python = ">=3.12"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you change to

Suggested change
requires-python = ">=3.12"
requires-python = ">=3.11,<3.13"

dependencies = [
"memory-module",
"fastapi==0.115.6",
"pydantic",
"python-dotenv",
"uvicorn"
]

[tool.uv]
package = false

[tool.uv.sources]
memory-module = { workspace = true }
52 changes: 52 additions & 0 deletions packages/memory-server/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import os
import sys

from dotenv import load_dotenv
from fastapi import APIRouter
from memory_service import MemoryService
from pydantic import BaseModel

sys.path.append(os.path.join(os.path.dirname(__file__), ".."))

from memory_module.interfaces.types import Memory

load_dotenv()

router = APIRouter()
openai_api_key = os.environ.get("OPENAI_API_KEY")
memory_service = MemoryService(openai_api_key=openai_api_key)


@router.get("/memories", response_model=list[Memory])
async def get_memories(user_id: str):
return await memory_service.get_all_memories(user_id=user_id)


class SearchQueryRequest(BaseModel):
query: str
user_id: str
limit: int


@router.post("/search")
async def search_memories(request: SearchQueryRequest):
if not request.user_id:
raise ValueError("User ID is required to search memories")

print(request)
if len(request.query) > 0:
return await memory_service.retrieve_memories(request.query, request.user_id, request.limit)
else:
# Return all memories if no query is provided
return await memory_service.get_all_memories(user_id=request.user_id)


class AddMessageRequest(BaseModel):
type: str
content: str


@router.post("/message")
async def add_message(request: AddMessageRequest):
await memory_service.add_message(request.type, request.content)
return {"message": "Message added successfully"}
1 change: 1 addition & 0 deletions packages/memory-server/sample.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OPENAI_API_KEY=
1 change: 1 addition & 0 deletions packages/memory_module/storage/in_memory_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Dict, List, NamedTuple, Optional, TypedDict

import numpy as np

from memory_module.interfaces.base_memory_storage import BaseMemoryStorage
from memory_module.interfaces.base_message_buffer_storage import (
BaseMessageBufferStorage,
Expand Down
1 change: 1 addition & 0 deletions packages/memory_module/storage/sqlite_memory_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Dict, List, Optional

import sqlite_vec

from memory_module.interfaces.base_memory_storage import BaseMemoryStorage
from memory_module.interfaces.types import (
BaseMemoryInput,
Expand Down
1 change: 1 addition & 0 deletions packages/memory_module/storage/sqlite_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import aiosqlite
import sqlite_vec

from memory_module.storage.migrations_manager import MigrationManager

logger = logging.getLogger(__name__)
Expand Down
1 change: 1 addition & 0 deletions packages/memory_module/utils/teams_bot_middlware.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from botbuilder.core import TurnContext
from botbuilder.core.middleware_set import Middleware
from botbuilder.schema import Activity, ResourceResponse

from memory_module.interfaces.base_memory_module import BaseMemoryModule
from memory_module.interfaces.types import (
AssistantMessageInput,
Expand Down
24 changes: 24 additions & 0 deletions playground/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
19 changes: 19 additions & 0 deletions playground/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Memory Playground UI

A simple web application to easily view and interact with the memory module.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Worth adding that this isn't meant for production


## Setup Instructions

To set up and run the playground, follow these steps:

1. Navigate to this folder in the termainl.
1. Set up the dependencies by doing `pnpm install` in the root folder.
1. Run `pnpm run dev` to start the application on port 5173.
1. Ensure that the memory server is configured and running locally. See `packages/memory-server`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Link to memory server readme?


## Notes
The memory server base url is defined in the `api/memoryServer.ts` file. By default it points to http://127.0.0.1:8000, i.e localhost:8000

# Disclaimer

Some components were taken from [TailAdmin](https://tailadmin.com/)'s free to use component package, which is under the MIT License.
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you haven't already, this disclaimer should be where that code is

29 changes: 29 additions & 0 deletions playground/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import js from '@eslint/js';
import globals from 'globals';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint';

export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'@typescript-eslint/no-explicit-any': 'warn',
},
}
);
13 changes: 13 additions & 0 deletions playground/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Memory Playground</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading
Loading