mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-08-28 05:25:33 +00:00
234 lines
7.9 KiB
Markdown
234 lines
7.9 KiB
Markdown
# Supermemory Cartesia SDK
|
|
|
|
Memory-enhanced voice agents with [Supermemory](https://supermemory.ai) and [Cartesia Line](https://cartesia.ai/agents).
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
pip install supermemory-cartesia
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
```python
|
|
import os
|
|
from line.llm_agent import LlmAgent, LlmConfig
|
|
from line.voice_agent_app import VoiceAgentApp
|
|
from supermemory_cartesia import SupermemoryCartesiaAgent
|
|
|
|
async def get_agent(env, call_request):
|
|
# Extract container_tag from call metadata (typically user ID)
|
|
container_tag = call_request.metadata.get("user_id", "default-user")
|
|
|
|
# Create base LLM agent
|
|
base_agent = LlmAgent(
|
|
model="gemini/gemini-2.5-flash-preview-09-2025",
|
|
api_key=os.getenv("GEMINI_API_KEY"),
|
|
config=LlmConfig(
|
|
system_prompt="You are a helpful voice assistant with memory.",
|
|
introduction="Hello! Great to talk with you again!"
|
|
)
|
|
)
|
|
|
|
# Wrap with Supermemory
|
|
memory_agent = SupermemoryCartesiaAgent(
|
|
agent=base_agent,
|
|
api_key=os.getenv("SUPERMEMORY_API_KEY"),
|
|
container_tag=container_tag,
|
|
custom_id=call_request.call_id,
|
|
)
|
|
|
|
return memory_agent
|
|
|
|
# Create voice agent app
|
|
app = VoiceAgentApp(get_agent=get_agent)
|
|
|
|
if __name__ == "__main__":
|
|
app.run(host="0.0.0.0", port=8000)
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Parameters
|
|
|
|
| Parameter | Type | Required | Description |
|
|
| --------------- | ------------ | -------- | ------------------------------------------------------------------ |
|
|
| `agent` | LlmAgent | **Yes** | The Cartesia Line agent to wrap |
|
|
| `container_tag` | str | **Yes** | Primary container tag for memory scoping (e.g., user ID) |
|
|
| `custom_id` | str | **Yes** | Custom ID for grouping conversation messages into a single document|
|
|
| `add_memory` | Literal | No | Memory persistence mode: "always" (default) or "never" |
|
|
| `container_tags`| List[str] | No | Additional container tags for organization (e.g., ["org", "prod"]) |
|
|
| `api_key` | str | No | Supermemory API key (or set `SUPERMEMORY_API_KEY` env var) |
|
|
| `config` | MemoryConfig | No | Advanced configuration |
|
|
| `base_url` | str | No | Custom API endpoint |
|
|
|
|
### Advanced Configuration
|
|
|
|
```python
|
|
from supermemory_cartesia import SupermemoryCartesiaAgent
|
|
|
|
memory_agent = SupermemoryCartesiaAgent(
|
|
agent=base_agent,
|
|
container_tag="user-123",
|
|
custom_id="conversation-456",
|
|
add_memory="always", # "always" (default) or "never"
|
|
container_tags=["org-acme", "prod"], # Optional: additional tags
|
|
config=SupermemoryCartesiaAgent.MemoryConfig(
|
|
search_limit=10, # Max memories to retrieve
|
|
search_threshold=0.1, # Similarity threshold
|
|
mode="full", # "profile", "query", or "full"
|
|
system_prompt="Based on previous conversations, I recall:\n\n",
|
|
),
|
|
)
|
|
|
|
# Read-only mode - retrieve memories but don't save new ones
|
|
read_only_agent = SupermemoryCartesiaAgent(
|
|
agent=base_agent,
|
|
container_tag="user-123",
|
|
custom_id="conversation-456",
|
|
add_memory="never", # Only retrieve, don't save
|
|
)
|
|
```
|
|
|
|
### Memory Modes
|
|
|
|
| Mode | Static Profile | Dynamic Profile | Search Results |
|
|
| ----------- | -------------- | --------------- | -------------- |
|
|
| `"profile"` | Yes | Yes | No |
|
|
| `"query"` | No | No | Yes |
|
|
| `"full"` | Yes | Yes | Yes |
|
|
|
|
## How It Works
|
|
|
|
1. **Intercepts events** - Listens for `UserTurnEnded` events from Cartesia Line
|
|
2. **Retrieves memories** - Queries Supermemory `/v4/profile` API with user's message
|
|
3. **Enriches context** - Passes memories as non-persistent context for the current turn
|
|
4. **Stores messages** - Sends conversation to Supermemory (background, non-blocking)
|
|
5. **Passes to agent** - Forwards enriched event to wrapped LlmAgent
|
|
|
|
### What Gets Stored
|
|
|
|
User and assistant messages are sent to Supermemory:
|
|
|
|
```json
|
|
{
|
|
"content": "User: What's the weather?\nAssistant: It's sunny today!",
|
|
"container_tags": ["user-123", "org-acme", "prod"],
|
|
"metadata": { "platform": "cartesia" }
|
|
}
|
|
```
|
|
|
|
## Architecture
|
|
|
|
Cartesia Line uses an event-driven architecture:
|
|
|
|
```
|
|
User Speaks (Audio)
|
|
↓
|
|
[Ink STT] → Automatic speech recognition
|
|
↓
|
|
UserTurnEnded Event {content: "user message", history: [...]}
|
|
↓
|
|
┌──────────────────────────────────────────────┐
|
|
│ SUPERMEMORY CARTESIA AGENT (Wrapper) │
|
|
│ │
|
|
│ process(env, event): │
|
|
│ 1. Intercept UserTurnEnded │
|
|
│ 2. Extract user message │
|
|
│ 3. Query Supermemory API │
|
|
│ 4. Add memories as per-turn context │
|
|
│ 5. Pass to wrapped LlmAgent │
|
|
│ 6. Store conversation (async background) │
|
|
└──────────────────────────────────────────────┘
|
|
↓
|
|
AgentSendText Event {text: "response"}
|
|
↓
|
|
[Sonic TTS] → Ultra-fast speech synthesis
|
|
↓
|
|
Audio Output
|
|
```
|
|
|
|
## Comparison with Pipecat SDK
|
|
|
|
| Aspect | Pipecat | Cartesia Line |
|
|
| ----------------------- | ------------------------------ | ---------------------------- |
|
|
| **Integration Pattern** | Extends `FrameProcessor` | Wrapper around `LlmAgent` |
|
|
| **Event Handling** | `process_frame()` method | `process()` method |
|
|
| **Events** | `LLMContextFrame`, `LLMMessagesFrame` | `UserTurnEnded`, `CallStarted` |
|
|
| **Context Object** | `LLMContext.get_messages()` | `event.history` |
|
|
| **Memory Injection** | Modify `context.add_message()` | Pass per-turn `context` |
|
|
|
|
## Full Example with Tools
|
|
|
|
```python
|
|
import os
|
|
from line.llm_agent import LlmAgent, LlmConfig
|
|
from line.tools import LoopbackTool
|
|
from line.voice_agent_app import VoiceAgentApp
|
|
from supermemory_cartesia import SupermemoryCartesiaAgent
|
|
|
|
# Define custom tools
|
|
async def get_weather(location: str) -> str:
|
|
return f"The weather in {location} is sunny, 72°F"
|
|
|
|
weather_tool = LoopbackTool(
|
|
name="get_weather",
|
|
description="Get current weather for a location",
|
|
function=get_weather
|
|
)
|
|
|
|
async def get_agent(env, call_request):
|
|
container_tag = call_request.metadata.get("user_id", "default-user")
|
|
org_id = call_request.metadata.get("org_id")
|
|
|
|
# Create LLM agent with tools
|
|
base_agent = LlmAgent(
|
|
model="gemini/gemini-2.5-flash-preview-09-2025",
|
|
api_key=os.getenv("GEMINI_API_KEY"),
|
|
tools=[weather_tool],
|
|
config=LlmConfig(
|
|
system_prompt="You are a personal assistant with memory and tools.",
|
|
introduction="Hi! How can I help you today?"
|
|
)
|
|
)
|
|
|
|
# Wrap with Supermemory
|
|
memory_agent = SupermemoryCartesiaAgent(
|
|
agent=base_agent,
|
|
api_key=os.getenv("SUPERMEMORY_API_KEY"),
|
|
container_tag=container_tag,
|
|
custom_id=call_request.call_id,
|
|
container_tags=[org_id] if org_id else None,
|
|
config=SupermemoryCartesiaAgent.MemoryConfig(
|
|
mode="full",
|
|
search_limit=15,
|
|
search_threshold=0.15,
|
|
)
|
|
)
|
|
|
|
return memory_agent
|
|
|
|
app = VoiceAgentApp(get_agent=get_agent)
|
|
```
|
|
|
|
## Development
|
|
|
|
```bash
|
|
# Clone repository
|
|
git clone https://github.com/supermemoryai/supermemory
|
|
cd supermemory/packages/cartesia-sdk-python
|
|
|
|
# Install in development mode
|
|
pip install -e ".[dev]"
|
|
|
|
# Run tests
|
|
pytest
|
|
|
|
# Format code
|
|
black .
|
|
isort .
|
|
```
|
|
|
|
## License
|
|
|
|
MIT
|