supermemory/packages/cartesia-sdk-python
abhay-codes07 c3295ab3b9
fix(cartesia): run the wrapped agent exactly once per event
process() wrapped both the memory enrichment and the agent streaming
loop in a single try/except whose fallback re-invoked
self.agent.process(). The fallback was meant to keep the agent running
when memory enrichment failed, but because the agent's own iteration
lived inside the same try, an agent error raised mid-stream, after
chunks had already been yielded to the caller, re-ran the entire agent
from scratch: the caller received a second full response concatenated
onto the partial one, plus a duplicate billable LLM call.

Scope the guard to the memory-enrichment/injection/storage block only,
and run the agent iteration exactly once after it. Enrichment failures
are logged and absorbed as before; agent errors now propagate to the
caller instead of triggering a duplicate run.

Regression tests stream from a fake inner agent that dies mid-stream:
each chunk is delivered exactly once, the agent's run counter stays at
1, and the error propagates (both tests fail against the previous
implementation with run_count == 2). A third test pins the intended
behaviour that enrichment failures still let the agent run once.
2026-09-02 04:47:01 +05:30
..
src/supermemory_cartesia fix(cartesia): run the wrapped agent exactly once per event 2026-09-02 04:47:01 +05:30
tests fix(cartesia): run the wrapped agent exactly once per event 2026-09-02 04:47:01 +05:30
pyproject.toml feat(python-sdks): SDK-level cross-source memory deduplication (#1532) 2026-09-01 06:10:36 +00:00
README.md fix(python-sdks): v4 API migration for integration packages (#1434) 2026-09-01 06:10:36 +00:00
requirements.txt fix(python-sdks): v4 API migration for integration packages (#1434) 2026-09-01 06:10:36 +00:00

Supermemory Cartesia SDK

Memory-enhanced voice agents with Supermemory and Cartesia Line.

Installation

pip install supermemory-cartesia

Quick Start

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

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:

{
  "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

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

# 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