Skip to content

Voice & realtime

Gradium

Building a Gradium voice agent with long-term memory using LiveKit Agents and SurrealDB Agent Memory.

Gradium provides streaming speech-to-text and text-to-speech models for realtime voice agents. Gradium plugs into a LiveKit Agents pipeline as the STT and TTS stages through the official livekit-plugins-gradium plugin, and SurrealDB Agent Memory hooks into the same pipeline's turn lifecycle: recall before the model speaks, store after. Use the Python SDK (surrealdb); there is no dedicated Gradium adapter.

Note

This is an integration guide. The code shows where SurrealDB Agent Memory fits in a LiveKit agent's turn lifecycle with Gradium speech models; adapt the hook names to your installed livekit-agents version.

pip install "livekit-agents[gradium,openai]"
pip install --pre surrealdb

Set the connection details. Create the Gradium API key in the Gradium Studio console:

export GRADIUM_API_KEY="gd_..."
export AGENT_MEMORY_ENDPOINT="https://memory.example.com"
export AGENT_MEMORY_CONTEXT="acme-prod"
export AGENT_MEMORY_API_KEY="sk-mem-..."

LiveKit calls on_user_turn_completed once Gradium has transcribed the caller's speech, before the LLM runs. Recall relevant memory there and add it to the turn context; store the exchange when the turn finishes. Gradium's STT performs semantic turn detection itself, so the session needs no separate VAD plugin:

import os
from livekit import agents
from livekit.agents import Agent, AgentServer, AgentSession, ChatContext, ChatMessage
from livekit.plugins import gradium, openai
from surrealdb import AsyncAgentMemory

class MemoryAgent(Agent):
    def __init__(self, memory: AsyncAgentMemory, scope: list[str]):
        super().__init__(instructions="You are a helpful voice assistant.")
        self._memory = memory
        self._scope = scope

    async def on_user_turn_completed(self, turn_ctx: ChatContext, new_message: ChatMessage):
        # Recall relevant memory and inject it as context for this turn.
        block = await self._memory.query_context(
            new_message.text_content, k=8, lens=self._scope,
        )
        if block:
            turn_ctx.add_message(role="system", content=f"## Memory\n{block}")

        # Store the caller's turn for future recall (non-blocking).
        await self._memory.remember(new_message.text_content, scopes=self._scope)


server = AgentServer()

@server.rtc_session()
async def entrypoint(ctx: agents.JobContext):
    memory = AsyncAgentMemory(
        endpoint=os.environ["AGENT_MEMORY_ENDPOINT"],
        context=os.environ["AGENT_MEMORY_CONTEXT"],
        api_key=os.environ["AGENT_MEMORY_API_KEY"],
    )

    session = AgentSession(
        stt=gradium.STT(model_name="default", language="en"),
        llm=openai.LLM(model="gpt-4o"),
        tts=gradium.TTS(model_name="default", voice_id="4SZHfMpw-p46Ywgs"),
    )

    await session.start(
        agent=MemoryAgent(memory, scope=["org/acme/user/alice"]),
        room=ctx.room,
    )

if __name__ == "__main__":
    agents.cli.run_app(server)

Pass voice_id explicitly; the plugin's default voice has changed between releases. Gradium's flagship voices cover English, French, Spanish, Portuguese, and German, and gradium.STT accepts the same five languages through its language option.

Bind a scope to the caller's identity so each person's memory stays isolated. It is a slash path or an array of paths, for example ["org/acme/user/alice"]. Derive it from the LiveKit participant identity when the room connects. Register paths with agent-memory scopes create before first use.

Voice turns are latency-sensitive. Gradium's streaming models and semantic turn detection keep the speech stages fast, which leaves the recall call as the main added latency in the loop. Keep recall to a single query_context call with a modest k, and let the write to remember run without blocking the response. For heavier synthesis, run reflect or consolidate between calls rather than inside a turn.

Was this page helpful?