Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -520,7 +520,21 @@ def should_continue(state: AgentState):
|
|
| 520 |
print("--- Condition: Tools called, routing to tools node. ---")
|
| 521 |
return "tools"
|
| 522 |
|
| 523 |
-
# 4.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 524 |
print(f"--- Condition: No tool call (Turn {current_turn}). Continuing to agent. ---")
|
| 525 |
return "agent"
|
| 526 |
|
|
@@ -614,7 +628,7 @@ Your goal: Provide the EXACT answer in the EXACT format requested.
|
|
| 614 |
chat_llm = ChatGroq(
|
| 615 |
temperature=0, # Maximum determinism
|
| 616 |
groq_api_key=GROQ_API_KEY,
|
| 617 |
-
model_name="
|
| 618 |
max_tokens=4096,
|
| 619 |
timeout=60
|
| 620 |
)
|
|
@@ -627,10 +641,33 @@ Your goal: Provide the EXACT answer in the EXACT format requested.
|
|
| 627 |
print("✅ Tools bound to LLM")
|
| 628 |
|
| 629 |
# --- Agent Node ---
|
| 630 |
-
|
| 631 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 632 |
print(f"\n{'='*60}")
|
| 633 |
print(f"AGENT TURN {current_turn}/{MAX_TURNS}")
|
|
|
|
|
|
|
| 634 |
print('='*60)
|
| 635 |
|
| 636 |
messages_to_send = state["messages"]
|
|
@@ -651,27 +688,29 @@ Your goal: Provide the EXACT answer in the EXACT format requested.
|
|
| 651 |
)
|
| 652 |
return {"messages": [error_msg], "turn": current_turn}
|
| 653 |
time.sleep(2 ** attempt) # Exponential backoff
|
|
|
|
| 654 |
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
| 655 |
-
# ---
|
| 656 |
#
|
| 657 |
-
#
|
| 658 |
-
#
|
| 659 |
-
# strip the tool call and send a correction message.
|
| 660 |
if current_turn == 1 and ai_message.tool_calls:
|
| 661 |
print("⚠️ AGENT VIOLATION: Tried to call tools on Turn 1. Forcing replan.")
|
| 662 |
|
| 663 |
# Strip the illegal tool call
|
| 664 |
ai_message.tool_calls = []
|
| 665 |
|
| 666 |
-
# Create
|
| 667 |
correction_message = SystemMessage(
|
| 668 |
content="SYSTEM: Protocol Violation. Your FIRST turn MUST be a plan with NO tool calls. "
|
| 669 |
"You are not allowed to call any tools on your first turn. "
|
| 670 |
"Re-read the protocol and provide your 2-3 sentence plan now."
|
| 671 |
)
|
| 672 |
|
| 673 |
-
# Return the
|
| 674 |
-
|
|
|
|
|
|
|
| 675 |
# --- END OF RULE ENFORCEMENT BLOCK ---
|
| 676 |
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
| 677 |
# --- FIX #2: REPLACE THE FALLBACK PARSING BLOCK ---
|
|
|
|
| 520 |
print("--- Condition: Tools called, routing to tools node. ---")
|
| 521 |
return "tools"
|
| 522 |
|
| 523 |
+
# 4. NEW LOOP PREVENTION:
|
| 524 |
+
# Check for consecutive AI messages without tool calls.
|
| 525 |
+
# This catches "thinking" loops or raw answer dribbling (like "58").
|
| 526 |
+
if isinstance(last_message, AIMessage) and not last_message.tool_calls:
|
| 527 |
+
# Check if the message *before* this one was ALSO an AIMessage.
|
| 528 |
+
# We need at least 3 messages total (System, Human, AI-Turn1-Plan)
|
| 529 |
+
# for this check to be valid, so we check len > 2.
|
| 530 |
+
if len(state['messages']) > 2 and isinstance(state['messages'][-2], AIMessage):
|
| 531 |
+
print(f"--- Condition: Detected 2+ consecutive AI messages (Turn {current_turn}). Ending to prevent loop. ---")
|
| 532 |
+
state['messages'].append(
|
| 533 |
+
SystemMessage(content=f"SYSTEM: Agent stuck in a loop (consecutive non-tool-call AI messages). Ending execution.")
|
| 534 |
+
)
|
| 535 |
+
return END
|
| 536 |
+
|
| 537 |
+
# 5. Default: Loop back to agent (e.g., after Turn 1 plan)
|
| 538 |
print(f"--- Condition: No tool call (Turn {current_turn}). Continuing to agent. ---")
|
| 539 |
return "agent"
|
| 540 |
|
|
|
|
| 628 |
chat_llm = ChatGroq(
|
| 629 |
temperature=0, # Maximum determinism
|
| 630 |
groq_api_key=GROQ_API_KEY,
|
| 631 |
+
model_name="qwen/qwen3-32b", # Best reasoning model
|
| 632 |
max_tokens=4096,
|
| 633 |
timeout=60
|
| 634 |
)
|
|
|
|
| 641 |
print("✅ Tools bound to LLM")
|
| 642 |
|
| 643 |
# --- Agent Node ---
|
| 644 |
+
def agent_node(state: AgentState):
|
| 645 |
+
|
| 646 |
+
# --- Turn Counter Logic ---
|
| 647 |
+
# We need to check if this is a retry of a failed turn (e.g., Turn 1 violation)
|
| 648 |
+
# We identify a retry if the *last* message was our "Protocol Violation" message
|
| 649 |
+
last_msg = state['messages'][-1]
|
| 650 |
+
is_a_retry = False
|
| 651 |
+
if isinstance(last_msg, SystemMessage) and "Protocol Violation" in last_msg.content:
|
| 652 |
+
is_a_retry = True
|
| 653 |
+
|
| 654 |
+
# Get the state's current turn number
|
| 655 |
+
current_turn = state.get('turn', 0)
|
| 656 |
+
|
| 657 |
+
# If this is NOT a retry, increment the turn.
|
| 658 |
+
# If it IS a retry, we *stay on the same turn number*
|
| 659 |
+
if not is_a_retry:
|
| 660 |
+
current_turn += 1
|
| 661 |
+
|
| 662 |
+
# Handle the very first run (where state['turn'] is 0)
|
| 663 |
+
if current_turn == 0:
|
| 664 |
+
current_turn = 1
|
| 665 |
+
# --- End Turn Counter Logic ---
|
| 666 |
+
|
| 667 |
print(f"\n{'='*60}")
|
| 668 |
print(f"AGENT TURN {current_turn}/{MAX_TURNS}")
|
| 669 |
+
if is_a_retry:
|
| 670 |
+
print("--- (Re-trying after protocol violation) ---")
|
| 671 |
print('='*60)
|
| 672 |
|
| 673 |
messages_to_send = state["messages"]
|
|
|
|
| 688 |
)
|
| 689 |
return {"messages": [error_msg], "turn": current_turn}
|
| 690 |
time.sleep(2 ** attempt) # Exponential backoff
|
| 691 |
+
|
| 692 |
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
| 693 |
+
# --- (FIX #1) RULE ENFORCEMENT BLOCK ---
|
| 694 |
#
|
| 695 |
+
# If it's Turn 1 AND the agent tried to call tools, we reject it
|
| 696 |
+
# and force it to re-do Turn 1.
|
|
|
|
| 697 |
if current_turn == 1 and ai_message.tool_calls:
|
| 698 |
print("⚠️ AGENT VIOLATION: Tried to call tools on Turn 1. Forcing replan.")
|
| 699 |
|
| 700 |
# Strip the illegal tool call
|
| 701 |
ai_message.tool_calls = []
|
| 702 |
|
| 703 |
+
# Create the correction message that forces the plan
|
| 704 |
correction_message = SystemMessage(
|
| 705 |
content="SYSTEM: Protocol Violation. Your FIRST turn MUST be a plan with NO tool calls. "
|
| 706 |
"You are not allowed to call any tools on your first turn. "
|
| 707 |
"Re-read the protocol and provide your 2-3 sentence plan now."
|
| 708 |
)
|
| 709 |
|
| 710 |
+
# Return the messages.
|
| 711 |
+
# Critically, we set the state's turn counter back to 1.
|
| 712 |
+
# This ensures the *next* run of this node is *still* Turn 1.
|
| 713 |
+
return {"messages": [ai_message, correction_message], "turn": 1}
|
| 714 |
# --- END OF RULE ENFORCEMENT BLOCK ---
|
| 715 |
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
| 716 |
# --- FIX #2: REPLACE THE FALLBACK PARSING BLOCK ---
|