11# SPDX-FileCopyrightText: 2025 GitHub
22# SPDX-License-Identifier: MIT
33
4- import asyncio
5- from threading import Thread
64import argparse
7- import os
8- import sys
9- from dotenv import load_dotenv
5+ import asyncio
6+ import json
107import logging
11- from logging . handlers import RotatingFileHandler
12- from pprint import pprint , pformat
8+ import os
9+ import pathlib
1310import re
14- import json
11+ import sys
1512import uuid
16- import pathlib
13+ from logging .handlers import RotatingFileHandler
14+ from pprint import pformat
15+ from typing import Callable
1716
18- from .agent import DEFAULT_MODEL , TaskRunHooks , TaskAgentHooks
19- #from agents.run import DEFAULT_MAX_TURNS # XXX: this is 10, we need more than that
20- from agents .exceptions import MaxTurnsExceeded , AgentsException
17+ from agents import Agent , RunContextWrapper , TContext , Tool
2118from agents .agent import ModelSettings
22- from agents .mcp import MCPServer , MCPServerStdio , MCPServerSse , MCPServerStreamableHttp , create_static_tool_filter
19+
20+ #from agents.run import DEFAULT_MAX_TURNS # XXX: this is 10, we need more than that
21+ from agents .exceptions import AgentsException , MaxTurnsExceeded
2322from agents .extensions .handoff_prompt import prompt_with_handoff_instructions
24- from agents import Tool , RunContextWrapper , TContext , Agent
25- from openai import BadRequestError , APITimeoutError , RateLimitError
23+ from agents .mcp import MCPServerSse , MCPServerStdio , MCPServerStreamableHttp , create_static_tool_filter
24+ from dotenv import load_dotenv
25+ from openai import APITimeoutError , BadRequestError , RateLimitError
2626from openai .types .responses import ResponseTextDeltaEvent
27- from typing import Callable
2827
29- from .shell_utils import shell_tool_call
30- from .mcp_utils import DEFAULT_MCP_CLIENT_SESSION_TIMEOUT , ReconnectingMCPServerStdio , AsyncDebugMCPServerStdio , MCPNamespaceWrap , mcp_client_params , mcp_system_prompt , StreamableMCPThread , compress_name
31- from .render_utils import render_model_output , flush_async_output
32- from .env_utils import TmpEnv
33- from .agent import TaskAgent
34- from .capi import list_tool_call_models
28+ from .agent import DEFAULT_MODEL , TaskAgent , TaskAgentHooks , TaskRunHooks
3529from .available_tools import AvailableTools
30+ from .capi import list_tool_call_models
31+ from .env_utils import TmpEnv
32+ from .mcp_utils import (
33+ DEFAULT_MCP_CLIENT_SESSION_TIMEOUT ,
34+ MCPNamespaceWrap ,
35+ ReconnectingMCPServerStdio ,
36+ StreamableMCPThread ,
37+ compress_name ,
38+ mcp_client_params ,
39+ mcp_system_prompt ,
40+ )
41+ from .render_utils import flush_async_output , render_model_output
42+ from .shell_utils import shell_tool_call
3643
3744load_dotenv ()
3845
@@ -74,7 +81,7 @@ def parse_prompt_args(available_tools: AvailableTools,
7481 args = parser .parse_known_args (user_prompt .split (' ' ) if user_prompt else None )
7582 except SystemExit as e :
7683 if e .code == 2 :
77- logging .error (f"User provided incomplete prompt: { user_prompt } " )
84+ logging .exception (f"User provided incomplete prompt: { user_prompt } " )
7885 return None , None , None , help_msg
7986 p = args [0 ].p .strip () if args [0 ].p else None
8087 t = args [0 ].t .strip () if args [0 ].t else None
@@ -218,14 +225,13 @@ async def mcp_session_task(
218225 except Exception as e :
219226 print (f"Streamable mcp server process exception: { e } " )
220227 except asyncio .CancelledError :
221- logging .error (f"Timeout on cleanup for mcp server: { server ._name } " )
228+ logging .exception (f"Timeout on cleanup for mcp server: { server ._name } " )
222229 finally :
223230 mcp_servers .remove (s )
224231 except RuntimeError as e :
225- logging .error (f"RuntimeError in mcp session task: { e } " )
232+ logging .exception (f"RuntimeError in mcp session task: { e } " )
226233 except asyncio .CancelledError as e :
227- logging .error (f"Timeout on main session task: { e } " )
228- pass
234+ logging .exception (f"Timeout on main session task: { e } " )
229235 finally :
230236 mcp_servers .clear ()
231237
@@ -318,17 +324,17 @@ async def _run_streamed():
318324 return
319325 except APITimeoutError :
320326 if not max_retry :
321- logging .error ( f "Max retries for APITimeoutError reached" )
327+ logging .exception ( "Max retries for APITimeoutError reached" )
322328 raise
323329 max_retry -= 1
324330 except RateLimitError :
325331 if rate_limit_backoff == MAX_RATE_LIMIT_BACKOFF :
326- raise APITimeoutError (f "Max rate limit backoff reached" )
332+ raise APITimeoutError ("Max rate limit backoff reached" )
327333 if rate_limit_backoff > MAX_RATE_LIMIT_BACKOFF :
328334 rate_limit_backoff = MAX_RATE_LIMIT_BACKOFF
329335 else :
330336 rate_limit_backoff += rate_limit_backoff
331- logging .error (f"Hit rate limit ... holding for { rate_limit_backoff } " )
337+ logging .exception (f"Hit rate limit ... holding for { rate_limit_backoff } " )
332338 await asyncio .sleep (rate_limit_backoff )
333339 await _run_streamed ()
334340 complete = True
@@ -338,22 +344,22 @@ async def _run_streamed():
338344 await render_model_output (f"** 🤖❗ Max Turns Reached: { e } \n " ,
339345 async_task = async_task ,
340346 task_id = task_id )
341- logging .error (f"Exceeded max_turns: { max_turns } " )
347+ logging .exception (f"Exceeded max_turns: { max_turns } " )
342348 except AgentsException as e :
343349 await render_model_output (f"** 🤖❗ Agent Exception: { e } \n " ,
344350 async_task = async_task ,
345351 task_id = task_id )
346- logging .error (f"Agent Exception: { e } " )
352+ logging .exception (f"Agent Exception: { e } " )
347353 except BadRequestError as e :
348354 await render_model_output (f"** 🤖❗ Request Error: { e } \n " ,
349355 async_task = async_task ,
350356 task_id = task_id )
351- logging .error (f"Bad Request: { e } " )
357+ logging .exception (f"Bad Request: { e } " )
352358 except APITimeoutError as e :
353359 await render_model_output (f"** 🤖❗ Timeout Error: { e } \n " ,
354360 async_task = async_task ,
355361 task_id = task_id )
356- logging .error (f"Bad Request: { e } " )
362+ logging .exception (f"Bad Request: { e } " )
357363
358364 if async_task :
359365 await flush_async_output (task_id )
@@ -369,10 +375,10 @@ async def _run_streamed():
369375 try :
370376 cleanup_attempts_left -= 1
371377 await asyncio .wait_for (mcp_sessions , timeout = MCP_CLEANUP_TIMEOUT )
372- except asyncio .TimeoutError as e :
378+ except asyncio .TimeoutError :
373379 continue
374380 except Exception as e :
375- logging .error (f"Exception in mcp server cleanup task: { e } " )
381+ logging .exception (f"Exception in mcp server cleanup task: { e } " )
376382
377383
378384async def main (available_tools : AvailableTools ,
@@ -425,7 +431,7 @@ async def on_handoff_hook(
425431 if model_dict :
426432 if not isinstance (model_dict , dict ):
427433 raise ValueError (f"Models section of the model_config file { model_config } must be a dictionary" )
428- model_keys = model_dict .keys ()
434+ model_keys = model_dict .keys ()
429435
430436 for task in taskflow ['taskflow' ]:
431437
@@ -557,15 +563,15 @@ async def run_prompts(async_task=False, max_concurrent_tasks=5):
557563
558564 # if this is a shell task, execute that and append the results
559565 if run :
560- await render_model_output (f "** 🤖🐚 Executing Shell Task\n " )
566+ await render_model_output ("** 🤖🐚 Executing Shell Task\n " )
561567 # this allows e.g. shell based jq output to become available for repeat prompts
562568 try :
563569 result = shell_tool_call (run ).content [0 ].model_dump_json ()
564570 last_mcp_tool_results .append (result )
565571 return True
566572 except RuntimeError as e :
567573 await render_model_output (f"** 🤖❗ Shell Task Exception: { e } \n " )
568- logging .error (f"Shell task error: { e } " )
574+ logging .exception (f"Shell task error: { e } " )
569575 return False
570576
571577 tasks = []
0 commit comments