chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
import asyncio
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from agents import Agent, AgentBase, ModelSettings, RunContextWrapper, Runner, trace
|
||||
from agents.tool import function_tool
|
||||
from examples.auto_mode import confirm_with_fallback, input_with_fallback, is_auto_mode
|
||||
|
||||
"""
|
||||
This example demonstrates the agents-as-tools pattern with conditional tool enabling.
|
||||
Agent tools are dynamically enabled/disabled based on user access levels using the
|
||||
is_enabled parameter.
|
||||
"""
|
||||
|
||||
|
||||
class AppContext(BaseModel):
|
||||
language_preference: str = "spanish_only" # "spanish_only", "french_spanish", "european"
|
||||
|
||||
|
||||
def french_spanish_enabled(ctx: RunContextWrapper[AppContext], agent: AgentBase) -> bool:
|
||||
"""Enable for French+Spanish and European preferences."""
|
||||
return ctx.context.language_preference in ["french_spanish", "european"]
|
||||
|
||||
|
||||
def european_enabled(ctx: RunContextWrapper[AppContext], agent: AgentBase) -> bool:
|
||||
"""Only enable for European preference."""
|
||||
return ctx.context.language_preference == "european"
|
||||
|
||||
|
||||
@function_tool(needs_approval=True)
|
||||
async def get_user_name() -> str:
|
||||
print("Getting the user's name...")
|
||||
return "Kaz"
|
||||
|
||||
|
||||
# Create specialized agents
|
||||
spanish_agent = Agent(
|
||||
name="spanish_agent",
|
||||
instructions=(
|
||||
"Respond in Spanish. Call get_user_name exactly once before replying, then greet that "
|
||||
"user by name and answer the user's question in a non-empty final response."
|
||||
),
|
||||
model_settings=ModelSettings(tool_choice="required"),
|
||||
tools=[get_user_name],
|
||||
)
|
||||
|
||||
french_agent = Agent(
|
||||
name="french_agent",
|
||||
instructions="You respond in French. Always reply to the user's question in French.",
|
||||
)
|
||||
|
||||
italian_agent = Agent(
|
||||
name="italian_agent",
|
||||
instructions="You respond in Italian. Always reply to the user's question in Italian.",
|
||||
)
|
||||
|
||||
# Create orchestrator with conditional tools
|
||||
orchestrator = Agent(
|
||||
name="orchestrator",
|
||||
instructions=(
|
||||
"You are a multilingual assistant. Call each available language tool requested by the "
|
||||
"user exactly once, including every requested tool when multiple languages are requested. "
|
||||
"Wait for all tool calls to finish, then combine their responses into a non-empty final "
|
||||
"response. Never translate the user's request yourself."
|
||||
),
|
||||
tools=[
|
||||
spanish_agent.as_tool(
|
||||
tool_name="respond_spanish",
|
||||
tool_description="Respond to the user's question in Spanish",
|
||||
is_enabled=True, # Always enabled
|
||||
needs_approval=True, # HITL
|
||||
),
|
||||
french_agent.as_tool(
|
||||
tool_name="respond_french",
|
||||
tool_description="Respond to the user's question in French",
|
||||
is_enabled=french_spanish_enabled,
|
||||
),
|
||||
italian_agent.as_tool(
|
||||
tool_name="respond_italian",
|
||||
tool_description="Respond to the user's question in Italian",
|
||||
is_enabled=european_enabled,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
async def main():
|
||||
"""Interactive demo with LLM interaction."""
|
||||
print("Agents-as-Tools with Conditional Enabling\n")
|
||||
print(
|
||||
"This demonstrates how language response tools are dynamically enabled based on user preferences.\n"
|
||||
)
|
||||
|
||||
print("Choose language preference:")
|
||||
print("1. Spanish only (1 tool)")
|
||||
print("2. French and Spanish (2 tools)")
|
||||
print("3. European languages (3 tools)")
|
||||
|
||||
choice = input_with_fallback("\nSelect option (1-3): ", "2").strip()
|
||||
preference_map = {"1": "spanish_only", "2": "french_spanish", "3": "european"}
|
||||
language_preference = preference_map.get(choice, "spanish_only")
|
||||
|
||||
# Create context and show available tools
|
||||
context = RunContextWrapper(AppContext(language_preference=language_preference))
|
||||
available_tools = await orchestrator.get_all_tools(context)
|
||||
tool_names = [tool.name for tool in available_tools]
|
||||
|
||||
print(f"\nLanguage preference: {language_preference}")
|
||||
print(f"Available tools: {', '.join(tool_names)}")
|
||||
print(f"The LLM will only see and can use these {len(available_tools)} tools\n")
|
||||
|
||||
# Get user request
|
||||
user_request = input_with_fallback(
|
||||
"Ask a question and see responses in available languages:\n",
|
||||
"Answer in Spanish and French: How do you say good morning?",
|
||||
)
|
||||
|
||||
# Run with LLM interaction
|
||||
print("\nProcessing request...")
|
||||
with trace("Conditional tool access"):
|
||||
result = await Runner.run(
|
||||
starting_agent=orchestrator,
|
||||
input=user_request,
|
||||
context=context.context,
|
||||
)
|
||||
while result.interruptions:
|
||||
|
||||
async def confirm(question: str) -> bool:
|
||||
return confirm_with_fallback(f"{question} (y/n): ", default=True)
|
||||
|
||||
state = result.to_state()
|
||||
for interruption in result.interruptions:
|
||||
prompt = f"\nDo you approve this tool call: {interruption.name} with arguments {interruption.arguments}?"
|
||||
confirmed = await confirm(prompt)
|
||||
if confirmed:
|
||||
state.approve(interruption)
|
||||
print(f"✓ Approved: {interruption.name}")
|
||||
else:
|
||||
state.reject(interruption)
|
||||
print(f"✗ Rejected: {interruption.name}")
|
||||
result = await Runner.run(orchestrator, state)
|
||||
|
||||
if is_auto_mode():
|
||||
called_tools: list[str] = []
|
||||
for item in result.new_items:
|
||||
tool_name = getattr(item.raw_item, "name", None)
|
||||
if item.type == "tool_call_item" and isinstance(tool_name, str):
|
||||
if tool_name.startswith("respond_"):
|
||||
called_tools.append(tool_name)
|
||||
if sorted(called_tools) != ["respond_french", "respond_spanish"]:
|
||||
raise RuntimeError(f"Expected Spanish and French responses once, got {called_tools}")
|
||||
if not result.final_output:
|
||||
raise RuntimeError("Expected a non-empty multilingual response")
|
||||
|
||||
print(f"\nResponse:\n{result.final_output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user