POST /api/v1/voice-engine/llm/v1/chat/completions. You describe your functions in the tools array; when the model decides a call is needed it answers with finish_reason: "tool_calls" and a message.tool_calls array instead of text. You execute the function yourself, then send the result back in a role: "tool" message for the model’s final answer.
The tools contract
In the response, each
tool_calls[] entry has an id, "type": "function", and a function object whose arguments value is a JSON string (not an object) — parse it before use.
1. Define tools and let the model call one
content: null, the arguments JSON string, and finish_reason: "tool_calls":
2. Force a specific tool
Use atool_choice object when the model must call one particular function (for example, an extraction step that always ends in a structured write):
3. Return the tool result and get the final answer
Run the function yourself, then append both the assistant’s tool-call message and arole: "tool" message (with tool_call_id matching the call’s id) and send the whole conversation back:
finish_reason: "stop"), e.g. “Yes — order 88412 is out for delivery and should arrive around 5:30 pm today.”
Keep sending the
tools array on follow-up turns. The API is stateless — every request must carry the full conversation and the full tool definitions.Streaming tool calls
Withstream: true, tool calls arrive incrementally in delta.tool_calls. Each fragment carries an index identifying which tool call it belongs to; the call id, type, and function.name arrive on the first fragment, and function.arguments arrives as string fragments you concatenate per index:
tool_calls[].index, assign id/type/name when they appear, and append every arguments fragment in order. When finish_reason: "tool_calls" arrives, each accumulated arguments string is a complete JSON document. Official OpenAI SDKs do this accumulation for you.
