Streams
StreamConnector is the SDK primitive for building a Bridge.
It connects two WebSockets, Teler’s call audio stream and your AI provider’s
WebSocket, and relays messages between them through pluggable handlers.
Build a Stream Call Flow
Section titled “Build a Stream Call Flow”Your flow_url endpoint returns a Stream flow when it wants Teler to open a
WebSocket. Use CallFlow.stream() to build the JSON.
| Parameter | SDK | Default | Notes |
|---|---|---|---|
ws_url / first arg | both | none | wss:// URL (TLS only). |
chunk_size / chunkSize | both | 400 | Audio chunk size. The SDK does not validate the value; the media pipeline expects a multiple of 20. |
record | both | true | Whether to record the streamed leg. |
sampleRate | both | "8k" | "8k" or "16k". |
from teler import CallFlow
flow = CallFlow.stream( ws_url="wss://your-domain.com/media-stream", chunk_size=500, sample_rate="8k", record=True,)import { CallFlow } from "@frejun/teler";
const flow = CallFlow.stream( "wss://your-domain.com/media-stream", { chunkSize: 500, sampleRate: "16k" },);StreamConnector
Section titled “StreamConnector”StreamConnector runs two coroutines/promises concurrently: one reading
the call WebSocket, one reading the remote WebSocket. Each handler decides
what to do with every message: relay it, drop it, or stop the stream.
The constructors take the same concepts but with significant differences:
- Python uses keyword arguments (e.g.,
remote_url=,stream_type=) - Node uses positional arguments with different order:
remoteUrlis first, followed bystreamType, handlers,headers, and optional timeout configuration. Node also requires handlers explicitly (Python defaults to relay-all) and supports custom headers and connection timeout.
| Purpose | Python (keyword) | Node (positional order) | Notes |
|---|---|---|---|
| Remote WebSocket URL | remote_url | 1st: remoteUrl | Required. Empty/unparseable raises BadParametersException. |
| Teler → remote handler | call_stream_handler | 2nd: callStreamHandler | Python defaults to relay-all; Node requires it. |
| Remote → Teler handler | remote_stream_handler | 3rd: remoteStreamHandler | Python defaults to relay-all; Node requires it. |
| Stream direction | stream_type | 4th: streamType | Defaults to BIDIRECTIONAL. UNIDIRECTIONAL raises NotImplementedException. |
| Remote WS headers | — | 5th: headers | Node only. Defaults to {}. Custom headers for the remote WebSocket connection. |
| Connection timeout | — | Node only (via constructor config) | Node supports connectTimeoutMs (milliseconds). Controls how long to wait for the remote WebSocket to connect before timing out. Python has no timeout configuration. |
from teler import StreamConnector, StreamOpfrom teler.streams import StreamType
connector = StreamConnector( stream_type=StreamType.BIDIRECTIONAL, remote_url="wss://your-ai-provider.example.com", call_stream_handler=..., remote_stream_handler=...,)import { StreamConnector, StreamOP, StreamType } from "@frejun/teler";
const connector = new StreamConnector( "wss://your-ai-provider.example.com", StreamType.BIDIRECTIONAL, callStreamHandler, remoteStreamHandler, {});const connector = client.streamConnector.create( remoteUrl, callStreamHandler, remoteStreamHandler, streamType, // optional, defaults to StreamType.BIDIRECTIONAL headers, // optional connectTimeoutMs // optional, defaults to 10000ms);StreamOp
Section titled “StreamOp”Every handler returns (data, StreamOp). The op tells the connector what
to do with data. The enum is named StreamOp in Python and StreamOP in
Node.
| Op | Effect |
|---|---|
RELAY | Forward to the other WebSocket. |
PASS | Not forwarded. (Effectively drops the message; only RELAY and STOP are acted on.) |
STOP | Close the WebSocket(s) cleanly and stop the bridge. |
# Relay everything (default)async def relay(message: str): return (message, StreamOp.RELAY)// Relay everything (default)const relay = async (payload) => [payload, StreamOP.RELAY];Handler signature
Section titled “Handler signature”Handlers are async. Python takes a raw JSON string; Node takes a structured
StreamData object. Both must return a 2-tuple of (data, StreamOp).
Returning the wrong shape raises BadParametersException (Python) or TelerException
(Node). See Errors for the full SDK exception mapping.
from typing import Tuplefrom teler import StreamOp
async def my_handler(message: str) -> Tuple[str, StreamOp]: # `message` is the raw JSON from the WebSocket. return (message, StreamOp.RELAY)import { StreamOP } from "@frejun/teler";
const myHandler = async (payload) => { // `payload` is a StreamData value (string | Buffer | Uint8Array | ...). return [payload, StreamOP.RELAY];};Run the bridge
Section titled “Run the bridge”The connector races both directions and exits when either side completes:
caller hangs up, provider closes, or a handler returns STOP.
# Inside your /media-stream WebSocket handlerawait connector.bridge_stream(call_ws)await connector.bridgeStream(callWs);Minimal echo Bridge
Section titled “Minimal echo Bridge”Drop-in starting point for any recipe. Single column here, full file shown.
import asynciofrom fastapi import FastAPI, WebSocketfrom teler import StreamConnector, StreamOp, CallFlow
app = FastAPI()
@app.post("/flow")async def flow(): return CallFlow.stream( ws_url="wss://your-domain.com/media-stream", chunk_size=400, record=True, )
async def relay(message: str): return (message, StreamOp.RELAY)
@app.websocket("/media-stream")async def media_stream(call_ws: WebSocket): await call_ws.accept() connector = StreamConnector( remote_url="wss://your-ai-provider.example.com", call_stream_handler=relay, remote_stream_handler=relay, ) await connector.bridge_stream(call_ws)import express from "express";import { WebSocketServer } from "ws";import { Client, CallFlow, StreamConnector, StreamOP, StreamType } from "@frejun/teler";
const app = express();
app.post("/flow", (_req, res) => { res.json(CallFlow.stream("wss://your-domain.com/media-stream", { chunkSize: 400, sampleRate: "16k", }));});
const server = app.listen(3000);const wss = new WebSocketServer({ server, path: "/media-stream" });
const relay = async (payload) => [payload, StreamOP.RELAY];
wss.on("connection", async (callWs) => { const connector = new StreamConnector( "wss://your-ai-provider.example.com", StreamType.BIDIRECTIONAL, relay, relay, { x-api-key: "API_KEY" } ); await connector.bridgeStream(callWs);});