Spaces:
Running
Running
Upload patch_agent_loop.py
Browse files- patch_agent_loop.py +95 -0
patch_agent_loop.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Patch agent_loop.py - detect task incomplete and send event."""
|
| 2 |
+
|
| 3 |
+
AGENT_LOOP = "/app/agent/core/agent_loop.py"
|
| 4 |
+
|
| 5 |
+
def patch():
|
| 6 |
+
import os
|
| 7 |
+
if not os.path.exists(AGENT_LOOP):
|
| 8 |
+
print(f"SKIP: {AGENT_LOOP} not found")
|
| 9 |
+
return
|
| 10 |
+
|
| 11 |
+
with open(AGENT_LOOP, "r", encoding="utf-8") as f:
|
| 12 |
+
content = f.read()
|
| 13 |
+
|
| 14 |
+
# Add import re if needed for detection
|
| 15 |
+
# Add _check_task_incomplete function before class Handlers
|
| 16 |
+
|
| 17 |
+
check_func = '''
|
| 18 |
+
|
| 19 |
+
def _check_task_incomplete(session: Session, llm_result: LLMResult) -> bool:
|
| 20 |
+
"""Check if model stopped streaming but task is incomplete.
|
| 21 |
+
|
| 22 |
+
Returns True when:
|
| 23 |
+
- LLM finished without tool calls
|
| 24 |
+
- But there are unfinished plan items
|
| 25 |
+
"""
|
| 26 |
+
if llm_result.tool_calls_acc:
|
| 27 |
+
return False # Has tool calls, task continues via tool execution
|
| 28 |
+
|
| 29 |
+
plan = getattr(session, "current_plan", None) or []
|
| 30 |
+
unfinished = [item for item in plan if item.get("status") in ("pending", "in_progress")]
|
| 31 |
+
|
| 32 |
+
# Also check if there are any pending actions in the loop
|
| 33 |
+
pending_actions = getattr(session, "pending_actions", None)
|
| 34 |
+
|
| 35 |
+
return len(unfinished) > 0 or bool(pending_actions)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def _unfinished_plan_items(session: Session) -> list[dict[str, str]]:
|
| 39 |
+
"""Helper to get unfinished plan items."""
|
| 40 |
+
plan = getattr(session, "current_plan", None) or []
|
| 41 |
+
return [item for item in plan if item.get("status") in ("pending", "in_progress")]
|
| 42 |
+
|
| 43 |
+
'''
|
| 44 |
+
|
| 45 |
+
# Insert after LLMResult dataclass definition and before Handlers class
|
| 46 |
+
if "_check_task_incomplete" not in content:
|
| 47 |
+
content = content.replace(
|
| 48 |
+
"class Handlers:",
|
| 49 |
+
check_func + "\n\nclass Handlers:"
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
# Now we need to add the call in the run_agent method
|
| 53 |
+
# Find where LLM result is processed and check for incomplete task
|
| 54 |
+
check_call = '''
|
| 55 |
+
# === Check for incomplete task after LLM response ===
|
| 56 |
+
if not llm_result.tool_calls_acc and llm_result.content:
|
| 57 |
+
# Model finished without tool calls - check if task is done
|
| 58 |
+
unfinished = _unfinished_plan_items(session)
|
| 59 |
+
if unfinished:
|
| 60 |
+
await session.send_event(
|
| 61 |
+
Event(
|
| 62 |
+
event_type="task_incomplete",
|
| 63 |
+
data={
|
| 64 |
+
"incomplete_plan": unfinished,
|
| 65 |
+
"message": "Model phản hồi đã dừng nhưng nhiệm vụ chưa hoàn thành."
|
| 66 |
+
}
|
| 67 |
+
)
|
| 68 |
+
)
|
| 69 |
+
'''
|
| 70 |
+
|
| 71 |
+
# Insert this check after processing tool calls but before final_response check
|
| 72 |
+
# Look for pattern: iteration increment and session updates
|
| 73 |
+
if "task_incomplete" not in content:
|
| 74 |
+
# Find location after tool_calls processing
|
| 75 |
+
insert_marker = " # -- End of turn --"
|
| 76 |
+
if insert_marker in content:
|
| 77 |
+
content = content.replace(
|
| 78 |
+
insert_marker,
|
| 79 |
+
check_call + "\n" + insert_marker
|
| 80 |
+
)
|
| 81 |
+
else:
|
| 82 |
+
# Try another location - after llm_result processing
|
| 83 |
+
result_marker = "final_response = llm_result.content or None"
|
| 84 |
+
if result_marker in content:
|
| 85 |
+
content = content.replace(
|
| 86 |
+
result_marker,
|
| 87 |
+
result_marker + "\n" + check_call
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
with open(AGENT_LOOP, "w", encoding="utf-8") as f:
|
| 91 |
+
f.write(content)
|
| 92 |
+
print(f"OK: Patched {AGENT_LOOP}")
|
| 93 |
+
|
| 94 |
+
if __name__ == "__main__":
|
| 95 |
+
patch()
|