Add OpenMemory (#2676)
Co-authored-by: Saket Aryan <94069182+whysosaket@users.noreply.github.com> Co-authored-by: Saket Aryan <saketaryan2002@gmail.com>
This commit is contained in:
0
openmemory/api/app/utils/__init__.py
Normal file
0
openmemory/api/app/utils/__init__.py
Normal file
37
openmemory/api/app/utils/categorization.py
Normal file
37
openmemory/api/app/utils/categorization.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
from openai import OpenAI
|
||||
from typing import List
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import BaseModel
|
||||
from tenacity import retry, stop_after_attempt, wait_exponential
|
||||
from app.utils.prompts import MEMORY_CATEGORIZATION_PROMPT
|
||||
|
||||
load_dotenv()
|
||||
|
||||
openai_client = OpenAI()
|
||||
|
||||
|
||||
class MemoryCategories(BaseModel):
|
||||
categories: List[str]
|
||||
|
||||
|
||||
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=15))
|
||||
def get_categories_for_memory(memory: str) -> List[str]:
|
||||
"""Get categories for a memory."""
|
||||
try:
|
||||
response = openai_client.responses.parse(
|
||||
model="gpt-4o-mini",
|
||||
instructions=MEMORY_CATEGORIZATION_PROMPT,
|
||||
input=memory,
|
||||
temperature=0,
|
||||
text_format=MemoryCategories,
|
||||
)
|
||||
response_json =json.loads(response.output[0].content[0].text)
|
||||
categories = response_json['categories']
|
||||
categories = [cat.strip().lower() for cat in categories]
|
||||
# TODO: Validate categories later may be
|
||||
return categories
|
||||
except Exception as e:
|
||||
raise e
|
||||
32
openmemory/api/app/utils/db.py
Normal file
32
openmemory/api/app/utils/db.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from sqlalchemy.orm import Session
|
||||
from app.models import User, App
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
def get_or_create_user(db: Session, user_id: str) -> User:
|
||||
"""Get or create a user with the given user_id"""
|
||||
user = db.query(User).filter(User.user_id == user_id).first()
|
||||
if not user:
|
||||
user = User(user_id=user_id)
|
||||
db.add(user)
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
return user
|
||||
|
||||
|
||||
def get_or_create_app(db: Session, user: User, app_id: str) -> App:
|
||||
"""Get or create an app for the given user"""
|
||||
app = db.query(App).filter(App.owner_id == user.id, App.name == app_id).first()
|
||||
if not app:
|
||||
app = App(owner_id=user.id, name=app_id)
|
||||
db.add(app)
|
||||
db.commit()
|
||||
db.refresh(app)
|
||||
return app
|
||||
|
||||
|
||||
def get_user_and_app(db: Session, user_id: str, app_id: str) -> Tuple[User, App]:
|
||||
"""Get or create both user and their app"""
|
||||
user = get_or_create_user(db, user_id)
|
||||
app = get_or_create_app(db, user, app_id)
|
||||
return user, app
|
||||
51
openmemory/api/app/utils/memory.py
Normal file
51
openmemory/api/app/utils/memory.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import os
|
||||
|
||||
from mem0 import Memory
|
||||
|
||||
|
||||
memory_client = None
|
||||
|
||||
|
||||
def get_memory_client(custom_instructions: str = None):
|
||||
"""
|
||||
Get or initialize the Mem0 client.
|
||||
|
||||
Args:
|
||||
custom_instructions: Optional instructions for the memory project.
|
||||
|
||||
Returns:
|
||||
Initialized Mem0 client instance.
|
||||
|
||||
Raises:
|
||||
Exception: If required API keys are not set.
|
||||
"""
|
||||
global memory_client
|
||||
|
||||
if memory_client is not None:
|
||||
return memory_client
|
||||
|
||||
try:
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "qdrant",
|
||||
"config": {
|
||||
"collection_name": "openmemory",
|
||||
"host": "mem0_store",
|
||||
"port": 6333,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memory_client = Memory.from_config(config_dict=config)
|
||||
except Exception:
|
||||
raise Exception("Exception occurred while initializing memory client")
|
||||
|
||||
# Update project with custom instructions if provided
|
||||
if custom_instructions:
|
||||
memory_client.update_project(custom_instructions=custom_instructions)
|
||||
|
||||
return memory_client
|
||||
|
||||
|
||||
def get_default_user_id():
|
||||
return "default_user"
|
||||
52
openmemory/api/app/utils/permissions.py
Normal file
52
openmemory/api/app/utils/permissions.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
from sqlalchemy.orm import Session
|
||||
from app.models import Memory, App, MemoryState
|
||||
|
||||
|
||||
def check_memory_access_permissions(
|
||||
db: Session,
|
||||
memory: Memory,
|
||||
app_id: Optional[UUID] = None
|
||||
) -> bool:
|
||||
"""
|
||||
Check if the given app has permission to access a memory based on:
|
||||
1. Memory state (must be active)
|
||||
2. App state (must not be paused)
|
||||
3. App-specific access controls
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
memory: Memory object to check access for
|
||||
app_id: Optional app ID to check permissions for
|
||||
|
||||
Returns:
|
||||
bool: True if access is allowed, False otherwise
|
||||
"""
|
||||
# Check if memory is active
|
||||
if memory.state != MemoryState.active:
|
||||
return False
|
||||
|
||||
# If no app_id provided, only check memory state
|
||||
if not app_id:
|
||||
return True
|
||||
|
||||
# Check if app exists and is active
|
||||
app = db.query(App).filter(App.id == app_id).first()
|
||||
if not app:
|
||||
return False
|
||||
|
||||
# Check if app is paused/inactive
|
||||
if not app.is_active:
|
||||
return False
|
||||
|
||||
# Check app-specific access controls
|
||||
from app.routers.memories import get_accessible_memory_ids
|
||||
accessible_memory_ids = get_accessible_memory_ids(db, app_id)
|
||||
|
||||
# If accessible_memory_ids is None, all memories are accessible
|
||||
if accessible_memory_ids is None:
|
||||
return True
|
||||
|
||||
# Check if memory is in the accessible set
|
||||
return memory.id in accessible_memory_ids
|
||||
28
openmemory/api/app/utils/prompts.py
Normal file
28
openmemory/api/app/utils/prompts.py
Normal file
@@ -0,0 +1,28 @@
|
||||
MEMORY_CATEGORIZATION_PROMPT = """Your task is to assign each piece of information (or “memory”) to one or more of the following categories. Feel free to use multiple categories per item when appropriate.
|
||||
|
||||
- Personal: family, friends, home, hobbies, lifestyle
|
||||
- Relationships: social network, significant others, colleagues
|
||||
- Preferences: likes, dislikes, habits, favorite media
|
||||
- Health: physical fitness, mental health, diet, sleep
|
||||
- Travel: trips, commutes, favorite places, itineraries
|
||||
- Work: job roles, companies, projects, promotions
|
||||
- Education: courses, degrees, certifications, skills development
|
||||
- Projects: to‑dos, milestones, deadlines, status updates
|
||||
- AI, ML & Technology: infrastructure, algorithms, tools, research
|
||||
- Technical Support: bug reports, error logs, fixes
|
||||
- Finance: income, expenses, investments, billing
|
||||
- Shopping: purchases, wishlists, returns, deliveries
|
||||
- Legal: contracts, policies, regulations, privacy
|
||||
- Entertainment: movies, music, games, books, events
|
||||
- Messages: emails, SMS, alerts, reminders
|
||||
- Customer Support: tickets, inquiries, resolutions
|
||||
- Product Feedback: ratings, bug reports, feature requests
|
||||
- News: articles, headlines, trending topics
|
||||
- Organization: meetings, appointments, calendars
|
||||
- Goals: ambitions, KPIs, long‑term objectives
|
||||
|
||||
Guidelines:
|
||||
- Return only the categories under 'categories' key in the JSON format.
|
||||
- If you cannot categorize the memory, return an empty list with key 'categories'.
|
||||
- Don't limit yourself to the categories listed above only. Feel free to create new categories based on the memory. Make sure that it is a single phrase.
|
||||
"""
|
||||
Reference in New Issue
Block a user