mirror of
https://github.com/langgenius/dify.git
synced 2026-10-04 13:47:55 +08:00
feat(dify-builder): Edit live test reuses prepared input, routes input-failures to testdata gate
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
ef88f6532c
commit
db150d1a43
2 files changed
+98
-3
No files matched your search
@@ -37,6 +37,7 @@ from core.dify_builder.handlers_fix import (
|
||||
build_form_fields,
|
||||
emit_canvas,
|
||||
first_failed_node,
|
||||
is_input_failure,
|
||||
merge_known_keys,
|
||||
mint_checkpoint,
|
||||
perform_revert,
|
||||
@@ -322,13 +323,13 @@ def handle_test_affected_paths(env: Env, turn: Turn, s: Session, fc: DifyBuilder
|
||||
edit.review; failure -> real diagnose + propose_repair, staged for the
|
||||
edit.await_repair approval gate. No auto-apply (human-gated)."""
|
||||
graph, _hash = env.dify.read_graph(s.app_id, turn.actor)
|
||||
if fc.test_input_ref == "":
|
||||
if fc.test_input_ref:
|
||||
inputs = env.repo.get_test_input(fc.test_input_ref).inputs
|
||||
else: # defensive: the gate normally prepares inputs first
|
||||
inputs = env.agent.generate_mock_inputs(start_schema(graph), {})
|
||||
ti = TestInput(session_id=s.id, source="mock", inputs=inputs)
|
||||
env.repo.save_test_input(ti)
|
||||
fc.test_input_ref = ti.id
|
||||
else:
|
||||
inputs = env.repo.get_test_input(fc.test_input_ref).inputs
|
||||
|
||||
emit = env.emit if env.emit is not None else (lambda _e: None)
|
||||
try:
|
||||
@@ -390,6 +391,52 @@ def handle_test_affected_paths(env: Env, turn: Turn, s: Session, fc: DifyBuilder
|
||||
run.culprit_node_id = first_failed_node(per_node)
|
||||
fc.verify_run_id = run.id
|
||||
emit_canvas(env, "mark_test_error")
|
||||
|
||||
if is_input_failure(run):
|
||||
# the run failed on its INPUT, not the config -- route back to the
|
||||
# testdata gate instead of the config-repair gate; clear the stale
|
||||
# input ref (and the verify_run_id we just set) so a fresh
|
||||
# provide_testdata cycle starts clean.
|
||||
fc.test_input_ref = ""
|
||||
fc.verify_run_id = ""
|
||||
test_items = append_card(
|
||||
fc,
|
||||
TestResultCard(
|
||||
title="Affected-path tests",
|
||||
subtitle="Failed",
|
||||
tone="error",
|
||||
stats=[TestStat(value="1", label="runs"), TestStat(value="1", label="errors")],
|
||||
run_ids=[run.id],
|
||||
),
|
||||
)
|
||||
form_items = append_card(
|
||||
fc,
|
||||
FormCard(
|
||||
variant="testdata",
|
||||
fields=testdata_form_fields(start_schema(graph)),
|
||||
values={},
|
||||
frozen=False,
|
||||
),
|
||||
)
|
||||
turn_items = append_card(
|
||||
fc,
|
||||
AssistantTurnItem(
|
||||
turn_id=str(uuid.uuid4()),
|
||||
stage_id="edit.await_testdata",
|
||||
trace=Trace(status="completed", steps=[]),
|
||||
reply_text="The run failed on its inputs — provide test data and retry.",
|
||||
cards=["test_result", "form"],
|
||||
),
|
||||
)
|
||||
return StepResult(
|
||||
next=PcState.EDIT_AWAIT_TESTDATA,
|
||||
context=fc,
|
||||
items=[*test_items, *form_items, *turn_items],
|
||||
run=run,
|
||||
run_id_sink=[run.id],
|
||||
)
|
||||
|
||||
# config failure: existing diagnose + propose_repair -> EDIT_AWAIT_REPAIR
|
||||
diagnosis = env.agent.diagnose(run, graph, per_node)
|
||||
intents, risk = env.agent.propose_repair(diagnosis, graph)
|
||||
fc.diagnosis = diagnosis
|
||||
|
||||
@@ -351,6 +351,54 @@ def test_edit_test_run_draft_raises_routes_to_await_repair_failed():
|
||||
assert result.run.status == "failed"
|
||||
|
||||
|
||||
def test_edit_test_input_failure_routes_to_testdata_gate():
|
||||
"""An INPUT-caused run failure (missing/invalid test data, per
|
||||
is_input_failure's signal match) must clear the stale input ref and route
|
||||
back to the testdata gate -- not the config-repair gate."""
|
||||
from core.dify_builder.handlers_edit import handle_test_affected_paths
|
||||
from core.dify_builder.models import TestInput
|
||||
|
||||
env, _ = _new_env()
|
||||
env.dify.verify_pass = False
|
||||
env.dify.fail_error = "File variable not found for selector: ['start', 'document']"
|
||||
s = _session(entry_mode=EntryMode.EDIT, current_state=PcState.EDIT_TEST_AFFECTED_PATHS)
|
||||
env.repo.save_test_input(TestInput(id="ti-1", session_id=s.id, source="mock", inputs={}))
|
||||
fc = DifyBuilderContext(edit_target_node_ids=["llm"], test_input_ref="ti-1")
|
||||
|
||||
result = handle_test_affected_paths(env, Turn(actor=_actor()), s, fc)
|
||||
|
||||
assert result.next == PcState.EDIT_AWAIT_TESTDATA
|
||||
assert result.context.test_input_ref == "" # stale input cleared
|
||||
assert result.context.verify_run_id == ""
|
||||
kinds = [i.kind for i in result.items]
|
||||
assert "form" in kinds
|
||||
assert "change_set" not in kinds # gate, not repair
|
||||
test_result = next(i for i in result.items if i.kind == "test_result")
|
||||
assert test_result.payload["tone"] == "error"
|
||||
assistant = next(i for i in result.items if i.kind == "assistant_turn")
|
||||
assert assistant.payload["stage_id"] == "edit.await_testdata"
|
||||
|
||||
|
||||
def test_edit_test_config_failure_still_routes_to_repair_gate():
|
||||
"""A config-caused run failure (the fake's default error, which matches no
|
||||
input-failure signal) must still route to the config-repair gate,
|
||||
unchanged."""
|
||||
from core.dify_builder.handlers_edit import handle_test_affected_paths
|
||||
from core.dify_builder.models import TestInput
|
||||
|
||||
env, _ = _new_env(agent=StubAgent())
|
||||
env.dify.verify_pass = False # default error "boom" -> config, not input
|
||||
s = _session(entry_mode=EntryMode.EDIT, current_state=PcState.EDIT_TEST_AFFECTED_PATHS)
|
||||
env.repo.save_test_input(TestInput(id="ti-1", session_id=s.id, source="mock", inputs={}))
|
||||
fc = DifyBuilderContext(edit_target_node_ids=["llm"], test_input_ref="ti-1")
|
||||
|
||||
result = handle_test_affected_paths(env, Turn(actor=_actor()), s, fc)
|
||||
|
||||
assert result.next == PcState.EDIT_AWAIT_REPAIR
|
||||
assert result.context.staged_repair # StubAgent proposes a repair
|
||||
assert result.context.test_input_ref == "ti-1" # untouched on the config path
|
||||
|
||||
|
||||
def test_review_publish_reaches_terminal_edit_publish_with_publish_card():
|
||||
from core.dify_builder.handlers_edit import handle_review
|
||||
|
||||
|
||||
Reference in new issue
Block a user