Speko Docs

createSpekoComponents

Build a { stt, llm, tts } bundle ready for voice.AgentSession.

createSpekoComponents is the one-call wiring helper for voice.AgentSession. It constructs SpekoSTT, SpekoLLM, SpekoTTS from a single options object and wraps STT and TTS with LiveKit's StreamAdapter so Speko's streaming REST proxy can drive a streaming session.

import { createSpekoComponents } from '@spekoai/adapter-livekit';

const { stt, llm, tts } = createSpekoComponents({
  speko,
  vad,
  intent: { language: 'en-US', optimizeFor: 'balanced' },
});

const session = new voice.AgentSession({ vad, stt, llm, tts });

Signature

function createSpekoComponents(
  options: CreateSpekoComponentsOptions,
): SpekoComponents;

CreateSpekoComponentsOptions

FieldTypeRequiredDescription
spekoSpeko✅Initialised @spekoai/sdk client.
intentIntent✅Routing hint shared by STT, LLM, and TTS.
vadVAD✅VAD instance used by the stt.StreamAdapter. Typically await silero.VAD.load().
voicestring?Voice id passed to SpekoTTS (maps to the Speko proxy's voice param).
constraintsPipelineConstraints?Allow-list constraints applied to all three modalities.
sentenceTokenizertokenize.SentenceTokenizer?Tokenizer for chunking LLM output before TTS. Defaults to tokenize.basic.SentenceTokenizer.
llm{ temperature?, maxTokens? }?Tuning forwarded to /v1/complete.
ttsOptions{ sampleRate?, speed? }?Output sample rate and speech speed forwarded to SpekoTTS.
agentIdstring?Enables the registered-tools loader. When set, the adapter calls speko.agents.tools.listChatTools(agentId) once per session — using the speko client you pass for auth and base URL — and merges the result with LiveKit's runtime ToolContext. Registered tools win on name collision. Omit to keep runtime-only behavior.
apiBaseUrlstring?Deprecated and ignored — the loader reads the base URL from the speko client. Safe to omit.
apiKeystring?Deprecated and ignored — the loader reads the API key from the speko client. Safe to omit.
onRegisteredToolsError(err: Error) => void?Called once if the registered-tools fetch fails. Voice session keeps running with runtime-only tools — this is a soft degradation, not a crash.

Registered tools

When agentId is set, createSpekoComponents constructs a RegisteredToolsLoader for the underlying SpekoLLM. The loader lazily calls speko.agents.tools.listChatTools(agentId) on the first chat() of each session — reusing the Speko client you pass for auth and base URL — and caches the result for the LLM's lifetime. Voice sessions live for seconds-to-minutes and chat() is called many times — re-fetching every turn would be wasteful. (apiBaseUrl/apiKey are deprecated and ignored; the speko client carries both.)

On collision with a runtime tool of the same name, the registered tool wins (it's the customer's authoritative declaration). Fetch failures are non-fatal — the loader returns undefined and the agent continues with runtime tools only, calling onRegisteredToolsError once.

listChatTools returns every source kind — inline, webhook, builtin, and integration — already in the ChatTool[] shape /v1/complete accepts.

See the tool calling guide for the full picture.

Returns — SpekoComponents

interface SpekoComponents {
  stt: stt.StreamAdapter;   // wraps SpekoSTT + vad
  llm: SpekoLLM;            // used directly
  tts: tts.StreamAdapter;   // wraps SpekoTTS + sentenceTokenizer
}

Drop the returned object straight into a voice.AgentSession.

Custom sentence tokenizer

import { tokenize } from '@livekit/agents';

const { stt, llm, tts } = createSpekoComponents({
  speko,
  vad,
  intent,
  sentenceTokenizer: new tokenize.basic.SentenceTokenizer({ minSentenceLength: 20 }),
});

Use a longer minimum sentence length if you want fewer, longer TTS calls at the cost of latency before the first audio chunk.

Constraints shared across modalities

createSpekoComponents({
  speko,
  vad,
  intent: { language: 'en' },
  constraints: {
    allowedProviders: {
      stt: ['deepgram'],
      llm: ['anthropic'],
      tts: ['cartesia'],
    },
  },
});

Every underlying call (/v1/transcribe, /v1/complete, /v1/synthesize) receives the same constraints object.

Opting out — use classes directly

If you need finer control, construct the classes yourself. createSpekoComponents is a convenience wrapper; nothing stops you from building the pipeline manually.

import { SpekoSTT, SpekoLLM, SpekoTTS } from '@spekoai/adapter-livekit';
import { stt, tts, tokenize } from '@livekit/agents';

const spekoSTT = new SpekoSTT({ speko, intent });
const wrappedSTT = new stt.StreamAdapter(spekoSTT, vad);

const spekoLLM = new SpekoLLM({ speko, intent, temperature: 0.7 });

const spekoTTS = new SpekoTTS({ speko, intent, voice: 'sonic-english' });
const wrappedTTS = new tts.StreamAdapter(spekoTTS, new tokenize.basic.SentenceTokenizer());

On this page