"""Minimal LiveKit Agents voice worker using Speko's OpenAI-compatible API."""from __future__ import annotations
import os
from dotenv import load_dotenv
from livekit import agents
from livekit.agents import Agent, AgentServer, AgentSession, JobContext
from livekit.plugins import openai
from openai import AsyncOpenAI
load_dotenv()
SPEKO_API_KEY = os.getenv("SPEKO_API_KEY")
SPEKO_BASE_URL = "https://api.speko.ai/v1"
SPEKO_OBJECTIVE = os.getenv("SPEKO_OBJECTIVE", "balanced")
SPEKO_LANGUAGE = os.getenv("SPEKO_LANGUAGE", "en")
ifnot SPEKO_API_KEY:
raise RuntimeError("SPEKO_API_KEY is required")
defspeko_client() -> AsyncOpenAI:
"""Build one OpenAI-compatible client shared by all three stages."""returnAsyncOpenAI(
api_key=SPEKO_API_KEY,
base_url=SPEKO_BASE_URL,
default_headers={
"X-Speko-Objective": SPEKO_OBJECTIVE,
"X-Speko-Language": SPEKO_LANGUAGE,
},
)
server = AgentServer()
@server.rtc_session()
asyncdefentrypoint(ctx: JobContext) -> None:
client = speko_client()
session = AgentSession(
# Segmented STT is used here so the example works with Speko's# transcription route instead of opening a speech-to-speech socket.
stt=openai.STT(
client=client,
model="auto",
use_realtime=False,
language=SPEKO_LANGUAGE,
),
llm=openai.LLM(
client=client,
model="auto",
),
# The Python LiveKit OpenAI TTS transport expects the raw PCM route.# Speko still selects the underlying provider when model is tts-1.
tts=openai.TTS(
client=client,
model="tts-1",
voice="alloy",
response_format="pcm",
),
)
await session.start(
Agent(
instructions=(
"You are a helpful voice assistant. ""Keep replies concise and conversational."
)
),
room=ctx.room,
)
await ctx.connect()
await session.generate_reply(
instructions="Greet the caller in one short sentence and offer help."
)
if __name__ == "__main__":
agents.cli.run_app(server)
import { fileURLToPath } from'node:url';
import { type JobContext, ServerOptions, cli, defineAgent, voice } from'@livekit/agents';
import * as openai from'@livekit/agents-plugin-openai';
const apiKey = process.env.SPEKO_API_KEY!;
const baseURL = 'https://api.speko.ai/v1';
exportdefaultdefineAgent({
entry: async (ctx: JobContext) => {
// No vad: 1.6 loads a bundled Silero when the option is absent, and// @livekit/local-inference ships with the framework.const session = new voice.AgentSession({
stt: new openai.STT({ apiKey, baseURL, model: 'auto', language: 'en', useRealtime: false }),
llm: new openai.LLM({ apiKey, baseURL, model: 'auto' }),
// The Node plugin always posts response_format: "pcm" and never parses// SSE, so 'auto' is right here where the Python file needs 'tts-1'.
tts: new openai.TTS({ apiKey, baseURL, model: 'auto', voice: 'alloy' }),
});
await session.start({
agent: new voice.Agent({ instructions: 'Be concise and helpful.' }),
room: ctx.room,
});
await ctx.connect();
},
});
cli.runApp(newServerOptions({ agent: fileURLToPath(import.meta.url) }));
Run
Language
Command
Needs
Python
uv run python agent.py console
Save the Python example as agent.py. Talks in the terminal; no LiveKit credentials.