Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ ignore = [
# Style choices — deliberate project conventions
"EM101", # Exception string literals
"EM102", # Exception f-strings
"G004", # Logging f-strings
"T201", # print() used for user output
"TRY003", # Raise with inline message strings

Expand Down
18 changes: 9 additions & 9 deletions src/seclab_taskflow_agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,27 +70,27 @@ def __init__(

async def on_agent_start(self, context: RunContextWrapper[TContext], agent: Agent[TContext]) -> None:
"""Called when an agent begins execution."""
logging.debug(f"TaskRunHooks on_agent_start: {agent.name}")
logging.debug("TaskRunHooks on_agent_start: %s", agent.name)
if self._on_agent_start:
await self._on_agent_start(context, agent)

async def on_agent_end(self, context: RunContextWrapper[TContext], agent: Agent[TContext], output: Any) -> None:
"""Called when an agent finishes execution."""
logging.debug(f"TaskRunHooks on_agent_end: {agent.name}")
logging.debug("TaskRunHooks on_agent_end: %s", agent.name)
if self._on_agent_end:
await self._on_agent_end(context, agent, output)

async def on_tool_start(self, context: RunContextWrapper[TContext], agent: Agent[TContext], tool: Tool) -> None:
"""Called before a tool invocation begins."""
logging.debug(f"TaskRunHooks on_tool_start: {tool.name}")
logging.debug("TaskRunHooks on_tool_start: %s", tool.name)
if self._on_tool_start:
await self._on_tool_start(context, agent, tool)

async def on_tool_end(
self, context: RunContextWrapper[TContext], agent: Agent[TContext], tool: Tool, result: str
) -> None:
"""Called after a tool invocation completes."""
logging.debug(f"TaskRunHooks on_tool_end: {tool.name} ")
logging.debug("TaskRunHooks on_tool_end: %s", tool.name)
if self._on_tool_end:
await self._on_tool_end(context, agent, tool, result)

Expand All @@ -117,33 +117,33 @@ async def on_handoff(
self, context: RunContextWrapper[TContext], agent: Agent[TContext], source: Agent[TContext]
) -> None:
"""Called when control is handed off from one agent to another."""
logging.debug(f"TaskAgentHooks on_handoff: {source.name} -> {agent.name}")
logging.debug("TaskAgentHooks on_handoff: %s -> %s", source.name, agent.name)
if self._on_handoff:
await self._on_handoff(context, agent, source)

async def on_start(self, context: RunContextWrapper[TContext], agent: Agent[TContext]) -> None:
"""Called when the agent starts processing."""
logging.debug(f"TaskAgentHooks on_start: {agent.name}")
logging.debug("TaskAgentHooks on_start: %s", agent.name)
if self._on_start:
await self._on_start(context, agent)

async def on_end(self, context: RunContextWrapper[TContext], agent: Agent[TContext], output: Any) -> None:
"""Called when the agent finishes processing."""
logging.debug(f"TaskAgentHooks on_end: {agent.name}")
logging.debug("TaskAgentHooks on_end: %s", agent.name)
if self._on_end:
await self._on_end(context, agent, output)

async def on_tool_start(self, context: RunContextWrapper[TContext], agent: Agent[TContext], tool: Tool) -> None:
"""Called before a tool invocation begins."""
logging.debug(f"TaskAgentHooks on_tool_start: {tool.name}")
logging.debug("TaskAgentHooks on_tool_start: %s", tool.name)
if self._on_tool_start:
await self._on_tool_start(context, agent, tool)

async def on_tool_end(
self, context: RunContextWrapper[TContext], agent: Agent[TContext], tool: Tool, result: str
) -> None:
"""Called after a tool invocation completes."""
logging.debug(f"TaskAgentHooks on_tool_end: {tool.name}")
logging.debug("TaskAgentHooks on_tool_end: %s", tool.name)
if self._on_tool_end:
await self._on_tool_end(context, agent, tool, result)

Expand Down
14 changes: 7 additions & 7 deletions src/seclab_taskflow_agent/mcp_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ def build_mcp_servers(
if "command" in params:

def _print_out(line: str) -> None:
logging.info(f"Streamable MCP Server stdout: {line}")
logging.info("Streamable MCP Server stdout: %s", line)

def _print_err(line: str) -> None:
logging.info(f"Streamable MCP Server stderr: {line}")
logging.info("Streamable MCP Server stderr: %s", line)

server_proc = StreamableMCPThread(
params["command"],
Expand Down Expand Up @@ -137,7 +137,7 @@ async def mcp_session_task(
"""
try:
for entry in entries:
logging.debug(f"Connecting mcp server: {entry.name}")
logging.debug("Connecting mcp server: %s", entry.name)
if entry.process is not None:
entry.process.start()
await entry.process.async_wait_for_connection(poll_interval=0.1)
Expand All @@ -148,17 +148,17 @@ async def mcp_session_task(

for entry in list(reversed(entries)):
try:
logging.debug(f"Starting cleanup for mcp server: {entry.name}")
logging.debug("Starting cleanup for mcp server: %s", entry.name)
await entry.server.cleanup()
logging.debug(f"Cleaned up mcp server: {entry.name}")
logging.debug("Cleaned up mcp server: %s", entry.name)
if entry.process is not None:
entry.process.stop()
try:
await asyncio.to_thread(entry.process.join_and_raise)
except Exception as e:
logging.warning(f"Streamable mcp server process exception: {e}")
logging.warning("Streamable mcp server process exception: %s", e)
except asyncio.CancelledError:
logging.exception(f"Timeout on cleanup for mcp server: {entry.name}")
logging.exception("Timeout on cleanup for mcp server: %s", entry.name)
except RuntimeError:
logging.exception("RuntimeError in mcp session task")
except asyncio.CancelledError:
Expand Down
2 changes: 1 addition & 1 deletion src/seclab_taskflow_agent/mcp_servers/codeql/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ def _get_source_prefix(database_path: Path, strip_leading_slash=True) -> str:
source_prefix = source_prefix.lstrip("/")
return source_prefix
except (yaml.YAMLError, FileNotFoundError, KeyError) as e:
logging.error(f"Error parsing sourceLocationPrefix: {e}")
logging.error("Error parsing sourceLocationPrefix: %s", e)
raise


Expand Down
8 changes: 4 additions & 4 deletions src/seclab_taskflow_agent/mcp_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def mcp_client_params(
case "stdio":
env = dict(sp.env) if sp.env else None
args = list(sp.args) if sp.args else None
logging.debug(f"Initializing toolbox: {tb}\nargs:\n{args}\nenv:\n{env}\n")
logging.debug("Initializing toolbox: %s\nargs:\n%s\nenv:\n%s\n", tb, args, env)
if env:
for k, v in list(env.items()):
try:
Expand All @@ -175,11 +175,11 @@ def mcp_client_params(
logging.critical(e)
logging.info("Assuming toolbox has default configuration available")
del env[k]
logging.debug(f"Tool call environment: {env}")
logging.debug("Tool call environment: %s", env)
if args:
for i, v in enumerate(args):
args[i] = swap_env(v)
logging.debug(f"Tool call args: {args}")
logging.debug("Tool call args: %s", args)
server_params["command"] = sp.command
server_params["args"] = args
server_params["env"] = env
Expand All @@ -199,7 +199,7 @@ def mcp_client_params(
if sp.command is not None:
env = dict(sp.env) if sp.env else None
args = list(sp.args) if sp.args else None
logging.debug(f"Initializing streamable toolbox: {tb}\nargs:\n{args}\nenv:\n{env}\n")
logging.debug("Initializing streamable toolbox: %s\nargs:\n%s\nenv:\n%s\n", tb, args, env)
exe = shutil.which(sp.command)
if exe is None:
raise FileNotFoundError(f"Could not resolve path to {sp.command}")
Expand Down
6 changes: 3 additions & 3 deletions src/seclab_taskflow_agent/prompt_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ def parse_prompt_args(
args = parser.parse_known_args(user_prompt.split(" ") if user_prompt else None)
except SystemExit as e:
if e.code == 2:
logging.exception(f"User provided incomplete prompt: {user_prompt}")
logging.exception("User provided incomplete prompt: %s", user_prompt)
return None, None, None, None, "", help_msg
except Exception:
logging.exception(f"Failed to parse prompt: {user_prompt}")
logging.exception("Failed to parse prompt: %s", user_prompt)
return None, None, None, None, "", help_msg
p = args[0].p.strip() if args[0].p else None
t = args[0].t.strip() if args[0].t else None
Expand All @@ -65,7 +65,7 @@ def parse_prompt_args(
if args[0].globals:
for g in args[0].globals:
if "=" not in g:
logging.error(f"Invalid global variable format: {g}. Expected KEY=VALUE")
logging.error("Invalid global variable format: %s. Expected KEY=VALUE", g)
return None, None, None, None, "", help_msg
key, value = g.split("=", 1)
cli_globals[key.strip()] = value.strip()
Expand Down
22 changes: 11 additions & 11 deletions src/seclab_taskflow_agent/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,14 +197,14 @@ async def _build_prompts_to_run(
logging.critical("No last MCP tool result available")
raise
except json.JSONDecodeError as exc:
logging.critical(f"Could not parse tool result as JSON: {last_mcp_tool_results[-1][:200]}")
logging.critical("Could not parse tool result as JSON: %s", last_mcp_tool_results[-1][:200])
raise ValueError("Tool result is not valid JSON") from exc

text = last_result.get("text", "")
try:
iterable_result = json.loads(text)
except json.JSONDecodeError as exc:
logging.critical(f"Could not parse result text: {text}")
logging.critical("Could not parse result text: %s", text)
raise ValueError("Result text is not valid JSON") from exc
try:
iter(iterable_result)
Expand All @@ -215,7 +215,7 @@ async def _build_prompts_to_run(
if not iterable_result:
await render_model_output("** 🤖❗MCP tool result iterable is empty!\n")
else:
logging.debug(f"Rendering templated prompts for results: {iterable_result}")
logging.debug("Rendering templated prompts for results: %s", iterable_result)
for value in iterable_result:
try:
rendered_prompt = render_template(
Expand All @@ -227,7 +227,7 @@ async def _build_prompts_to_run(
)
prompts_to_run.append(rendered_prompt)
except jinja2.TemplateError as e:
logging.error(f"Error rendering template for result {value}: {e}")
logging.error("Error rendering template for result %s: %s", value, e)
raise ValueError(f"Template rendering failed: {e}")

# Consume only after all prompts rendered successfully so that
Expand Down Expand Up @@ -408,15 +408,15 @@ async def _run_streamed() -> None:
rate_limit_backoff = MAX_RATE_LIMIT_BACKOFF
else:
rate_limit_backoff += rate_limit_backoff
logging.exception(f"Hit rate limit ... holding for {rate_limit_backoff}")
logging.exception("Hit rate limit ... holding for %s", rate_limit_backoff)
await asyncio.sleep(rate_limit_backoff)

await _run_streamed()
complete = True

except MaxTurnsExceeded as e:
await render_model_output(f"** 🤖❗ Max Turns Reached: {e}\n", async_task=async_task, task_id=task_id)
logging.exception(f"Exceeded max_turns: {max_turns}")
logging.exception("Exceeded max_turns: %s", max_turns)
except AgentsException as e:
await render_model_output(f"** 🤖❗ Agent Exception: {e}\n", async_task=async_task, task_id=task_id)
logging.exception("Agent Exception")
Expand Down Expand Up @@ -576,7 +576,7 @@ async def on_handoff_hook(context: RunContextWrapper[TContext], agent: Agent[TCo
inputs_dict=inputs,
)
except jinja2.TemplateError as e:
logging.error(f"Template rendering error: {e}")
logging.error("Template rendering error: %s", e)
raise ValueError(f"Failed to render prompt template: {e}") from e

with TmpEnv(env):
Expand Down Expand Up @@ -657,7 +657,7 @@ async def _deploy(ra: dict, pp: str) -> bool:
complete = True
for result in task_results:
if isinstance(result, Exception):
logging.error(f"Caught exception in Gather: {result}")
logging.error("Caught exception in Gather: %s", result)
result = False
complete = result and complete
return complete
Expand Down Expand Up @@ -690,13 +690,13 @@ async def _deploy(ra: dict, pp: str) -> bool:
f"** 🤖🔄 Task {task_name!r} failed: {exc}\n"
f"** 🤖🔄 Retrying in {backoff}s ({remaining} attempts left)\n"
)
logging.warning(f"Task {task_name!r} attempt {attempt + 1} failed: {exc}")
logging.warning("Task %r attempt %s failed: %s", task_name, attempt + 1, exc)
await asyncio.sleep(backoff)
else:
logging.error(f"Task {task_name!r} failed after {TASK_RETRY_LIMIT} attempts: {exc}")
logging.error("Task %r failed after %s attempts: %s", task_name, TASK_RETRY_LIMIT, exc)
except Exception as exc:
last_task_error = exc
logging.error(f"Task {task_name!r} failed (non-retriable): {exc}")
logging.error("Task %r failed (non-retriable): %s", task_name, exc)
break

# If all retries exhausted with an exception, save and re-raise
Expand Down
4 changes: 2 additions & 2 deletions src/seclab_taskflow_agent/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def save(self) -> Path:
self.updated_at = datetime.now(timezone.utc).isoformat()
path = self.file_path
path.write_text(self.model_dump_json(indent=2))
logging.debug(f"Session checkpoint saved: {path}")
logging.debug("Session checkpoint saved: %s", path)
return path

def record_task(
Expand Down Expand Up @@ -132,5 +132,5 @@ def list_sessions(cls) -> list[TaskflowSession]:
try:
sessions.append(cls.model_validate_json(f.read_text()))
except Exception:
logging.warning(f"Skipping corrupt session file: {f}")
logging.warning("Skipping corrupt session file: %s", f)
return sessions
2 changes: 1 addition & 1 deletion src/seclab_taskflow_agent/shell_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def shell_command_to_string(cmd: list[str]) -> str:
Raises:
RuntimeError: If the command exits with a non-zero return code.
"""
logging.info(f"Executing: {cmd}")
logging.info("Executing: %s", cmd)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8")
stdout, stderr = p.communicate()
p.wait()
Expand Down
Loading