171 lines
4.4 KiB
Python
171 lines
4.4 KiB
Python
"""
|
|
Tool Router preload with OpenAI Agents.
|
|
|
|
Shows direct tool exposure for:
|
|
1. Composio tools via preload["tools"].
|
|
2. SDK custom tools via preload=True on the custom tool or toolkit.
|
|
3. A nested custom tool override with preload=False inside a preloaded toolkit.
|
|
|
|
Usage:
|
|
COMPOSIO_API_KEY=... OPENAI_API_KEY=... python examples/tool_router/preload.py
|
|
"""
|
|
|
|
import os
|
|
|
|
from agents import Agent, Runner
|
|
from composio_openai_agents import OpenAIAgentsProvider
|
|
from pydantic import BaseModel, Field
|
|
|
|
from composio import Composio
|
|
|
|
|
|
def require_env(name: str) -> str:
|
|
value = os.environ.get(name)
|
|
if not value:
|
|
raise RuntimeError(f"Set {name} before running this example.")
|
|
return value
|
|
|
|
|
|
class UserLookupInput(BaseModel):
|
|
user_id: str = Field(description="Internal user ID, for example user-1")
|
|
|
|
|
|
class TeamSearchInput(BaseModel):
|
|
team: str = Field(description="Team name to search for")
|
|
|
|
|
|
class AccountInput(BaseModel):
|
|
user_id: str = Field(description="Internal user ID")
|
|
|
|
|
|
INTERNAL_USERS = {
|
|
"user-1": {
|
|
"id": "user-1",
|
|
"name": "Ada Lovelace",
|
|
"email": "ada@example.com",
|
|
"team": "platform",
|
|
"plan": "enterprise",
|
|
},
|
|
"user-2": {
|
|
"id": "user-2",
|
|
"name": "Grace Hopper",
|
|
"email": "grace@example.com",
|
|
"team": "developer-tools",
|
|
"plan": "startup",
|
|
},
|
|
}
|
|
|
|
|
|
composio = Composio(
|
|
api_key=require_env("COMPOSIO_API_KEY"),
|
|
base_url=os.environ.get("COMPOSIO_BASE_URL"),
|
|
provider=OpenAIAgentsProvider(),
|
|
)
|
|
require_env("OPENAI_API_KEY")
|
|
|
|
|
|
@composio.experimental.tool(
|
|
slug="LOOKUP_INTERNAL_USER",
|
|
name="Lookup internal user",
|
|
description="Look up an internal demo user profile by user ID.",
|
|
preload=True,
|
|
)
|
|
def lookup_internal_user(input: UserLookupInput, ctx):
|
|
user = INTERNAL_USERS.get(input.user_id)
|
|
if not user:
|
|
raise ValueError(f'User "{input.user_id}" not found')
|
|
return user
|
|
|
|
|
|
@composio.experimental.tool(
|
|
slug="SEARCH_INTERNAL_USERS",
|
|
name="Search internal users",
|
|
description=(
|
|
"Search demo internal users by team. This custom tool is search-only "
|
|
"because preload is not enabled."
|
|
),
|
|
)
|
|
def search_internal_users(input: TeamSearchInput, ctx):
|
|
return {
|
|
"results": [
|
|
user for user in INTERNAL_USERS.values() if user["team"] == input.team
|
|
]
|
|
}
|
|
|
|
|
|
internal_admin = composio.experimental.Toolkit(
|
|
slug="INTERNAL_ADMIN",
|
|
name="Internal admin",
|
|
description="Demo internal administration tools.",
|
|
preload=True,
|
|
)
|
|
|
|
|
|
@internal_admin.tool(
|
|
slug="GET_ACCOUNT_HEALTH",
|
|
name="Get account health",
|
|
description="Return internal account health details for a user.",
|
|
)
|
|
def get_account_health(input: AccountInput, ctx):
|
|
return {
|
|
"user_id": input.user_id,
|
|
"health": "green",
|
|
"open_incidents": 0,
|
|
"renewal_risk": "low",
|
|
}
|
|
|
|
|
|
@internal_admin.tool(
|
|
slug="GET_ACCOUNT_AUDIT_LOG",
|
|
name="Get account audit log",
|
|
description=(
|
|
"Return internal account audit events. This overrides the toolkit "
|
|
"preload default and remains search-only."
|
|
),
|
|
preload=False,
|
|
)
|
|
def get_account_audit_log(input: AccountInput, ctx):
|
|
return {
|
|
"user_id": input.user_id,
|
|
"events": ["login", "settings_viewed"],
|
|
}
|
|
|
|
|
|
session = composio.create(
|
|
user_id="preload-example-user",
|
|
toolkits=["hackernews"],
|
|
tools={"hackernews": {"enable": ["HACKERNEWS_GET_USER"]}},
|
|
preload={"tools": ["HACKERNEWS_GET_USER"]},
|
|
manage_connections=False,
|
|
experimental={
|
|
"custom_tools": [lookup_internal_user, search_internal_users],
|
|
"custom_toolkits": [internal_admin],
|
|
},
|
|
)
|
|
|
|
tools = session.tools()
|
|
tool_names = [tool.name for tool in tools]
|
|
assert "HACKERNEWS_GET_USER" in tool_names
|
|
assert "LOCAL_LOOKUP_INTERNAL_USER" in tool_names
|
|
assert "LOCAL_INTERNAL_ADMIN_GET_ACCOUNT_HEALTH" in tool_names
|
|
assert "LOCAL_SEARCH_INTERNAL_USERS" not in tool_names
|
|
assert "LOCAL_INTERNAL_ADMIN_GET_ACCOUNT_AUDIT_LOG" not in tool_names
|
|
|
|
print("Direct tools exposed to the agent:")
|
|
for tool in tools:
|
|
print(f"- {tool.name}")
|
|
|
|
agent = Agent(
|
|
name="Preload Demo Agent",
|
|
instructions="Use the provided tools to perform the task.",
|
|
model=os.environ.get("OPENAI_MODEL", "gpt-5.5"),
|
|
tools=tools,
|
|
)
|
|
|
|
prompt = (
|
|
'Look up Hacker News user "pg", then look up internal user user-1 and '
|
|
"their account health. Summarize the useful facts."
|
|
)
|
|
result = Runner.run_sync(agent, prompt)
|
|
print(result.final_output)
|