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
spekoSpekoInitialised @spekoai/sdk client.
intentIntentRouting hint shared by STT, LLM, and TTS.
vadVADVAD 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