mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-10 22:41:17 +00:00
update readme and agents.md
This commit is contained in:
parent
161c698472
commit
906577759a
2 changed files with 38 additions and 227 deletions
|
|
@ -1,147 +1,34 @@
|
|||
# agents.md
|
||||
# AGENTS.md
|
||||
|
||||
Agent knowledge file for Supermemory Pipecat SDK. This describes how Supermemory integrates with Pipecat for memory-enhanced voice AI.
|
||||
## Overview
|
||||
|
||||
## What This Package Does
|
||||
This package adds persistent memory to Pipecat voice AI pipelines using Supermemory.
|
||||
|
||||
Supermemory Pipecat SDK provides a `FrameProcessor` that intercepts conversation frames, retrieves relevant memories from Supermemory, injects them into the LLM context, and stores new messages.
|
||||
**Tech Stack:** Python 3.10+, Pipecat, Supermemory SDK
|
||||
|
||||
## Package Structure
|
||||
## Commands
|
||||
|
||||
```
|
||||
packages/pipecat-sdk-python/
|
||||
├── src/supermemory_pipecat/
|
||||
│ ├── __init__.py # Exports: SupermemoryPipecatService
|
||||
│ ├── service.py # Main FrameProcessor class
|
||||
│ ├── exceptions.py # Error hierarchy
|
||||
│ └── utils.py # Helpers: get_last_user_message, deduplicate_memories
|
||||
├── pyproject.toml # Package: supermemory-pipecat
|
||||
└── README.md
|
||||
```bash
|
||||
pip install supermemory-pipecat
|
||||
```
|
||||
|
||||
## Core Class: SupermemoryPipecatService
|
||||
## Integration Pattern
|
||||
|
||||
Extends `pipecat.processors.frame_processor.FrameProcessor`.
|
||||
|
||||
### Constructor Parameters
|
||||
|
||||
| Parameter | Type | Required | Default |
|
||||
|-----------|------|----------|---------|
|
||||
| `user_id` | str | Yes | - |
|
||||
| `session_id` | str | No | None |
|
||||
| `api_key` | str | No | `SUPERMEMORY_API_KEY` env |
|
||||
| `params` | InputParams | No | defaults |
|
||||
| `base_url` | str | No | `https://api.supermemory.ai` |
|
||||
|
||||
### InputParams Configuration
|
||||
|
||||
```python
|
||||
InputParams(
|
||||
search_limit=10, # Max memories to retrieve
|
||||
search_threshold=0.1, # Similarity threshold (0.0-1.0)
|
||||
mode="full", # "profile" | "query" | "full"
|
||||
add_memory="always", # "always" | "never"
|
||||
add_as_system_message=True,
|
||||
system_prompt="Based on previous conversations, I recall:\n\n",
|
||||
position=1,
|
||||
)
|
||||
```
|
||||
|
||||
### Key Mappings
|
||||
|
||||
- `user_id` → `container_tag` (direct, no transformation)
|
||||
- `session_id` → `custom_id` (in Supermemory API)
|
||||
|
||||
## How It Works
|
||||
|
||||
### Pipeline Position
|
||||
|
||||
```
|
||||
[Transport] → [STT] → [UserContext] → [SupermemoryPipecatService] → [LLM] → [TTS] → [Output]
|
||||
```
|
||||
|
||||
### Frame Processing Flow
|
||||
|
||||
1. **Intercept**: Catches `LLMContextFrame`, `OpenAILLMContextFrame`, `LLMMessagesFrame`
|
||||
2. **Extract**: Gets last user message from context
|
||||
3. **Track**: Stores message in `_conversation_history` (clean, no injections)
|
||||
4. **Retrieve**: Calls `client.profile()` via Supermemory SDK
|
||||
5. **Inject**: Adds formatted memories to context as system message
|
||||
6. **Store**: Sends last user message to Supermemory (background, non-blocking)
|
||||
7. **Push**: Forwards enhanced frame downstream
|
||||
|
||||
### Supermemory SDK Integration
|
||||
|
||||
**Retrieval** - via `supermemory.AsyncSupermemory.profile()`:
|
||||
```python
|
||||
response = await client.profile(
|
||||
container_tag="user-123",
|
||||
q="What's the weather?",
|
||||
threshold=0.1,
|
||||
extra_body={"limit": 10},
|
||||
)
|
||||
# response.profile.static: List[str]
|
||||
# response.profile.dynamic: List[str]
|
||||
# response.search_results.results: List[object]
|
||||
```
|
||||
|
||||
**Storage** - via `supermemory.AsyncSupermemory.memories.add()`:
|
||||
```python
|
||||
await client.memories.add(
|
||||
content="User: What's the weather?",
|
||||
container_tags=["user-123"],
|
||||
custom_id="session-456",
|
||||
metadata={"platform": "pipecat"},
|
||||
)
|
||||
```
|
||||
|
||||
## Memory Modes
|
||||
|
||||
| Mode | Static Profile | Dynamic Profile | Search Results |
|
||||
|------|----------------|-----------------|----------------|
|
||||
| `profile` | Yes | Yes | No |
|
||||
| `query` | No | No | Yes |
|
||||
| `full` | Yes | Yes | Yes |
|
||||
|
||||
## Instance State
|
||||
|
||||
```python
|
||||
self.user_id: str # User identifier
|
||||
self.container_tag: str # Same as user_id
|
||||
self.session_id: Optional[str] # Session grouping
|
||||
self._conversation_history: List[Dict] # Clean message history
|
||||
self._last_query: Optional[str] # Dedup tracking
|
||||
self._supermemory_client # Supermemory SDK client
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
- Memory retrieval failures: Log warning, continue without memories
|
||||
- Memory storage failures: Log error, don't crash pipeline
|
||||
- Frame processing errors: Log error, pass original frame through
|
||||
|
||||
## Sample Usage
|
||||
Place `SupermemoryPipecatService` between context aggregator and LLM in the pipeline:
|
||||
|
||||
```python
|
||||
from supermemory_pipecat import SupermemoryPipecatService
|
||||
|
||||
# Create service
|
||||
memory = SupermemoryPipecatService(
|
||||
api_key=os.getenv("SUPERMEMORY_API_KEY"),
|
||||
user_id="user-123",
|
||||
session_id="conv-456",
|
||||
params=SupermemoryPipecatService.InputParams(
|
||||
mode="full",
|
||||
add_memory="always",
|
||||
),
|
||||
user_id="user-123", # Required: identifies the user
|
||||
session_id="session-456", # Optional: groups conversations
|
||||
)
|
||||
|
||||
# Add to pipeline
|
||||
pipeline = Pipeline([
|
||||
transport.input(),
|
||||
stt,
|
||||
context_aggregator.user(),
|
||||
memory, # ← Intercepts here, retrieves/injects/stores
|
||||
memory, # <- Memory service here
|
||||
llm,
|
||||
tts,
|
||||
transport.output(),
|
||||
|
|
@ -149,98 +36,37 @@ pipeline = Pipeline([
|
|||
])
|
||||
```
|
||||
|
||||
## Full Working Example
|
||||
## Configuration
|
||||
|
||||
```python
|
||||
"""Pipecat + Supermemory Voice Bot"""
|
||||
|
||||
import os
|
||||
from fastapi import FastAPI, WebSocket
|
||||
from pipecat.pipeline.pipeline import Pipeline
|
||||
from pipecat.pipeline.runner import PipelineRunner
|
||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||
from pipecat.services.openai.llm import OpenAILLMService
|
||||
from pipecat.services.openai.tts import OpenAITTSService
|
||||
from pipecat.services.openai.stt import OpenAISTTService
|
||||
from pipecat.transports.websocket.fastapi import (
|
||||
FastAPIWebsocketParams,
|
||||
FastAPIWebsocketTransport,
|
||||
memory = SupermemoryPipecatService(
|
||||
api_key="...", # Or use SUPERMEMORY_API_KEY env var
|
||||
user_id="user-123",
|
||||
session_id="session-456",
|
||||
params=SupermemoryPipecatService.InputParams(
|
||||
search_limit=10, # Max memories to retrieve
|
||||
search_threshold=0.1, # Similarity threshold 0.0-1.0
|
||||
mode="full", # "profile" | "query" | "full"
|
||||
system_prompt="Based on previous conversations:\n\n",
|
||||
),
|
||||
)
|
||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||
from pipecat.serializers.protobuf import ProtobufFrameSerializer
|
||||
|
||||
from supermemory_pipecat import SupermemoryPipecatService
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
SYSTEM_PROMPT = """You are a helpful voice assistant with memory.
|
||||
Keep responses brief. Your output will be converted to audio."""
|
||||
|
||||
@app.websocket("/ws")
|
||||
async def websocket_endpoint(websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
|
||||
transport = FastAPIWebsocketTransport(
|
||||
websocket=websocket,
|
||||
params=FastAPIWebsocketParams(
|
||||
audio_in_enabled=True,
|
||||
audio_out_enabled=True,
|
||||
vad_enabled=True,
|
||||
vad_analyzer=SileroVADAnalyzer(),
|
||||
vad_audio_passthrough=True,
|
||||
serializer=ProtobufFrameSerializer(),
|
||||
),
|
||||
)
|
||||
|
||||
stt = OpenAISTTService(api_key=os.getenv("OPENAI_API_KEY"))
|
||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o-mini")
|
||||
tts = OpenAITTSService(api_key=os.getenv("OPENAI_API_KEY"), voice="alloy")
|
||||
|
||||
# Supermemory integration
|
||||
memory = SupermemoryPipecatService(
|
||||
user_id="test-user",
|
||||
session_id="voice-session",
|
||||
)
|
||||
|
||||
context = OpenAILLMContext([{"role": "system", "content": SYSTEM_PROMPT}])
|
||||
context_aggregator = llm.create_context_aggregator(context)
|
||||
|
||||
pipeline = Pipeline([
|
||||
transport.input(),
|
||||
stt,
|
||||
context_aggregator.user(),
|
||||
memory,
|
||||
llm,
|
||||
tts,
|
||||
transport.output(),
|
||||
context_aggregator.assistant(),
|
||||
])
|
||||
|
||||
task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
|
||||
runner = PipelineRunner(handle_sigint=False)
|
||||
await runner.run(task)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8001)
|
||||
```
|
||||
|
||||
## Key Files Reference
|
||||
## Memory Modes
|
||||
|
||||
- `service.py:46` - `SupermemoryPipecatService` class definition
|
||||
- `service.py:96` - `__init__()` constructor
|
||||
- `service.py:157` - `_retrieve_memories()` API call
|
||||
- `service.py:218` - `_store_message()` storage logic
|
||||
- `service.py:264` - `_enhance_context_with_memories()` injection
|
||||
- `service.py:328` - `process_frame()` main entry point
|
||||
| Mode | Retrieves | Use When |
|
||||
|------|-----------|----------|
|
||||
| `"profile"` | User profile only | Personalization without search |
|
||||
| `"query"` | Search results only | Finding relevant past context |
|
||||
| `"full"` | Profile + search | Complete memory (default) |
|
||||
|
||||
## Differences from Mem0
|
||||
## Environment Variables
|
||||
|
||||
| Aspect | Mem0 | Supermemory |
|
||||
|--------|------|-------------|
|
||||
| Identity | `user_id`, `agent_id`, `run_id` | `user_id` only (= container_tag) |
|
||||
| Retrieval | `memory.search()` | `client.profile()` (static + dynamic + search) |
|
||||
| Storage | Full conversation | Last user message only |
|
||||
| Metadata | `{"platform": "pipecat"}` | `{"platform": "pipecat"}` |
|
||||
| Session | N/A | `session_id` → `custom_id` |
|
||||
- `SUPERMEMORY_API_KEY` - Supermemory API key
|
||||
- `OPENAI_API_KEY` - For OpenAI services (STT/LLM/TTS)
|
||||
|
||||
## Boundaries
|
||||
|
||||
- Always place memory service after `context_aggregator.user()` and before `llm`
|
||||
- Always provide `user_id` - it's required
|
||||
- Never hardcode API keys in code - use environment variables
|
||||
|
|
|
|||
|
|
@ -58,8 +58,6 @@ memory = SupermemoryPipecatService(
|
|||
search_limit=10, # Max memories to retrieve
|
||||
search_threshold=0.1, # Similarity threshold
|
||||
mode="full", # "profile", "query", or "full"
|
||||
add_memory="always", # "always" or "never"
|
||||
add_as_system_message=True,
|
||||
system_prompt="Based on previous conversations, I recall:\n\n",
|
||||
),
|
||||
)
|
||||
|
|
@ -156,19 +154,6 @@ async def websocket_endpoint(websocket: WebSocket):
|
|||
await runner.run(task)
|
||||
```
|
||||
|
||||
## Conversation History
|
||||
|
||||
Access the tracked conversation (without injected memories):
|
||||
|
||||
```python
|
||||
# Get conversation history
|
||||
history = memory.get_conversation_history()
|
||||
# [{"role": "user", "content": "Hello"}, {"role": "user", "content": "What's my name?"}]
|
||||
|
||||
# Clear history
|
||||
memory.clear_conversation_history()
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue