Skip to main content
Your model decides when to search the web or read a page, and your code runs the call with one helper. The SDKs ship the tool definitions and the helper, so you write only the loop.

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 official openai 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 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:
To call OpenAI itself, leave OPENAI_BASE_URL unset.
3

Save the program

Save this program as tool-calling.mts or tool_calling.py:
Each pass of the loop does three things:
  1. Sends the conversation and TOOLS to the model.
  2. If the model answers without a tool call, prints the answer and stops.
  3. Otherwise runs every tool call with run_tool_call and appends each result as a role: "tool" message with the matching tool_call_id.
4

Run it

The program prints the model’s final message. The system prompt asks the model to cite the tool results by number, such as [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:
You see the same numbered block that to_context() renders, one [n] Title (url) header per result followed by the page text:
Example output · results vary
A 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 the to_context() block in the prompt. This program uses the same environment variables as the tool-calling loop:
The program prints the model’s answer, then the sources behind each [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 to name, 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:
The program prints the text of the model’s final message. If a tool call fails, the tool_result block carries is_error: true and the error message.