Skip to content

Bring your own brain

2wai doesn’t require you to use any particular language model, or a language model at all. The API’s only job on the “what does the avatar say” side is: given text, speak it. Everything upstream of that (understanding the user, deciding on a reply, retrieving context, calling tools) is entirely yours.

async function reply(sessionId, userMessage) {
// 1. Decide what to say. Use whatever you want here: this example uses
// the OpenAI SDK, but any LLM, retrieval pipeline, or static logic works.
const completion = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: userMessage }],
});
const text = completion.choices[0].message.content;
// 2. Speak it.
await fetch(`https://api.2wai.ai/v1/sessions/${sessionId}/speak`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TWAI_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text }),
});
}

That’s the entire integration surface. Swap step 1 for whatever you already run in production (a RAG pipeline, a fine-tuned model, a support macro system) without changing anything about how you talk to 2wai.

If your LLM call supports streaming tokens, don’t wait for the full reply before calling /speak. Flush at sentence boundaries instead, so the avatar starts talking while the rest of the reply is still being generated:

async function replyStreaming(sessionId, userMessage) {
const stream = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: userMessage }],
stream: true,
});
let buffer = '';
for await (const chunk of stream) {
buffer += chunk.choices[0]?.delta?.content ?? '';
// Flush on sentence-ending punctuation so /speak calls stay small and
// the avatar starts responding sooner.
const match = buffer.match(/^(.*?[.!?])\s+/);
if (match) {
const sentence = match[1];
buffer = buffer.slice(match[0].length);
await speak(sessionId, sentence);
}
}
if (buffer.trim()) {
await speak(sessionId, buffer.trim());
}
}
async function speak(sessionId, text) {
await fetch(`https://api.2wai.ai/v1/sessions/${sessionId}/speak`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TWAI_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text }),
});
}

If your product lets users interrupt the avatar mid-reply (barge-in), call POST /v1/sessions/{id}/interrupt as soon as you detect new user input, before queuing the new reply:

async function interrupt(sessionId) {
await fetch(`https://api.2wai.ai/v1/sessions/${sessionId}/interrupt`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.TWAI_SECRET_KEY}` },
});
}