|
115 | 115 | - Resolution is the diffraction limit (d_min), typically 1.5-4.0 Å. Only extract as resolution if explicitly stated as resolution/d_min. |
116 | 116 | - If the text says "Resolution limit: Not mentioned" or similar, do NOT extract a resolution value from anywhere else in the text. |
117 | 117 |
|
118 | | -**CRITICAL: max_refine_cycles vs after_program** |
| 118 | +**CRITICAL: max_refine_cycles vs after_program vs after_cycle** |
119 | 119 | - max_refine_cycles=N: Limits the NUMBER of refinement jobs to N. The workflow continues normally until refinement, then stops after N refinement jobs. |
120 | 120 | - after_program="X": FORCES program X to be run IMMEDIATELY, bypassing normal workflow. Only use when user wants to skip directly to a specific program. |
| 121 | +- after_cycle=N: Stops after N AGENT CYCLES (each cycle = one program execution). ONLY use when user says "stop after N cycles" with an explicit number. |
121 | 122 | - "maximum of one refinement" or "at most one refinement" → ONLY set max_refine_cycles=1, do NOT set after_program |
122 | 123 | - "solve the structure with one refinement" → max_refine_cycles=1 (workflow proceeds normally: xtriage → model → refine) |
| 124 | +- "stop after refinement" or "stop after one refinement" → max_refine_cycles=1, skip_validation=true |
123 | 125 | - "just run refinement" or "only refinement" → after_program="phenix.refine" (skip to refinement immediately) |
| 126 | +- Do NOT use after_cycle for "stop after refinement" — that would stop after the first agent cycle (e.g., xtriage), not after refinement. |
124 | 127 |
|
125 | 128 | **CRITICAL: skip_validation RULE** |
126 | 129 | If the user specifies ANY explicit stop condition (like "stop after X" or "Stop Condition: ..."), |
@@ -851,6 +854,10 @@ def _log(msg): |
851 | 854 | # e.g., after_program=phenix.map_symmetry but constraints say "build a model" → don't stop |
852 | 855 | validated = _fix_multi_step_workflow_conflict(validated, _log) |
853 | 856 |
|
| 857 | + # Fix after_cycle=1 when max_refine_cycles is set |
| 858 | + # LLM often confuses "one refinement cycle" with "one agent cycle" |
| 859 | + validated = _fix_after_cycle_refinement_conflict(validated, _log) |
| 860 | + |
854 | 861 | return validated |
855 | 862 |
|
856 | 863 |
|
@@ -1023,6 +1030,47 @@ def _fix_multi_step_workflow_conflict(directives, log): |
1023 | 1030 | return directives |
1024 | 1031 |
|
1025 | 1032 |
|
| 1033 | +def _fix_after_cycle_refinement_conflict(directives, log): |
| 1034 | + """ |
| 1035 | + Fix conflict where LLM sets after_cycle=1 alongside max_refine_cycles. |
| 1036 | +
|
| 1037 | + When user says "stop after refinement" or "one refinement cycle", the LLM |
| 1038 | + sometimes produces both max_refine_cycles=1 AND after_cycle=1. The after_cycle=1 |
| 1039 | + is wrong — it would stop after the first agent cycle (e.g., xtriage), not after |
| 1040 | + the refinement program completes. |
| 1041 | +
|
| 1042 | + Rules: |
| 1043 | + - after_cycle=1 + max_refine_cycles=N → remove after_cycle (keep max_refine_cycles) |
| 1044 | + - after_cycle=1 alone (no max_refine_cycles, no after_program) → suspicious, |
| 1045 | + only keep if no workflow programs would run before refinement |
| 1046 | + """ |
| 1047 | + stop_conditions = directives.get("stop_conditions", {}) |
| 1048 | + if not stop_conditions: |
| 1049 | + return directives |
| 1050 | + |
| 1051 | + after_cycle = stop_conditions.get("after_cycle") |
| 1052 | + max_refine = stop_conditions.get("max_refine_cycles") |
| 1053 | + after_program = stop_conditions.get("after_program") |
| 1054 | + |
| 1055 | + # Case 1: after_cycle + max_refine_cycles — the after_cycle is redundant/wrong |
| 1056 | + if after_cycle is not None and max_refine is not None: |
| 1057 | + log("DIRECTIVES: Removing after_cycle=%d (conflicts with max_refine_cycles=%d)" % |
| 1058 | + (after_cycle, max_refine)) |
| 1059 | + log("DIRECTIVES: (LLM confused 'one refinement' with 'one agent cycle')") |
| 1060 | + del directives["stop_conditions"]["after_cycle"] |
| 1061 | + |
| 1062 | + # Case 2: after_cycle=1 alone (no after_program, no max_refine) |
| 1063 | + # This almost always means the LLM misinterpreted "stop after refinement" |
| 1064 | + # as "stop after 1 cycle". Convert to max_refine_cycles=1. |
| 1065 | + elif after_cycle == 1 and after_program is None and max_refine is None: |
| 1066 | + log("DIRECTIVES: Converting after_cycle=1 → max_refine_cycles=1") |
| 1067 | + log("DIRECTIVES: (after_cycle=1 would stop after xtriage, not after refinement)") |
| 1068 | + del directives["stop_conditions"]["after_cycle"] |
| 1069 | + directives["stop_conditions"]["max_refine_cycles"] = 1 |
| 1070 | + |
| 1071 | + return directives |
| 1072 | + |
| 1073 | + |
1026 | 1074 | def _fix_program_name(name): |
1027 | 1075 | """ |
1028 | 1076 | Try to fix common variations in program names. |
|
0 commit comments