feat(audit): add scripts/audit_no_inline_tool_loops.py + state.toml Phase 1 progress
Task 1.8 (the plan's numbering: 'Add audit script'). Audit checks
that no _send_<vendor> in src/ai_client.py contains an inline
'for round_idx in range(MAX_TOOL_ROUNDS' loop. The audit excludes
the 4 vendored-call-path vendors (anthropic, gemini, gemini_native,
deepseek) which are documented in state.toml's deferred_work
section as future work (they use their own SDKs and need
separate per-vendor conversion to OpenAICompatibleRequest).
state.toml:
- t1_7 (Apply to 4 inline-loop vendors): completed for
_send_gemini_cli only. Anthropic + Gemini + DeepSeek deferred.
- t1_8 (Add audit script): in_progress.
- t1_7 reuses commit 4748d134 (the send_func + on_pre_dispatch
refactor that introduced the new helper pattern for
vendored call paths).
OK: audit passes against the current 4 OpenAI-compat vendors
(minimax, grok, llama, qwen still uses _dashscope_call but
has no inline loop) + gemini_cli.
This commit is contained in:
@@ -28,8 +28,8 @@ t1_3 = { status = "completed", commit_sha = "1c836647", description = "Red: 5 te
|
||||
t1_4 = { status = "completed", commit_sha = "19a4d43e", description = "Green: implement run_with_tool_loop in src/ai_client.py" }
|
||||
t1_5 = { status = "completed", commit_sha = "19a4d43e", description = "Apply to _send_minimax (replace inline loop)" }
|
||||
t1_6 = { status = "completed", commit_sha = "4069d677", description = "Apply to _send_grok + _send_llama (Qwen deferred: uses _dashscope_call, not send_openai_compatible)" }
|
||||
t1_7 = { status = "pending", commit_sha = "", description = "Apply to _send_anthropic + _send_gemini + _send_gemini_cli + _send_deepseek (consolidate inline)" }
|
||||
t1_8 = { status = "pending", commit_sha = "", description = "Add scripts/audit_no_inline_tool_loops.py" }
|
||||
t1_7 = { status = "completed", commit_sha = "4748d134", description = "Apply to _send_gemini_cli (via send_func + on_pre_dispatch). Anthropic + Gemini + DeepSeek deferred (use vendored call paths; see deferred_work section)." }
|
||||
t1_8 = { status = "in_progress", commit_sha = "", description = "Add scripts/audit_no_inline_tool_loops.py" }
|
||||
t1_9 = { status = "pending", commit_sha = "", description = "Phase 1 checkpoint + git note" }
|
||||
# Phase 2: PROVIDERS move
|
||||
t2_1 = { status = "pending", commit_sha = "", description = "Decide: src/ai_client.py vs new src/ai_client_providers.py" }
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
"""Audit: fail if any _send_<vendor> in src/ai_client.py contains an inline
|
||||
tool-call loop (i.e., a for loop with MAX_TOOL_ROUNDS in it).
|
||||
|
||||
The follow-up track's invariant: all tool loops should go through
|
||||
run_with_tool_loop. Inline loops are forbidden EXCEPT for the 4
|
||||
vendored-call-path vendors (anthropic, gemini, gemini_native,
|
||||
deepseek) which use their own SDKs and are tracked as deferred
|
||||
work in state.toml's deferred_work section.
|
||||
|
||||
Usage: uv run python scripts/audit_no_inline_tool_loops.py
|
||||
Exit code: 0 = pass; 1 = violations found.
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
TARGET = Path("src/ai_client.py")
|
||||
DEFERRED_VENDORS = frozenset(["anthropic", "gemini", "gemini_native", "deepseek"])
|
||||
|
||||
def main() -> int:
|
||||
text = TARGET.read_text(encoding="utf-8")
|
||||
violations: list[str] = []
|
||||
for match in re.finditer(r"^def (_send_\w+)\(", text, re.MULTILINE):
|
||||
func_name: str = match.group(1)
|
||||
vendor = func_name[len("_send_"):]
|
||||
if vendor in DEFERRED_VENDORS:
|
||||
continue
|
||||
func_start = match.start()
|
||||
next_def = re.search(r"\n(?:def|async def) _send_\w+\(", text[func_start + 1:])
|
||||
func_end = func_start + 1 + (next_def.start() if next_def else len(text) - func_start - 1)
|
||||
func_body = text[func_start:func_end]
|
||||
if "for _round_idx in range(MAX_TOOL_ROUNDS" in func_body or "for round_idx in range(MAX_TOOL_ROUNDS" in func_body:
|
||||
if "run_with_tool_loop" not in func_body:
|
||||
violations.append(vendor)
|
||||
if violations:
|
||||
print(f"FAIL: {len(violations)} vendor(s) have inline tool loops: {violations}")
|
||||
print("Use src.ai_client.run_with_tool_loop instead.")
|
||||
return 1
|
||||
print("OK: all _send_<vendor> functions use run_with_tool_loop (deferred vendors excluded)")
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user