Skip to main content
Already have an agent: a LangGraph or LangChain graph, a LiteLLM or vLLM gateway, a service of your own on any cloud? You don’t have to rebuild it. Lokutor handles everything around the model (speech recognition, deciding when the caller has finished, barge-in, speech synthesis, phone lines, tool execution) and your code supplies the intelligence. Both ways use the same conversation format, the OpenAI chat messages, so what you build for one works for the other. If you can run a public endpoint, or you need phone calls, use endpoint mode. If your model or tools sit behind a firewall, or you are developing on a laptop, use client mode.

Client mode

Your model runs in the same process that holds the session. Lokutor sends each conversation to your function; you return the reply, all at once or as you produce it. Lokutor starts speaking at the first sentence, and tells you to stop when the caller speaks over the reply.
The handler may return a string, or a generator (async generators in JavaScript) that yields strings. An exception, or a handler that does not answer within 20 seconds, ends that turn with no reply and is reported in the session as an error message. The Python SDK runs each request on its own thread, so a slow model never blocks the microphone.

LangGraph in the same process

Endpoint mode

Point an agent, or one session, at an OpenAI-compatible chat completions endpoint.
Don’t put a secret in browser code. For a browser, set the endpoint on the agent (the key stays on Lokutor’s side) or keep the endpoint behind a gateway that needs no key.

What your endpoint receives

A standard request. Lokutor asks for a stream:
It answers with a server-sent-event stream of chat.completion.chunk (content deltas, optional tool_calls deltas, an optional last chunk with usage, then data: [DONE]). A server that ignores stream and returns one chat.completion JSON body works too, and a strict server that rejects stream_options is retried without it. custom_llm_server.py is a complete minimal endpoint (about 60 lines of FastAPI) to wrap any agent.

Tools

Your agent may use Lokutor’s tools, its own, or both. Tool arguments are checked against the tool’s schema before it runs. A call with missing, blank or mistyped required arguments isn’t executed: you get an error result and can call again or ask the caller. Lokutor’s own ask_expert and background-task tools are not offered when you bring your own model: you are the intelligence.

What your model also receives

Besides the caller’s turns, Lokutor sends a few other chat requests through the same model, so make sure yours treats them as ordinary chat:
  • the opening line, when the agent speaks first and has no fixed first message;
  • a check-in after a long silence, with a note that the caller has said nothing;
  • a summary of a long conversation, to keep it within budget;
  • after the call, your agent’s evaluation and data-collection rules, when you use them (in client mode there is no session left to answer them, so they are skipped).

What is different from the platform’s own model

  • Speculative replies are off. The platform’s model starts a reply during the caller’s pause and discards it when they keep talking, about 0.85 extra requests per turn. Sending those to your service would multiply its load, so they are off unless you set speculative_llm to true.
  • No fallback. If your endpoint or your handler fails or is too slow, that turn gets no spoken reply and the session receives an error message that says why. Lokutor does not answer with its own model in its place. Your endpoint has 20 seconds to start answering.
  • Latency is yours. Time to first audio is your time to the first sentence plus Lokutor’s recognition, turn detection and speech synthesis. Stream, and start with a short sentence: say “Let me check that” before a slow lookup.
  • Billing. The platform minute, recognition and speech are billed as usual. Model tokens are not billed: you pay your own endpoint. See Pricing.
  • Security. Endpoint mode is called from Lokutor’s servers, so an address that is not public is refused: localhost, private ranges, link-local and cloud-metadata addresses, and a hostname that resolves to one, even through a redirect. For local development, use client mode, or a tunnel such as cloudflared tunnel --url http://localhost:8000.

Protocol reference (client mode)

Everything is JSON on the session’s WebSocket (protocol).

Troubleshooting