Custom tools
experimental.tool turns your own function into a tool alongside Composio's, with its input schema inferred from a Pydantic model and its description from the docstring.
from pydantic import BaseModel, Field
from composio import Composio
composio = Composio(api_key="not-a-real-key", allow_tracking=False)
ORDERS = {"A-1001": "shipped on 12 March"}
class LookupInput(BaseModel):
order_id: str = Field(description="The order id, like A-1001")The tool's input is a Pydantic model; its field descriptions become part of the schema the model reads.
@composio.experimental.tool
def lookup_order(input: LookupInput, ctx):
"""Look up the status of an order in the shop database."""
return {"status": ORDERS.get(input.order_id, "no such order")}print(lookup_order.slug, "|", lookup_order.name)
print(lookup_order.description)
print(json.dumps(lookup_order.input_schema))The decorator returned a CustomTool. From the function it inferred the slug LOOKUP_ORDER, a display name, the description from the docstring, and a JSON schema from LookupInput, with the field's description. No request was made: defining a custom tool is local.
print(lookup_order.execute(LookupInput(order_id="A-1001"), ctx=None))
print(lookup_order.execute(LookupInput(order_id="A-9999"), ctx=None))execute is your function. At run time Composio passes a ctx, which can call an app with the user's connection through ctx.proxy_execute; this function does not need it, so a test can pass None. Add the tool to a session with composio.use(session_id, custom_tools=[lookup_order]), or with experimental={"custom_tools": [lookup_order]} on create. Custom tools run in your process, not on Composio's servers.
- Add an optional field with a default to
LookupInputand print the schema. - Pass
slug="SHOP_LOOKUP_ORDER"to the decorator. - Read
extends_toolkitin the decorator's docstring: what does it give a custom tool?
Every expert started right here.