diff --git a/AGENTS.md b/AGENTS.md index a231e6cf95..782861ad36 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,22 +1,3 @@ - -# OpenSpec Instructions - -These instructions are for AI assistants working in this project. - -Always open `@/openspec/AGENTS.md` when the request: -- Mentions planning or proposals (words like proposal, spec, change, plan) -- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work -- Sounds ambiguous and you need the authoritative spec before coding - -Use `@/openspec/AGENTS.md` to learn: -- How to create and apply change proposals -- Spec format and conventions -- Project structure and guidelines - -Keep this managed block so 'openspec update' can refresh the instructions. - - - # AGENTS.md ## Project Overview diff --git a/api/controllers/console/app/generator.py b/api/controllers/console/app/generator.py index eb9f12752b..15bfcdf999 100644 --- a/api/controllers/console/app/generator.py +++ b/api/controllers/console/app/generator.py @@ -1,6 +1,6 @@ import logging from collections.abc import Sequence -from typing import Any, cast +from typing import Any from flask_restx import Resource from pydantic import BaseModel, Field @@ -75,7 +75,6 @@ class FlowchartGeneratePayload(BaseModel): existing_nodes: list[dict[str, Any]] = Field(default_factory=list, description="Existing workflow nodes") available_tools: list[dict[str, Any]] = Field(default_factory=list, description="Available tools") selected_node_ids: list[str] = Field(default_factory=list, description="IDs of selected nodes for context") - # Phase 10: Regenerate with previous workflow context previous_workflow: PreviousWorkflow | None = Field(default=None, description="Previous workflow for regeneration") regenerate_mode: bool = Field(default=False, description="Whether this is a regeneration request") # Language preference for generated content (node titles, descriptions) @@ -309,13 +308,7 @@ class FlowchartGenerateApi(Resource): try: # Convert PreviousWorkflow to dict if present - previous_workflow_dict = None - if args.previous_workflow: - previous_workflow_dict = { - "nodes": args.previous_workflow.nodes, - "edges": args.previous_workflow.edges, - "warnings": args.previous_workflow.warnings, - } + previous_workflow_dict = args.previous_workflow.model_dump() if args.previous_workflow else None result = WorkflowGenerator.generate_workflow_flowchart( tenant_id=current_tenant_id, @@ -325,7 +318,7 @@ class FlowchartGenerateApi(Resource): existing_nodes=args.existing_nodes, available_tools=args.available_tools, selected_node_ids=args.selected_node_ids, - previous_workflow=cast(dict[str, object], previous_workflow_dict), + previous_workflow=previous_workflow_dict, regenerate_mode=args.regenerate_mode, preferred_language=args.language, available_models=args.available_models, diff --git a/web/app/components/workflow/hooks/use-workflow-vibe.tsx b/web/app/components/workflow/hooks/use-workflow-vibe.tsx index 8ec2916513..ea8232b47a 100644 --- a/web/app/components/workflow/hooks/use-workflow-vibe.tsx +++ b/web/app/components/workflow/hooks/use-workflow-vibe.tsx @@ -3,6 +3,7 @@ import type { ToolDefaultValue } from '../block-selector/types' import type { Edge, Node, ToolWithProvider } from '../types' import type { Tool } from '@/app/components/tools/types' +import type { BackendEdgeSpec, BackendNodeSpec } from '@/service/debug' import type { Model } from '@/types/app' import { useSessionStorageState } from 'ahooks' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' @@ -13,7 +14,6 @@ import Toast from '@/app/components/base/toast' import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' import { useGetLanguage } from '@/context/i18n' -import type { BackendEdgeSpec, BackendNodeSpec } from '@/service/debug' import { generateFlowchart } from '@/service/debug' import { useAllBuiltInTools, @@ -44,7 +44,7 @@ import { useNodesMetaData } from './use-nodes-meta-data' import { useNodesSyncDraft } from './use-nodes-sync-draft' import { useNodesReadOnly } from './use-workflow' import { useWorkflowHistory, WorkflowHistoryEvent } from './use-workflow-history' -import { NODE_TYPE_ALIASES, correctFieldName } from './use-workflow-vibe-config' +import { correctFieldName, NODE_TYPE_ALIASES } from './use-workflow-vibe-config' type VibeCommandDetail = { dsl?: string @@ -125,11 +125,6 @@ export const replaceVariableReferences = ( // Replace {{#old_id.field#}} patterns and correct field names return data.replace(/\{\{#([^.#]+)\.([^#]+)#\}\}/g, (match, oldId, field) => { const newNode = nodeIdMap.get(oldId) - // #region agent log - if (!newNode) { - console.warn(`[VIBE DEBUG] replaceVariableReferences: No mapping for "${oldId}" in template "${match}"`) - } - // #endregion if (newNode) { const nodeType = newNode.data?.type as string || '' const correctedField = correctFieldName(field, nodeType) @@ -435,6 +430,28 @@ export const useVibeFlowData = ({ storageKey }: UseVibeFlowDataParams) => { } } +const buildEdge = ( + source: Node, + target: Node, + sourceHandle = 'source', + targetHandle = 'target', +): Edge => ({ + id: `${source.id}-${sourceHandle}-${target.id}-${targetHandle}`, + type: CUSTOM_EDGE, + source: source.id, + sourceHandle, + target: target.id, + targetHandle, + data: { + sourceType: source.data.type, + targetType: target.data.type, + isInIteration: false, + isInLoop: false, + _connectedNodeIsSelected: false, + }, + zIndex: 0, +}) + export const useWorkflowVibe = () => { const { t } = useTranslation() const store = useStoreApi() @@ -609,8 +626,6 @@ export const useWorkflowVibe = () => { const { getNodes } = store.getState() const nodes = getNodes() - - if (!nodesMetaDataMap) { Toast.notify({ type: 'error', message: t('workflow.vibe.nodesUnavailable') }) return { nodes: [], edges: [] } @@ -744,7 +759,7 @@ export const useWorkflowVibe = () => { // Backend may omit this field, but Dify's Pydantic model requires it if (nodeType === BlockEnum.ParameterExtractor && backendConfig.parameters) { const parameters = backendConfig.parameters as Array<{ name?: string, type?: string, description?: string, required?: boolean }> - mergedConfig.parameters = parameters.map((param) => ({ + mergedConfig.parameters = parameters.map(param => ({ ...param, required: param.required ?? true, // Default to required if not specified })) @@ -809,29 +824,6 @@ export const useWorkflowVibe = () => { return { nodes: [], edges: [] } } - const buildEdge = ( - source: Node, - target: Node, - sourceHandle = 'source', - targetHandle = 'target', - ): Edge => ({ - id: `${source.id}-${sourceHandle}-${target.id}-${targetHandle}`, - type: CUSTOM_EDGE, - source: source.id, - sourceHandle, - target: target.id, - targetHandle, - data: { - sourceType: source.data.type, - targetType: target.data.type, - isInIteration: false, - isInLoop: false, - _connectedNodeIsSelected: false, - }, - zIndex: 0, - }) - - const newEdges: Edge[] = [] for (const edgeSpec of backendEdges) { const sourceNode = nodeIdMap.get(edgeSpec.source) @@ -848,11 +840,9 @@ export const useWorkflowVibe = () => { sourceHandle = 'source' } - newEdges.push(buildEdge(sourceNode, targetNode, sourceHandle, edgeSpec.targetHandle || 'target')) } - // Layout nodes const bounds = nodes.reduce( (acc, node) => { @@ -1057,28 +1047,6 @@ export const useWorkflowVibe = () => { return emptyGraph } - const buildEdge = ( - source: Node, - target: Node, - sourceHandle = 'source', - targetHandle = 'target', - ): Edge => ({ - id: `${source.id}-${sourceHandle}-${target.id}-${targetHandle}`, - type: CUSTOM_EDGE, - source: source.id, - sourceHandle, - target: target.id, - targetHandle, - data: { - sourceType: source.data.type, - targetType: target.data.type, - isInIteration: false, - isInLoop: false, - _connectedNodeIsSelected: false, - }, - zIndex: 0, - }) - const newEdges: Edge[] = [] for (const edgeSpec of parseResultToUse.edges) { const sourceNode = nodeIdMap.get(edgeSpec.sourceId) @@ -1288,10 +1256,10 @@ export const useWorkflowVibe = () => { const { vibePanelBackendNodes, vibePanelBackendEdges, vibePanelLastWarnings } = workflowStore.getState() const previousWorkflow = regenerateMode && vibePanelBackendNodes && vibePanelBackendNodes.length > 0 ? { - nodes: vibePanelBackendNodes, - edges: vibePanelBackendEdges || [], - warnings: vibePanelLastWarnings || [], - } + nodes: vibePanelBackendNodes, + edges: vibePanelBackendEdges || [], + warnings: vibePanelLastWarnings || [], + } : undefined // Map language code to human-readable language name for LLM