Tools
Let Olive call your functions, or search your vector stores, in a Responses request.
Define a function tool, run it in your code, and send the result back so Olive can finish its answer. Send tools with POST /responses.
When to use it
- A value must come from your code, not from the model's text. For example, a BMI calculated from weight and height.
- Olive should search your vector store with
file_search.
| Tool type | Who runs it | Status |
|---|---|---|
function | Your code | Available |
file_search | Avey, inside the request | Available |
web_search | Avey | Coming soon. Returns 400 for now. See Web search. |
mcp | None | Not available. Returns 400. |
Every other tool type returns 400.
The SDK samples assume the client from the Quickstart.
Define a function
Describe the function with a name and a JSON schema. Olive never runs it. It asks for a call when it needs the result.
curl https://api.avey.ai/llm/responses \
-H "Authorization: Bearer $AVEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "olive",
"input": "Calculate the BMI for a patient who weighs 82 kg and is 1.75 m tall.",
"tools": [{
"type": "function",
"name": "calculate_bmi",
"description": "Body mass index from weight in kilograms and height in metres.",
"parameters": {
"type": "object",
"properties": {
"weight_kg": { "type": "number" },
"height_m": { "type": "number" }
},
"required": ["weight_kg", "height_m"]
}
}]
}'The Go samples reuse the Quickstart client. Add the imports each sample uses.
Return the result
When Olive wants the function, output holds a function_call item. Its arguments is a JSON string. Run the function, then send the result as a function_call_output item with the same call_id. Chain it to the response that asked for the call, and send tools again.
import json
call = next((item for item in response.output if item.type == "function_call"), None)
if call is not None:
args = json.loads(call.arguments)
result = {"bmi": round(args["weight_kg"] / args["height_m"] ** 2, 1)}
response = client.responses.create(
model="olive",
previous_response_id=response.id,
input=[{
"type": "function_call_output",
"call_id": call.call_id,
"output": json.dumps(result),
}],
tools=tools,
)
print(response.output_text)Olive can ask for more than one call, or for another call after your result. Loop until a response has no function_call item.
Arguments are model output
Olive writes the arguments as it writes any text. They can be malformed JSON, miss a required field, or carry a plausible wrong value. Validate them against your schema and check clinical values for range. A function that changes a record, places an order or sends a message needs a clinician's confirmation, not only Olive's request.
File search
Pass file_search with the IDs of the stores to search. Avey runs the search inside the request, so your code does not answer a call.
response = client.responses.create(
model="olive",
input="What is the recommended first-line antiplatelet therapy?",
tools=[{"type": "file_search", "vector_store_ids": [store.id]}],
)Only stores you own are searched. Citations come back as file_citation annotations on the output text. See Vector stores.
Errors
| Status | Message | Cause |
|---|---|---|
| 400 | tool type "<type>" is not available | A tool type other than function or file_search, such as web_search or code_interpreter. |
| 400 | unknown tool "<label>"; send only the server_label of a tool you have access to | An mcp tool. |
| 400 | 'tools' must be sent once. | The request body has two tools keys. |
Things to know
- Send tools on
POST /responses. - Function tools on Chat Completions are passed to Olive as sent, but they are untested. Use
POST /responsesfor tools.