Implement two-pass advisor: separate analysis and repair steps

Full mode now runs two sequential LLM calls per generation:

Pass 1 (analysis): a focused prompt asks the model to identify exactly
what needs to change — what to add, what to remove, what to fix — and
output a single DIAGNOSTIC line. Both user feedback and feedback history
are present. Max tokens is halved since only one line is needed.

Pass 2 (repair): builds on pass 1 by including the analysis result as
"Analysis of what needs to change:" before the task instruction. The
model applies the analysis rather than having to simultaneously diagnose
and rewrite in a single response.

Only diagnostics mode runs only pass 1, stores the analysis in
advisor_feedback_history as an advisor-sourced entry (labelled
"Advisor note:" in future history context), and returns without
modifying the prompt — making it useful as a genuine preview step
before switching to Full mode.

Only prompt mode runs only pass 2 directly (no analysis overhead),
unchanged from before.

Both passes receive feedback_prompt and feedback_history.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-05-15 19:19:35 +03:00
co-authored by Claude Sonnet 4.6
parent 462beda1a1
commit c473502acf
2 changed files with 107 additions and 23 deletions
+106 -22
View File
@@ -8806,6 +8806,20 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
"feedback": feedback,
"rating": str(rating_label or "").strip(),
"iteration": int(iter_num),
"source": "user",
})
global_state["advisor_feedback_history"] = history[-limit:]
def _v2_record_advisor_diagnostic(self, global_state, diagnostic, rating_label, iter_num, limit=10):
diagnostic = str(diagnostic or "").strip()
if not diagnostic:
return
history = list(global_state.setdefault("advisor_feedback_history", []))
history.append({
"feedback": diagnostic,
"rating": str(rating_label or "").strip(),
"iteration": int(iter_num),
"source": "advisor",
})
global_state["advisor_feedback_history"] = history[-limit:]
@@ -8820,10 +8834,52 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
if not feedback:
continue
rating = str(entry.get("rating", "")).strip()
entry_text = f"{rating}: {feedback}" if rating else feedback
source = str(entry.get("source", "user")).strip()
if source == "advisor":
entry_text = f"Advisor note: {feedback}"
else:
entry_text = f"{rating}: {feedback}" if rating else feedback
lines.append(f"{i}. {entry_text}")
return "\n".join(lines)
def _v2_advisor_analysis_prompt(
self,
prompt,
intent_prompt,
repair_candidates,
previous_run=None,
feedback_prompt="",
feedback_history="",
):
has_feedback = bool(str(feedback_prompt or "").strip())
previous_run = previous_run if isinstance(previous_run, dict) else {}
previous_prompt = self._v2_prompt_key(previous_run.get("encoded_prompt", "") or previous_run.get("prompt", ""))
analysis_system = (
"You are analyzing a video generation prompt to identify specific improvements needed.\n\n"
"Output exactly one line:\n"
"DIAGNOSTIC: a concise list of specific changes — what to add, what to remove, what to fix. "
"Be concrete. Reference actual words from the prompt."
)
if has_feedback:
analysis_system += "\n\n" + V2_PROMPT_ADVISOR_FEEDBACK_OVERRIDE.strip()
user_lines = []
if has_feedback:
user_lines.append(f"User feedback: {str(feedback_prompt).strip()}")
user_lines.append(f"Current prompt: {self._v2_prompt_key(prompt)}")
user_lines.append(f"User intent: {self._v2_prompt_key(intent_prompt) or 'same as current prompt'}")
if previous_prompt:
user_lines.append(f"Previous prompt (what caused the feedback): {previous_prompt}")
candidates = [
str(c.get("text", "")) for c in (repair_candidates or [])[:5]
if isinstance(c, dict) and str(c.get("text", "")).strip()
]
if candidates:
user_lines.append(f"Memory suggestions: {', '.join(candidates)}.")
if feedback_history:
user_lines.append(f"Past feedback (most recent first):\n{feedback_history}")
user_lines.append("Task: identify exactly what needs to change in the current prompt.")
return analysis_system + "\n\n" + "\n".join(user_lines)
def _v2_advisor_prompt(
self,
prompt,
@@ -8833,6 +8889,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
mode="Full",
feedback_prompt="",
feedback_history="",
analysis="",
):
has_feedback = bool(str(feedback_prompt or "").strip())
previous_run = previous_run if isinstance(previous_run, dict) else {}
@@ -8876,14 +8933,17 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
if feedback_history:
user_lines.append(f"Past feedback (most recent first):\n{feedback_history}")
if analysis:
user_lines.append(f"Analysis of what needs to change: {analysis}")
if has_feedback:
user_lines.append(
"Task: apply the user feedback exactly. "
"Rewrite the current prompt to implement what the user asked for."
"Task: apply the user feedback and the analysis above. "
"Rewrite the current prompt to implement the required changes."
)
else:
user_lines.append(
"Task: produce the best next prompt. "
"Task: apply the analysis above to produce the best next prompt. "
"Keep it unchanged if no repair is justified."
)
return "\n".join(sections) + "\n\n" + "\n".join(user_lines)
@@ -9008,7 +9068,38 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
if mode in {"Only prompt", "Full"} and not repair_signal and not is_perfect and not has_feedback:
return current_prompt, "Advisor: skipped; no repair signal and no user feedback.", "", False
advisor_prompt_text = self._v2_advisor_prompt(
# Pass 1: analysis — always runs for Full and Only diagnostics.
# Only prompt skips this and goes straight to repair.
analysis = ""
if mode in {"Full", "Only diagnostics"}:
analysis_prompt_text = self._v2_advisor_analysis_prompt(
current_prompt,
intent_prompt,
repair_candidates,
previous_run=previous_run,
feedback_prompt=feedback_prompt,
feedback_history=feedback_history,
)
analysis_raw, analysis_status = self._v2_generate_advisor_text(
clip, analysis_prompt_text, seed=seed, image=image, thinking=thinking,
max_length=max(128, max_length // 2),
)
analysis, _ = self._v2_parse_advisor_response(analysis_raw, prompt_labels=("ANALYSIS",))
if not analysis and analysis_raw:
analysis = re.sub(r"\s+", " ", analysis_raw).strip()[:400]
if mode == "Only diagnostics":
return current_prompt, f"Advisor: diagnostics only. {analysis or 'No analysis returned.'}", analysis, False
# Guard checks before pass 2.
if (not allow_prompt_change and not has_feedback) or (is_perfect and not has_feedback):
reason = "perfect rating — analysis only" if is_perfect else "prompt changes disabled"
return current_prompt, f"Advisor: {reason}. {analysis}".strip(), analysis, False
if intent_changed and not has_feedback:
return current_prompt, f"Advisor: intent changed; repair skipped. {analysis}".strip(), analysis, False
# Pass 2: repair — builds on the analysis from pass 1 (or runs standalone for Only prompt).
repair_prompt_text = self._v2_advisor_prompt(
current_prompt,
intent_prompt,
repair_candidates,
@@ -9016,25 +9107,16 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
mode=mode,
feedback_prompt=feedback_prompt,
feedback_history=feedback_history,
analysis=analysis,
)
raw, gen_status = self._v2_generate_advisor_text(
clip, advisor_prompt_text, seed=seed, image=image, thinking=thinking, max_length=max_length
clip, repair_prompt_text, seed=seed, image=image, thinking=thinking, max_length=max_length
)
if not raw:
return current_prompt, gen_status, "", False
return current_prompt, gen_status, analysis, False
prompt_labels = ("PROMPT",) if mode == "Only prompt" else ("REPAIRED_PROMPT", "PROMPT")
diagnostic, advised = self._v2_parse_advisor_response(raw, prompt_labels=prompt_labels)
# Feedback always allows prompt changes even on Perfect rating.
# Without feedback, Perfect stays in analysis-only mode.
if mode == "Only diagnostics" or (not allow_prompt_change and not has_feedback) or (is_perfect and not has_feedback):
reason = "diagnostics only" if mode == "Only diagnostics" else ("perfect rating — analysis only" if is_perfect else "prompt changes disabled")
return current_prompt, f"Advisor: {reason}. {diagnostic or advised or 'No diagnostic returned.'}", diagnostic or advised, False
# Intent-change guard does not block user feedback.
if intent_changed and not has_feedback:
return current_prompt, f"Advisor: intent changed; repair skipped. {diagnostic}".strip(), diagnostic, False
_, advised = self._v2_parse_advisor_response(raw, prompt_labels=prompt_labels)
valid, reason = self._v2_validate_advisor_prompt(
current_prompt,
@@ -9046,10 +9128,10 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
has_user_feedback=has_feedback,
)
if not valid:
return current_prompt, f"Advisor: rejected ({reason}). {diagnostic}".strip(), diagnostic, False
return current_prompt, f"Advisor: rejected ({reason}). {analysis}".strip(), analysis, False
if self._v2_prompt_key(advised) == self._v2_prompt_key(current_prompt):
return current_prompt, f"Advisor: no change. {diagnostic}".strip(), diagnostic, False
return advised, f"Advisor: applied repair. {diagnostic}".strip(), diagnostic, True
return current_prompt, f"Advisor: no change. {analysis}".strip(), analysis, False
return advised, f"Advisor: applied repair. {analysis}".strip(), analysis, True
def _v2_negative_advisor_prompt(
self,
@@ -10063,7 +10145,9 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
)
if advisor_applied:
encoded_role = "advisor repaired prompt"
elif advisor_mode != "Off" and not learning_mode and repair_candidates and not advisor_applied:
if advisor_mode == "Only diagnostics" and advisor_diagnostic:
self._v2_record_advisor_diagnostic(global_state, advisor_diagnostic, advisor_rating_label, int(global_state.get("total_iterations", 0)))
if advisor_mode != "Off" and not learning_mode and repair_candidates and not advisor_applied:
additions = [c["text"] for c in repair_candidates if isinstance(c, dict) and str(c.get("text", "")).strip()]
if additions:
base = str(pre_advisor_prompt or "").strip()
+1 -1
View File
@@ -1489,7 +1489,7 @@ def test_refiner_v2_advisor_uses_explicit_system_prompt_previous_prompt_thinking
assert applied is False
assert "diagnostics only" in status
assert diagnostic == "add clearer smoke motion."
assert "FunPack Refiner V2 Prompt Advisor" in advisor_prompt
assert "analyzing a video generation prompt" in advisor_prompt
assert "Previous prompt (what caused the feedback): old encoded prompt" in advisor_prompt
assert kwargs["image"] is image
assert kwargs["thinking"] is True