style(python): clean up example file spacing

Single blank lines between definitions, noqa for E302/E303
since this is an example file not a library module.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dhawal Upadhyay
2026-03-23 17:58:05 -07:00
parent d1abe5df56
commit 81067dad81
+3 -33
View File
@@ -8,6 +8,7 @@ extension (Gmail proxy), and custom toolkit.
Usage:
COMPOSIO_API_KEY=... OPENAI_API_KEY=... python examples/custom_tools_agent_test.py
"""
# ruff: noqa: E302, E303
import asyncio
import base64
@@ -16,26 +17,21 @@ import sys
from agents import Agent, Runner
from pydantic import BaseModel, Field
from composio import Composio
from composio_openai_agents import OpenAIAgentsProvider
composio = Composio(provider=OpenAIAgentsProvider())
# ── 1. Standalone tool — slug/name/description inferred from function ──
class UserLookupInput(BaseModel):
user_id: str = Field(description="User ID (e.g. user-1)")
USERS = {
"user-1": {"name": "Alice Johnson", "email": "alice@acme.com", "role": "admin"},
"user-2": {"name": "Bob Smith", "email": "bob@acme.com", "role": "developer"},
}
@composio.experimental.tool()
def get_user(input: UserLookupInput, ctx):
"""Look up an internal user by ID. Returns name, email, and role."""
@@ -43,21 +39,16 @@ def get_user(input: UserLookupInput, ctx):
if not user:
raise ValueError(f'User "{input.user_id}" not found')
return user
# slug = GET_USER (from function name)
# name = Get User (humanized)
# description = "Look up an internal user by ID..." (from docstring)
# ── 2. Standalone tool with overrides ──────────────────────────────────
class GreetInput(BaseModel):
name: str = Field(description="Name to greet")
style: str = Field(default="friendly", description="Greeting style")
@composio.experimental.tool(
slug="GREET_V2",
name="Greeting Generator",
@@ -71,22 +62,17 @@ def greet(input: GreetInput, ctx):
"casual": f"Yo {input.name}, what's up?",
}
return {"message": greetings.get(input.style, f"Hello {input.name}!")}
# slug = GREET_V2 (overridden, not GREET)
# name = Greeting Generator (overridden, not Greet)
# description = "Generate a personalized..." (overridden, not docstring)
# ── 3. Extension tool — inherits Gmail auth via ctx.proxy_execute() ────
class DraftInput(BaseModel):
to: str = Field(description="Recipient email address")
subject: str = Field(description="Email subject")
body: str = Field(description="Email body (plain text)")
@composio.experimental.tool(extends_toolkit="gmail")
def create_draft(input: DraftInput, ctx):
"""Create a real Gmail draft. Appears in the user's drafts folder."""
@@ -106,37 +92,30 @@ def create_draft(input: DraftInput, ctx):
data = res["data"]
return {"draft_id": data["id"], "to": input.to, "subject": input.subject}
# ── 4. Custom toolkit — groups related tools under one namespace ───────
role_manager = composio.experimental.Toolkit(
slug="ROLE_MANAGER",
name="Role Manager",
description="Manage user roles in the system",
)
class SetRoleInput(BaseModel):
user_id: str = Field(description="User ID")
role: str = Field(description="New role (admin, developer, viewer)")
@role_manager.tool()
def set_role(input: SetRoleInput, ctx):
"""Set a user's role. Returns confirmation."""
return {"user_id": input.user_id, "role": input.role, "updated": True}
@role_manager.tool(name="List All Roles")
def list_roles(input: UserLookupInput, ctx):
"""List available roles for a user."""
return {"roles": ["admin", "developer", "viewer"], "current": "admin"}
# ── Agent ──────────────────────────────────────────────────────────────
async def run_test(prompt, test_name):
print(f"\n{'=' * 60}")
print(f"TEST: {test_name}")
@@ -152,7 +131,6 @@ async def run_test(prompt, test_name):
},
)
tools = session.tools()
agent = Agent(
name="Assistant",
instructions=(
@@ -172,7 +150,6 @@ async def run_test(prompt, test_name):
print(f"\nERROR: {e}")
return False
async def main():
tests = [
("Look up user-2", "Standalone (inferred slug/name)"),
@@ -180,14 +157,8 @@ async def main():
("Set user-1 role to developer", "Toolkit tool"),
("Look up user-1 and set their role to viewer", "Mixed local tools"),
("What is the weather in Tokyo?", "Remote (weathermap)"),
(
'Draft an email to bob@acme.com with subject "Test" and body "Hello!"',
"Gmail proxy (extends_toolkit)",
),
(
"Look up user-1, draft them an email saying hi, include weather in SF",
"Mixed all paths",
),
('Draft an email to bob@acme.com with subject "Test" and body "Hello!"', "Gmail proxy"),
("Look up user-1, draft them an email saying hi, include weather in SF", "Mixed all"),
]
results = []
@@ -202,7 +173,6 @@ async def main():
print(f" [{'PASS' if ok else 'FAIL'}] {name}")
print(f"\n{sum(1 for _, ok in results if ok)}/{len(results)} passed")
if __name__ == "__main__":
if not os.environ.get("OPENAI_API_KEY"):
print("Error: OPENAI_API_KEY env var required")