What you get
TOOLS is a list of two tool definitions in OpenAI function-calling format. Pass it as tools to any chat completions client that accepts that format.
run_tool_call runs whichever of the two tools the model picked and returns the text to send back. Each SDK names the pieces in its own style:
The programs on this page are in TypeScript and Python. In Go,
keenable.RunToolCall(ctx, client, name, arguments) returns (string, error) and takes the same tool names and arguments.
Run the tool-calling loop
The program below asks a question, lets the model call the Keenable tools as often as it needs, and prints the final answer. It uses the officialopenai client, which works with any provider that has an OpenAI-compatible chat completions endpoint. Keenable needs no API key here.
1
Install the packages
Install the Keenable SDK and the
openai client:2
Point the client at your LLM provider
The To call OpenAI itself, leave
openai client reads the key and the base URL from the environment. The program reads the model name from OPENAI_MODEL. Use your provider’s values:OPENAI_BASE_URL unset.3
Save the program
Save this program as Each pass of the loop does three things:
tool-calling.mts or tool_calling.py:- Sends the conversation and
TOOLSto the model. - If the model answers without a tool call, prints the answer and stops.
- Otherwise runs every tool call with
run_tool_calland appends each result as arole: "tool"message with the matchingtool_call_id.
4
Run it
[1].What a tool call returns
run_tool_call takes the client, the tool name and the arguments, and returns one string. The arguments can be the raw JSON string from the model or an already-decoded object.
This program runs a search tool call by hand, with no model and no key:
to_context() renders, one [n] Title (url) header per result followed by the page text:
Example output · results vary
keenable_fetch call returns the same shape with a single [1] Title (url) entry, so the model cites a fetched page the way it cites a search result. Both blocks are limited to 12,000 characters. See Context size.
For keenable_search, the helper passes on only query, site and published_after. It drops any other argument the model sends.
Errors inside a tool call
run_tool_call raises a KeenableError instead of returning an error string. The loop above catches it and sends the message back as the tool result, so the model can retry with other arguments or another URL.
In the first three cases the SDK sends no request. If the API returns
429, see Rate limits. An API key removes the keyless hourly cap.
Ground an answer without tool calling
If your model has no tool calling, or you want one search per question, search first and put theto_context() block in the prompt. This program uses the same environment variables as the tool-calling loop:
[n] marker:
Example output · results vary
cited() returns the results that to_context() kept, in the same order, so the list always matches the numbers the model saw. To narrow the search, pass the Search parameters to search.
Use the tools with Anthropic’s Messages API
Anthropic’s Messages API takes the same JSON schema under different field names. Map each definition toname, description and input_schema, then run each tool_use block with run_tool_call and send the result back as a tool_result block.
This program runs the same loop with the anthropic SDK. Install it with npm install keenable @anthropic-ai/sdk or pip install keenable anthropic, and set ANTHROPIC_API_KEY:
tool_result block carries is_error: true and the error message.