Files
openai--openai-agents-python/examples/hosted_mcp/human_in_the_loop.py
T
2026-07-13 12:39:17 +08:00

134 lines
4.4 KiB
Python

import argparse
import asyncio
import json
from typing import Literal
from agents import (
Agent,
HostedMCPTool,
ModelSettings,
RunConfig,
Runner,
RunResult,
RunResultStreaming,
)
from agents.model_settings import MCPToolChoice
from examples.auto_mode import confirm_with_fallback
def prompt_for_interruption(
tool_name: str | None, arguments: str | dict[str, object] | None
) -> bool:
params: object = {}
if arguments:
if isinstance(arguments, str):
try:
params = json.loads(arguments)
except json.JSONDecodeError:
params = arguments
else:
params = arguments
try:
return confirm_with_fallback(
f"Approve running tool (mcp: {tool_name or 'unknown'}, params: {json.dumps(params)})? (y/n) ",
default=True,
)
except (EOFError, KeyboardInterrupt):
return False
async def _drain_stream(
result: RunResultStreaming,
verbose: bool,
) -> RunResultStreaming:
async for event in result.stream_events():
if verbose:
print(event)
elif event.type == "raw_response_event" and event.data.type == "response.output_text.delta":
print(event.data.delta, end="", flush=True)
if not verbose:
print()
return result
async def main(verbose: bool, stream: bool) -> None:
require_approval: Literal["always"] = "always"
# Use the concise question tool first, then allow the model to answer after approval instead of
# forcing the same tool again.
agent = Agent(
name="MCP Assistant",
instructions=(
"You must always use the MCP tools to answer questions. "
"Use the DeepWiki hosted MCP server to answer questions and do not ask the user for "
"additional configuration."
),
model_settings=ModelSettings(
tool_choice=MCPToolChoice(server_label="deepwiki", name="ask_question")
),
tools=[
HostedMCPTool(
tool_config={
"type": "mcp",
"server_label": "deepwiki",
"server_url": "https://mcp.deepwiki.com/mcp",
"require_approval": require_approval,
}
)
],
)
resume_config = RunConfig(model_settings=ModelSettings(tool_choice="auto"))
question = "Which language is the repository openai/codex written in?"
run_result: RunResult | RunResultStreaming
if stream:
stream_result = Runner.run_streamed(agent, question, max_turns=100)
stream_result = await _drain_stream(stream_result, verbose)
while stream_result.interruptions:
state = stream_result.to_state()
for interruption in stream_result.interruptions:
approved = prompt_for_interruption(interruption.name, interruption.arguments)
if approved:
state.approve(interruption)
else:
state.reject(interruption)
stream_result = Runner.run_streamed(
agent,
state,
max_turns=100,
run_config=resume_config,
)
stream_result = await _drain_stream(stream_result, verbose)
print(f"Done streaming; final result: {stream_result.final_output}")
run_result = stream_result
else:
run_result = await Runner.run(agent, question, max_turns=100)
while run_result.interruptions:
state = run_result.to_state()
for interruption in run_result.interruptions:
approved = prompt_for_interruption(interruption.name, interruption.arguments)
if approved:
state.approve(interruption)
else:
state.reject(interruption)
run_result = await Runner.run(
agent,
state,
max_turns=100,
run_config=resume_config,
)
print(run_result.final_output)
if verbose:
for item in run_result.new_items:
print(item)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", action="store_true", default=False)
parser.add_argument("--stream", action="store_true", default=False)
args = parser.parse_args()
asyncio.run(main(args.verbose, args.stream))