Spaces:
Paused
Paused
Update graph.py
Browse files
graph.py
CHANGED
|
@@ -411,61 +411,78 @@ def run_intent_agent(state: AgentState):
|
|
| 411 |
|
| 412 |
def run_pm_agent(state: AgentState):
|
| 413 |
log.info("--- PM ---")
|
| 414 |
-
|
| 415 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 416 |
path = ensure_list(state, 'execution_path') + ["PM"]
|
| 417 |
-
|
|
|
|
|
|
|
| 418 |
context_parts = [
|
| 419 |
f"=== USER REQUEST ===\n{state.get('userInput', '')}",
|
| 420 |
f"\n=== OBJECTIVE ===\n{state.get('coreObjectivePrompt', '')}",
|
| 421 |
f"\n=== MEMORY ===\n{state.get('retrievedMemory', 'None')}",
|
| 422 |
]
|
| 423 |
-
|
| 424 |
if state.get('qaFeedback'):
|
| 425 |
context_parts.append(f"\n=== QA FEEDBACK (MUST FIX) ===\n{state.get('qaFeedback')}")
|
| 426 |
context_parts.append(f"\n=== PREVIOUS PLAN ===\n{json.dumps(state.get('pmPlan', {}).get('plan_steps', []), indent=2)}")
|
| 427 |
-
|
| 428 |
full_context = "\n".join(context_parts)
|
| 429 |
-
|
| 430 |
prompt = f"""Create DETAILED, EXECUTABLE plan.
|
| 431 |
|
| 432 |
{full_context}
|
| 433 |
|
| 434 |
-
|
| 435 |
-
- State EXACTLY what will be created/analyzed
|
| 436 |
-
- Specify WHAT data/information will be used
|
| 437 |
-
- Define WHAT methods will be applied
|
| 438 |
-
|
| 439 |
-
JSON format:
|
| 440 |
{{
|
| 441 |
-
"plan_steps": [
|
| 442 |
"experiment_needed": true/false,
|
| 443 |
"experiment_type": "notebook|script|excel|word|pdf|repo",
|
| 444 |
-
"experiment_goal": "
|
| 445 |
-
"key_requirements": [
|
| 446 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 447 |
|
| 448 |
-
Be specific about using uploaded files, implementing algorithms, creating schemas."""
|
| 449 |
-
|
| 450 |
-
response = llm.invoke(prompt)
|
| 451 |
-
plan = parse_json_from_llm(getattr(response, "content", "") or "")
|
| 452 |
-
|
| 453 |
if not plan:
|
| 454 |
detection = detect_requested_output_types(state.get('userInput', ''))
|
| 455 |
plan = {
|
| 456 |
"plan_steps": ["Analyze request", "Process information", "Create deliverable", "Review"],
|
| 457 |
"experiment_needed": detection.get('requires_artifact', False),
|
| 458 |
"experiment_type": detection.get('artifact_type', 'word'),
|
| 459 |
-
"experiment_goal": state.get('coreObjectivePrompt', state.get('userInput', ''))
|
|
|
|
| 460 |
}
|
| 461 |
-
|
|
|
|
| 462 |
exp_type = normalize_experiment_type(plan.get('experiment_type'), plan.get('experiment_goal',''))
|
| 463 |
plan['experiment_type'] = exp_type
|
| 464 |
-
|
| 465 |
if plan.get('experiment_needed') and not plan.get('experiment_goal'):
|
| 466 |
plan['experiment_goal'] = state.get('userInput','')
|
| 467 |
-
|
| 468 |
-
|
|
|
|
|
|
|
|
|
|
| 469 |
|
| 470 |
def _extract_code_blocks(text: str, lang_hint: Optional[str]=None) -> List[str]:
|
| 471 |
if lang_hint and "python" in (lang_hint or "").lower():
|
|
|
|
| 411 |
|
| 412 |
def run_pm_agent(state: AgentState):
|
| 413 |
log.info("--- PM ---")
|
| 414 |
+
# Ensure keys
|
| 415 |
+
current_rework = ensure_int(state, 'rework_cycles', 0)
|
| 416 |
+
max_loops_val = ensure_int(state, 'max_loops', INITIAL_MAX_REWORK_CYCLES)
|
| 417 |
+
|
| 418 |
+
# If we've exhausted loops, short-circuit and produce fallback plan with a note
|
| 419 |
+
if current_rework > max_loops_val:
|
| 420 |
+
path = ensure_list(state, 'execution_path') + ["PM"]
|
| 421 |
+
fallback_plan = {
|
| 422 |
+
"plan_steps": ["Rework limit exceeded. Manual review required."],
|
| 423 |
+
"experiment_needed": False,
|
| 424 |
+
"experiment_type": "word",
|
| 425 |
+
"experiment_goal": state.get('coreObjectivePrompt', state.get('userInput',''))
|
| 426 |
+
}
|
| 427 |
+
return {"pmPlan": fallback_plan, "execution_path": path, "rework_cycles": current_rework, "status_update": "Rework limit hit - manual review"}
|
| 428 |
+
|
| 429 |
+
# Normal behavior: increment rework count for this pass
|
| 430 |
+
current_cycles = current_rework + 1
|
| 431 |
path = ensure_list(state, 'execution_path') + ["PM"]
|
| 432 |
+
|
| 433 |
+
# (rest of your original PM prompt & parse flow, but ensure the output sets rework_cycles and max_loops)
|
| 434 |
+
# --- build full_context like before ---
|
| 435 |
context_parts = [
|
| 436 |
f"=== USER REQUEST ===\n{state.get('userInput', '')}",
|
| 437 |
f"\n=== OBJECTIVE ===\n{state.get('coreObjectivePrompt', '')}",
|
| 438 |
f"\n=== MEMORY ===\n{state.get('retrievedMemory', 'None')}",
|
| 439 |
]
|
|
|
|
| 440 |
if state.get('qaFeedback'):
|
| 441 |
context_parts.append(f"\n=== QA FEEDBACK (MUST FIX) ===\n{state.get('qaFeedback')}")
|
| 442 |
context_parts.append(f"\n=== PREVIOUS PLAN ===\n{json.dumps(state.get('pmPlan', {}).get('plan_steps', []), indent=2)}")
|
|
|
|
| 443 |
full_context = "\n".join(context_parts)
|
| 444 |
+
|
| 445 |
prompt = f"""Create DETAILED, EXECUTABLE plan.
|
| 446 |
|
| 447 |
{full_context}
|
| 448 |
|
| 449 |
+
Return JSON with:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
{{
|
| 451 |
+
"plan_steps": [...],
|
| 452 |
"experiment_needed": true/false,
|
| 453 |
"experiment_type": "notebook|script|excel|word|pdf|repo",
|
| 454 |
+
"experiment_goal": "...",
|
| 455 |
+
"key_requirements": [...]
|
| 456 |
}}
|
| 457 |
+
Be concrete.
|
| 458 |
+
"""
|
| 459 |
+
try:
|
| 460 |
+
response = llm.invoke(prompt)
|
| 461 |
+
plan = parse_json_from_llm(getattr(response, "content", "") or "")
|
| 462 |
+
except Exception as e:
|
| 463 |
+
log.warning("PM LLM failed: %s", e)
|
| 464 |
+
plan = None
|
| 465 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 466 |
if not plan:
|
| 467 |
detection = detect_requested_output_types(state.get('userInput', ''))
|
| 468 |
plan = {
|
| 469 |
"plan_steps": ["Analyze request", "Process information", "Create deliverable", "Review"],
|
| 470 |
"experiment_needed": detection.get('requires_artifact', False),
|
| 471 |
"experiment_type": detection.get('artifact_type', 'word'),
|
| 472 |
+
"experiment_goal": state.get('coreObjectivePrompt', state.get('userInput', '')),
|
| 473 |
+
"key_requirements": []
|
| 474 |
}
|
| 475 |
+
|
| 476 |
+
# Normalize experiment_type
|
| 477 |
exp_type = normalize_experiment_type(plan.get('experiment_type'), plan.get('experiment_goal',''))
|
| 478 |
plan['experiment_type'] = exp_type
|
|
|
|
| 479 |
if plan.get('experiment_needed') and not plan.get('experiment_goal'):
|
| 480 |
plan['experiment_goal'] = state.get('userInput','')
|
| 481 |
+
|
| 482 |
+
# Attach loop control info
|
| 483 |
+
plan['max_loops_initial'] = max_loops_val
|
| 484 |
+
plan['estimated_cost_usd'] = plan.get('estimated_cost_usd', plan.get('estimated_cost_usd', 0.0))
|
| 485 |
+
return {"pmPlan": plan, "execution_path": path, "rework_cycles": current_cycles, "max_loops": max_loops_val, "status_update": f"Plan created ({len(plan.get('plan_steps', []))} steps)"}
|
| 486 |
|
| 487 |
def _extract_code_blocks(text: str, lang_hint: Optional[str]=None) -> List[str]:
|
| 488 |
if lang_hint and "python" in (lang_hint or "").lower():
|