Skip to content

Commit a147e9e

Browse files
committed
Address second round of review feedback
- capi: unknown endpoint fallback now type-checks response before iterating, avoids AttributeError on dict responses without data key - render_utils: flush_async_output is a no-op when no buffer exists, prevents masking the real error when an async agent fails early - session: list_sessions sorts by file mtime instead of filename so most-recent-first ordering is actually correct - runner: replace confusing issubset(set()) idiom with straightforward set difference check for unknown model settings keys - runner: catch JSONDecodeError on outer tool result parse separately from the inner text parse so malformed results get a clear error - tests: suppress S105 false positive on test token assertion
1 parent 69fc1e3 commit a147e9e

5 files changed

Lines changed: 30 additions & 20 deletions

File tree

src/seclab_taskflow_agent/capi.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,14 @@ def list_capi_models(token: str) -> dict[str, dict]:
9999
case AI_API_ENDPOINT_ENUM.AI_API_OPENAI:
100100
models_list = r.json().get("data", [])
101101
case _:
102-
# Unknown endpoint — try OpenAI-style {"data": [...]}
102+
# Unknown endpoint — try common response shapes
103103
body = r.json()
104-
models_list = body.get("data", body) if isinstance(body, dict) else body
104+
if isinstance(body, dict):
105+
models_list = body.get("data", [])
106+
elif isinstance(body, list):
107+
models_list = body
108+
else:
109+
models_list = []
105110
for model in models_list:
106111
models[model.get("id")] = dict(model)
107112
except httpx.RequestError:

src/seclab_taskflow_agent/render_utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ async def flush_async_output(task_id: str) -> None:
2424
"""Flush buffered async output for *task_id* to the console."""
2525
async with async_output_lock:
2626
if task_id not in async_output:
27-
raise ValueError(f"No async output for task: {task_id}")
28-
data = async_output[task_id]
29-
del async_output[task_id]
27+
# No buffered output (agent may have failed before producing any).
28+
return
29+
data = async_output.pop(task_id)
3030
await render_model_output(f"** 🤖✏️ Output for async task: {task_id}\n\n")
3131
await render_model_output(data)
3232

src/seclab_taskflow_agent/runner.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,10 @@ def _resolve_model_config(
8080
models_params: dict[str, dict[str, Any]] = m_config.model_settings or {}
8181
if models_params and not isinstance(models_params, dict):
8282
raise ValueError(f"Settings section of model_config file {model_config_ref} must be a dictionary")
83-
if not set(models_params.keys()).difference(model_keys).issubset(set()):
83+
unknown = set(models_params) - set(model_keys)
84+
if unknown:
8485
raise ValueError(
85-
f"Settings section of model_config file {model_config_ref} contains models not in the model section"
86+
f"Settings section of model_config file {model_config_ref} contains models not in the model section: {unknown}"
8687
)
8788
for k, v in models_params.items():
8889
if not isinstance(v, dict):
@@ -199,20 +200,24 @@ async def _build_prompts_to_run(
199200
logging.warning("repeat_prompt enabled but no {{ result }} in prompt")
200201
try:
201202
last_result = json.loads(last_mcp_tool_results[-1])
202-
text = last_result.get("text", "")
203-
try:
204-
iterable_result = json.loads(text)
205-
except json.JSONDecodeError as exc:
206-
logging.critical(f"Could not parse result text: {text}")
207-
raise ValueError("Result text is not valid JSON") from exc
208-
try:
209-
iter(iterable_result)
210-
except TypeError:
211-
logging.critical("Last MCP tool result is not iterable")
212-
raise
213203
except IndexError:
214204
logging.critical("No last MCP tool result available")
215205
raise
206+
except json.JSONDecodeError as exc:
207+
logging.critical(f"Could not parse tool result as JSON: {last_mcp_tool_results[-1][:200]}")
208+
raise ValueError("Tool result is not valid JSON") from exc
209+
210+
text = last_result.get("text", "")
211+
try:
212+
iterable_result = json.loads(text)
213+
except json.JSONDecodeError as exc:
214+
logging.critical(f"Could not parse result text: {text}")
215+
raise ValueError("Result text is not valid JSON") from exc
216+
try:
217+
iter(iterable_result)
218+
except TypeError:
219+
logging.critical("Last MCP tool result is not iterable")
220+
raise
216221

217222
if not iterable_result:
218223
await render_model_output("** 🤖❗MCP tool result iterable is empty!\n")

src/seclab_taskflow_agent/session.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def load(cls, session_id: str) -> TaskflowSession:
128128
def list_sessions(cls) -> list[TaskflowSession]:
129129
"""List all saved sessions, most recent first."""
130130
sessions: list[TaskflowSession] = []
131-
for f in sorted(session_dir().glob("*.json"), reverse=True):
131+
for f in sorted(session_dir().glob("*.json"), key=lambda p: p.stat().st_mtime, reverse=True):
132132
try:
133133
sessions.append(cls.model_validate_json(f.read_text()))
134134
except Exception:

tests/test_runner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ def test_engine_keys_extracted(self):
237237
)
238238
assert api_type == "responses"
239239
assert endpoint == "https://custom.api"
240-
assert token == "secret"
240+
assert token == "secret" # noqa: S105
241241
assert "api_type" not in settings
242242
assert "endpoint" not in settings
243243
assert "token" not in settings

0 commit comments

Comments
 (0)