Commit Graph
15 Commits
Author SHA1 Message Date
twwu d5ca5ea8ef 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.
2026-09-21 15:12:22 +08:00
Charles YaoandClaude Opus 5 96f98b5804 feat(dify-builder): persist the Dify run id on the test-result card
The run id already reaches a client over SSE, which is enough to open the
run on the canvas while the page is up. It is not enough afterwards: SSE
frames do not survive a reload, and the persisted card's ``run_ids`` holds
BUILDER run ids -- uuid4s the engine mints, which resolve to nothing
outside it. So on reopening a session the card was the only record of the
result, and the canvas view was unreachable.

Put ``dify_run_id`` on TestResultCard, beside ``run_ids``, wired at all
nine construction sites across the Build, Edit and Fix flows. Now the
canvas is reachable from the durable conversation too, which is what makes
the card's ``output`` genuinely the client's choice to render or skip
rather than the only surviving copy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 17:31:34 -07:00
Charles YaoandClaude Opus 5 2a0fae4bcb feat(dify-builder): show what the test run actually produced
A passing build test reported "All checks passed" and threw away the
run's actual node outputs. TestResultCard gains an `output: str` field
(pretty-printed JSON of the last node that produced anything, "" when
none); handle_test_and_repair's success card now populates it via a new
_terminal_output(per_node) helper.

_terminal_output is deliberately status-agnostic (no node.status filter):
per-node status spellings differ by source ("success" vs "succeeded"), and
a status filter that silently misses would defeat the point of the card.

FakeDifyPort (tests/unit_tests/core/dify_builder/fakes.py) gains a
settable run_outputs dict, seeded onto the success-path NodeOutput's
outputs, so tests can assert on what the card shows without hand-rolling
a Run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 15:22:13 -07:00
Charles YaoandClaude Opus 5 e8b14b50ff refactor(dify-builder): drop the resource conflict policy
The resource-recommendation step made the user pick a "conflict
policy" that never decided anything. Remove it end to end: the
ConflictPolicyOption card option, ResourceSelectCard's
conflict_policy_options field, the third bind_resources() parameter
across the port/placeholder/LLM-agent implementations, and the dead
conflict_policy field on the (unused) ResourcesPayload contract type.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 14:10:34 -07:00
Charles YaoandClaude Opus 5 aa2b4fe4ab feat(dify-builder): name apps from their prompt, and turn gate actions into card options
Two App Builder spec changes that share the Builder session contract.

Naming (spec N1-N3): an app created from the homepage prompt is no longer named
by the user. core/dify_builder/naming.py cuts the prompt's opening clause at
create time -- instant and infallible, since it runs inside POST /apps -- and
CreateAppPayload.name becomes optional alongside a new prompt field. The Builder
then refines that name once during goal analysis, where a model and the detected
reply language are already in hand, so the judgement calls that are
English-shaped in pure Python (dropping an imperative lead-in, spotting a
contentless prompt) work in any language. Plan approval freezes the name.

Card options: SessionView gains a Decision carrying the gate's choice as options
inside the card plus a fixed confirm/cancel pair, replacing a button row whose
shape changed at every state. Option ids are the old action ids untouched, so
resolve_action_kind and the per-state legality check are unchanged, and
SessionView.actions keeps being populated -- the deployed web client goes on
working until it reads decision instead.

The regenerated console OpenAPI doc also picks up ErrorCard.diagnostics, missed
when cc9ca8b1dd landed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 17:55:38 -07:00
Charles YaoandClaude Opus 5 cc9ca8b1dd feat(dify-builder): carry generation diagnostics into the exported debug log
api pods lose their logs on restart, so a failed build's real cause
("Workflow generator: structural validation failed: ...") could only be read
from a pod log that may already be gone. Session 01a08fd0 hit exactly that.

Carry the generator's own diagnostics back with the result instead:
BuildNodesResult and ErrorCard gain a ``diagnostics`` list, and build_nodes
records one entry per failed generation attempt -- initial AND the corrective
retry, so an error that changes between attempts stays visible -- plus the
all-rejected and exception paths.

