bendytree
04/24/2025, 1:30 PMmodel-output
is designed to stream just the spoken content.
Is there any way to get the full llm output client-side? Or other ideas on how to get data from the custom-llm back to the client.
// Client-side
const vapi = new Vapi("...");
vapi.start({
...
clientMessages: ['model-output'],
model: { provider: 'custom-llm', ... },
);
vapi.on("message", (message) => {
if (message.type === 'model-output') {
console.log(message); // ?
}
});
// Server-side (Custom LLM)
app.post('/agent/vapi-custom-llm/chat/completions', async (req, res, next) => {
res.write(`data: {"id":"1","object":"chat.completion.chunk","choices":[{"delta":{"role":"assistant","content":""},"index":0}]}\n\n`);
res.write(`data: {"id":"1","object":"chat.completion.chunk","choices":[{"delta":{"content":"Here is the link."},"index":0}]}\n\n`);
// additional url param added here
res.write(`data: {"url":"https://example.com/abc","id":"1","object":"chat.completion.chunk","choices":[{"delta":{},"index":0,"finish_reason":"stop"}]}\n\n`);
res.end(`data: [DONE]\n\n`);
});
Vapi Support Bot
04/24/2025, 1:32 PMclientMessages
with tool calls to send extra data to the client:
javascript
// Client-side
const vapi = new Vapi("...");
vapi.start({
clientMessages: ['model-output', 'tool-calls'],
model: { provider: 'custom-llm', ... },
});
vapi.on("message", (message) => {
if (message.type === 'tool-calls') {
console.log(message.data.url); // Access your extra data
}
});
On your server-side, format the response as a tool call with your extra data in the arguments.
Source:
- [Discord discussion on client-side events](https://discord.com/channels/1211482211119796234/1330803502615625738)Vapi Support Bot
04/24/2025, 3:20 PMbendytree
04/24/2025, 3:57 PMres.write(`data: {"id":"1","object":"chat.completion.chunk","choices":[{"delta":{"role":"assistant","content":""},"index":0}]}\n\n`);
res.write(`data: {"id":"1","object":"chat.completion.chunk","choices":[{"delta":{"content":"Here is the link."},"index":0}]}\n\n`);
res.write(`data: {"id":"1","object":"chat.completion.chunk","choices":[{"delta":{"tool_calls":[{"id":"2134","type":"function","index":0,"function":{"name":"parse_data","arguments":${JSON.stringify(JSON.stringify({url: "foobar"}))}}}]},"index":0}]}\n\n`);
res.write(`data: {"id":"1","object":"chat.completion.chunk","choices":[{"delta":{},"index":0,"finish_reason":"stop"}]}\n\n`);
res.end(`data: [DONE]\n\n`);
If the tool called is registered on the vapi assistant, then the client event tool-call
is triggered. Unfortunately this creates a rapid loop of the agent responding over the top of itself. Even if the LLM returns an empty string it still cuts itself off.
So the only option is to not register the tool call in your assistant, and actually that works much better. So the LLM streams the text and the tool call. Client-side, you will not get a tool call event, but... instead it comes in as an additional model-output
message. So I get these two messages client-side:
model-output {"type":"model-output","output":"Here is the link."}
model-output {"type":"model-output","output":[{"id":"2134","type":"function","function":{"name":"parse_data","arguments":"{\"url\":\"foobar\"}"},"isPrecededByText":true}]}
Vapi
04/28/2025, 12:52 AMbendytree
04/28/2025, 6:29 PMVapi
04/30/2025, 9:03 AM