chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
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)
|
||||
Reference in New Issue
Block a user