Python API¶
The client and the integrations are the stable Python surface. Everything else is reached through the CLI.
callbus.client.CallBusClient ¶
catalog ¶
catalog(server=None)
{count, catalog, tools}: each tool's id (name), api_name, description, input
schema, annotations and, for OpenAPI operations, the HTTP method.
search ¶
search(
query,
*,
k=None,
instruction=None,
full_schemas=False,
session=None,
)
{search_id, mode, took_ms, rule, tools}, with confidence when the server has a
calibration and note when the agent should know something about the list.
rank ¶
rank(query, tools, *, instruction=None, session=None)
Score tools the caller has (MCP records: name, description, inputSchema,
optional server) for query; -> [{index, name, server?, score}], best first,
index being the tool's position in tools. Sent in chunks that stay under the
server's limits (RANK_CHUNK tools, RANK_BYTES of JSON).
call ¶
call(name, arguments=None, *, search_id=None, session=None)
{name, call_id, outcome, isError, http_status, content, structuredContent?}; a tool
that fails is a result with isError, not an exception.
feedback ¶
feedback(
name,
outcome,
*,
search_id=None,
took_ms=None,
session=None,
)
Report a call the client ran itself (outcome: ok or tool_error), so the server's
usage log, and with it callbus learn, sees it; -> {name, call_id}.
callbus.client.AsyncCallBusClient ¶
CallBusClient's API as coroutines: each method runs the standard-library client in a
worker thread (asyncio.to_thread, which carries the caller's context). Takes the same
arguments, or client= an existing one. Cancelling a call drops its answer; the request
was sent, so the tool may still run.
feedback
async
¶
feedback(name, outcome, **kwargs)
CallBusClient.feedback: search_id, took_ms, session.
callbus.client.CallBusError ¶
Bases: RuntimeError
Claude tool search¶
callbus.integrations.anthropic.Toolbox ¶
One conversation's tools: the frozen tool list for the API, and what callbus found and ran.
respond(response) answers a response's tool calls; approve(entry, arguments) may veto a
call; on_event(kind, details) sees every search, call and turn. With inline (see the
module's docstring) opening(task) gives the first messages and additions() the system
message to append after each answer.
opening ¶
opening(task)
The first messages: the task, and (prefetch) the tools a search for it finds.
additions ¶
additions()
The role: "system" message that adds the tools found since the last one (inline),
to append right after the message that answered their search; None when there are none.
respond ¶
respond(response)
The user message that answers response's tool calls; None when there are none to
answer (end of turn, max_tokens, refusal, pause_turn: see run).
callbus.integrations.anthropic.run ¶
run(
llm,
callbus,
task,
*,
model,
max_tokens=4096,
max_turns=12,
**create,
)
A reference loop: llm is an anthropic.Anthropic() (or its .beta); create
goes to every messages.create. Stops when a response asks for no tool, on max_tokens,
refusal and unknown stop reasons, or after max_turns requests; pause_turn is sent
back as is.
OpenAI tool search¶
callbus.integrations.openai.Toolbox ¶
One conversation's tools: what callbus found and loaded, and the calls it ran.
respond(response) answers a response's tool searches and calls; approve(entry,
arguments) may veto a call; on_event(kind, details) sees every search, call and turn;
namespaces groups the found tools by server and shrink=False loads every input schema
in full (see the module's docstring).
respond ¶
respond(response)
The items that answer response's tool searches and calls, in order; [] when it asks
for none or is incomplete.
callbus.integrations.openai.run ¶
run(llm, callbus, task, *, model, max_turns=12, **create)
A reference loop: llm is an openai.OpenAI(); create goes to every
responses.create. Stops when a response has nothing to answer, is incomplete, or after
max_turns requests.
LangChain¶
CallBusToolSelector is built lazily, on first use (langchain is an optional extra), so the
module carries its documentation:
callbus.integrations.langchain ¶
callbus as the tool selection of a LangChain 1.x agent (langchain.agents.create_agent).
CallBusToolSelector is an AgentMiddleware: before each model call it narrows the tools the
model is shown to the ones callbus finds for the conversation's last user message. Two ways:
-
The agent's own tools, any LangChain tools: they go to
callbus serve's/v1/rank(any tool list works, not only callbus's catalogue), and the model sees what callbus's adaptive K keeps. It is LangChain'sLLMToolSelectorMiddlewarewithout the extra model call.from langchain.agents import create_agent from callbus.integrations.langchain import CallBusToolSelector
agent = create_agent(model, tools=my_tools, middleware=[CallBusToolSelector()])
-
callbus's catalogue: with
toolbox=, the agent hasToolbox.registry()among its tools and the catalogue's tools are picked by a callbus search (/v1/search, the server's index, with the toolbox'skandinstruction).callbus serveruns their calls and links each to that search in its usage log, whichcallbus learnreads. The agent's other tools are shown as they are.from callbus.client import CallBusClient from callbus.integrations.langgraph import Toolbox
box = Toolbox(CallBusClient()) agent = create_agent(model, tools=[box.registry().values(), my_tools], middleware=[CallBusToolSelector(toolbox=box)])
Either way the model keeps the tools the conversation already called, any tool tool_choice
names, always_include and provider tools (dicts, such as web search); shown tools keep the
agent's order. A model call goes on with every tool when there are at most min_tools to choose
from, when a tool is deferred to the provider's own tool search (extras["defer_loading"]), when
there is no user text, and when callbus fails or does not answer within timeout_s: the
selection then carries on in the background and is kept, so a later call is fast. With a toolbox a
failure shows the catalogue's tools of the session's last selection (and the fixed ones), not the
whole catalogue, which no model takes in one request. An agent makes several model calls per user
message; a selection is kept for ttl_s seconds, and a call that comes while the same selection
is still being made waits for that one, so they ask callbus once. Selections run in daemon threads,
four at a time: one still running never holds up the interpreter's exit.
Needs langchain 1.x (callbus[langchain]), imported when CallBusToolSelector is first
used. Selector is the same work without LangChain's class.
Selector ¶
The middleware's work: select(request) -> request (aselect), the request a model call
should get. on_select(details) sees every selection made or reused and every failure
(outcome selected or skipped), not the calls with nothing to choose from (few tools,
deferred ones, no user text).
LangGraph¶
callbus.integrations.langgraph.Toolbox ¶
The catalogue (taken once) as LangChain tools, and the searches and calls made with it.
retrieve_tools(query) -> list[str] and aretrieve_tools search; k and
instruction go to every search (default: the server's); hits outside servers are
dropped. approve(entry, arguments) may veto a call; on_event(kind, details) sees every
search and call.
LlamaIndex¶
callbus.integrations.llamaindex.CallBusToolRetriever ¶
Bases: ObjectRetriever
An agent's tools from callbus searches (see the module docstring).
k and instruction go to every search (default: the server's). approve(hit,
arguments) may veto a call; on_event(kind, details) sees every search and call.
LiteLLM¶
callbus.integrations.litellm.ToolFilter ¶
The filter without LiteLLM: await ToolFilter(...)(data, call_type) -> a new request
dict with fewer tools, or None to send the request as it came.
warm_up ¶
warm_up()
Rank each warm list once, so callbus has embedded its tools before a request needs
them; -> how many lists it took. A failure is logged, never raised: the filter fails open.