fix(dify-builder): stream native test runs after canvas refresh

Run workflow streaming inside the Builder worker to avoid queue deadlocks. Forward native events to shared editor callbacks and wait for graph refresh before retesting repairs.

Keep command cleanup stable, preserve batched canvas events, and allow failed or unfinished historical runs to refresh again.
This commit is contained in:
twwu authored and Wu Tianwei committed 2026-09-21 15:12:22 +08:00
1 parent e2633ad6a0
commit d5ca5ea8ef
64 files changed
+4966 -415

No files matched your search

+108
View File
@@ -0,0 +1,108 @@
"""Full debugger workflow stream payloads, using the runtime's data schemas.
Workflow adds workflow_run_id to every payload; Chatflow leaves it out of
message, text, reasoning, agent-log, audio, and error events. Errors are flat
and omit task_id. Preserve both native wire shapes inside Builder's envelope.
"""
from typing import Annotated, Literal
from pydantic import ConfigDict, Field
from core.app.entities import task_entities as events
from fields.base import ResponseModel
class WorkflowStreamEvent[Event: str, Data](ResponseModel):
model_config = ConfigDict(extra="allow")
event: Event
task_id: str
workflow_run_id: str
data: Data
class WorkflowStreamDataEvent[Event: str, Data](ResponseModel):
model_config = ConfigDict(extra="allow")
event: Event
task_id: str
workflow_run_id: str | None = None
data: Data
class WorkflowStreamError(ResponseModel):
model_config = ConfigDict(extra="allow")
event: Literal["error"]
workflow_run_id: str | None = None
code: str
status: int
message: str
class WorkflowStreamMessageEvent[Event: str](ResponseModel):
model_config = ConfigDict(extra="allow")
event: Event
task_id: str
workflow_run_id: str | None = None
class WorkflowStreamMessage(WorkflowStreamMessageEvent[Literal["message"]]):
id: str
answer: str
from_variable_selector: list[str] = Field(default_factory=list)
class WorkflowStreamMessageEnd(WorkflowStreamMessageEvent[Literal["message_end"]]):
id: str
metadata: dict[str, object] = Field(default_factory=dict)
files: list[dict[str, object]] = Field(default_factory=list)
class WorkflowStreamMessageFile(WorkflowStreamMessageEvent[Literal["message_file"]]):
id: str
type: str
belongs_to: str
url: str
class WorkflowStreamMessageReplace(WorkflowStreamMessageEvent[Literal["message_replace"]]):
answer: str
reason: str
class WorkflowStreamAudio(WorkflowStreamMessageEvent[Literal["tts_message", "tts_message_end"]]):
audio: str
audio_type: str | None = None
WorkflowStreamPayload = Annotated[
WorkflowStreamEvent[Literal["workflow_started"], events.WorkflowStartStreamResponse.Data]
| WorkflowStreamEvent[Literal["workflow_finished"], events.WorkflowFinishStreamResponse.Data]
| WorkflowStreamEvent[Literal["workflow_paused"], events.WorkflowPauseStreamResponse.Data]
| WorkflowStreamEvent[Literal["node_started"], events.NodeStartStreamResponse.Data]
| WorkflowStreamEvent[Literal["node_finished"], events.NodeFinishStreamResponse.Data]
| WorkflowStreamEvent[Literal["node_retry"], events.NodeRetryStreamResponse.Data]
| WorkflowStreamEvent[Literal["iteration_started"], events.IterationNodeStartStreamResponse.Data]
| WorkflowStreamEvent[Literal["iteration_next"], events.IterationNodeNextStreamResponse.Data]
| WorkflowStreamEvent[Literal["iteration_completed"], events.IterationNodeCompletedStreamResponse.Data]
| WorkflowStreamEvent[Literal["loop_started"], events.LoopNodeStartStreamResponse.Data]
| WorkflowStreamEvent[Literal["loop_next"], events.LoopNodeNextStreamResponse.Data]
| WorkflowStreamEvent[Literal["loop_completed"], events.LoopNodeCompletedStreamResponse.Data]
| WorkflowStreamDataEvent[Literal["text_chunk"], events.TextChunkStreamResponse.Data]
| WorkflowStreamDataEvent[Literal["text_replace"], events.TextReplaceStreamResponse.Data]
| WorkflowStreamDataEvent[Literal["reasoning_chunk"], events.ReasoningChunkStreamResponse.Data]
| WorkflowStreamDataEvent[Literal["agent_log"], events.AgentLogStreamResponse.Data]
| WorkflowStreamEvent[Literal["human_input_required"], events.HumanInputRequiredResponse.Data]
| WorkflowStreamEvent[Literal["human_input_form_filled"], events.HumanInputFormFilledResponse.Data]
| WorkflowStreamEvent[Literal["human_input_form_timeout"], events.HumanInputFormTimeoutResponse.Data]
| WorkflowStreamAudio
| WorkflowStreamMessage
| WorkflowStreamMessageEnd
| WorkflowStreamMessageFile
| WorkflowStreamMessageReplace
| WorkflowStreamError,
Field(discriminator="event"),
]