Each entry holds a server-side UTC ``at`` (so the surviving pod-log window can
be searched around it), ``source``, ``attempt``, the generator's ``message``
(the same text the server logs), its ``codes``, and the full structured
``errors`` including ``node_id``. That is strictly more than the log line
carries: for an UNRESOLVED_REFERENCE the export now names the offending node.

The list rides in the ErrorCard's item payload, which is what the streamed
conversation item -- and therefore the exported debug log -- preserves. It is
not rendered in the UI. Retries that eventually succeed keep their breadcrumbs.

Deliberately not mirroring the "structural validation failed" prefix: build.py
cannot tell structural failures from the rest without duplicating the
generator's code taxonomy, which would go stale.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 15:16:10 -07:00
twwu cecf2a1b9a fix(dify-builder): handle unavailable models during session creation 2026-09-11 18:28:52 +08:00
twwu c51e277389 feat(dify-builder): show affected nodes in change cards
Render change sets directly in plan-style cards with affected node names,
IDs, and change details. Capture node metadata for proposed and applied
changes and persist it with conversation history.

Remove the view_changes action and expand/collapse state. Update generated
contracts, translations, and regression coverage.

Validation: 112 frontend tests and 742 backend unit tests passed.
2026-09-07 13:44:39 +08:00
twwu 4893c5e621 refactor(dify-builder): separate reasoning from execution progress 2026-09-04 13:43:16 +08:00
twwu eb25c2d8ee feat(dify-builder): add live progress and conversation streaming 2026-09-04 10:16:50 +08:00
twwu 3eacff14a6 feat: add typed SSE interactions to Dify Builder 2026-09-03 08:48:23 +08:00
twwu 22be55af5f feat: redesign app builder fix flow 2026-08-31 18:18:30 +08:00
Charles YaoandClaude Opus 4.8 d2cfaf7f72 fix(dify_builder): narrow input-failure signals, fix build delete/create id collision
Final-review fix wave for the testdata-gate + start-selector slice:

- I1 (Important): is_input_failure's signal list included the bare
  "required" (and generic "variable not found"), which also fire on
  config/runtime faults unrelated to test inputs -- misrouting those
  failures to the *_AWAIT_TESTDATA gate, where the diagnosed auto-repair
  is never offered and re-running just loops at the gate. Narrowed
  _INPUT_FAILURE_SIGNALS to ("file variable", "not provided", "missing
  input"), favoring precision per the spec's ambiguous-failure ->
  config-repair safe default. The real file-input E2E error ("File
  variable not found for selector: [...]") still matches via "file
  variable".

- M2 (Minor, latent): handle_plan_approval's from-scratch delete-then-
  create-filter path could drop a generator create_node/connect that
  reuses a just-deleted placeholder start's id, since _already_present
  compared against the PRE-delete existing_node_ids/existing_edges --
  delete with no re-create, silently vanishing the node. Deleted ids are
  now excluded from that comparison set; the loop-back path (no deletes)
  is unaffected.

- Cleanups: state.py's Build/Edit state-count comments (14->15, 9->10,
  verified against the actual enum members), FormCard's docstring now
  lists the "testdata" variant, and handle_plan_approval binds
  current_graph's nodes to one local instead of calling .get() 3x and
  documents the delete-placeholder branch.

Tests: extended test_is_input_failure_matches_input_signals's sibling
with a config "required" case (red before the signal-list fix, green
after), and added a plan_approval test where the generator's create_node
reuses the deleted placeholder start's id (red before the id-exclusion
fix -- a dangling connect ValueError -- green after).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-30 10:03:48 -07:00
Charles YaoandClaude Opus 4.8 4c0efd947d feat(dify-builder): echo chosen model in SessionView + regen contract
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 22:51:27 -07:00
Charles YaoandClaude Opus 4.8 7e83264330 refactor: rename Workflow Copilot → Dify Builder
Project rename across api/ + web/: modules, symbols, routes
(/console/api/dify-builder/*), feature flag (DIFY_BUILDER_ENABLED),
Celery queue/tasks, the 7 DB tables (via migration df1b0114de40),
and regenerated contract artifacts. Behavior unchanged; 430 tests green,
ruff clean. Branch renamed feat/copilot-py-backend → dify-builder.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 11:18:35 -07:00