mirror of
https://github.com/langgenius/dify.git
synced 2026-09-24 23:22:26 +08:00
feat(workflow-generator): enhance the AI auto-creation flow end-to-end (#38175)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
autofix-ci[bot]
Copilot
parent
3ad06bebd9
commit
8809cc036d
@@ -12,13 +12,19 @@ createApp) rather than from inside another workflow.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from collections.abc import Iterator
|
||||
from typing import Any
|
||||
|
||||
from core.app.app_config.entities import ModelConfig
|
||||
from core.model_manager import ModelManager
|
||||
from core.llm_generator.llm_generator import LLMGenerator
|
||||
from core.model_manager import ModelInstance, ModelManager
|
||||
from core.workflow.generator import WorkflowGenerator
|
||||
from core.workflow.generator.tool_catalogue import build_tool_catalogue, format_tool_catalogue, installed_tool_keys
|
||||
from core.workflow.generator.types import WorkflowGenerateResultDict, WorkflowGenerationMode
|
||||
from core.workflow.generator.types import (
|
||||
WorkflowGenerateResultDict,
|
||||
WorkflowGenerationMode,
|
||||
WorkflowGenerationModeRequest,
|
||||
)
|
||||
from graphon.model_runtime.entities.model_entities import ModelType
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -37,7 +43,7 @@ class WorkflowGeneratorService:
|
||||
cls,
|
||||
*,
|
||||
tenant_id: str,
|
||||
mode: WorkflowGenerationMode,
|
||||
mode: WorkflowGenerationModeRequest,
|
||||
instruction: str,
|
||||
model_config: ModelConfig,
|
||||
ideal_output: str = "",
|
||||
@@ -46,6 +52,12 @@ class WorkflowGeneratorService:
|
||||
"""
|
||||
Resolve a model instance for the tenant and run the generator.
|
||||
|
||||
``mode`` accepts the ``"auto"`` sentinel — when set, the instruction is
|
||||
classified into a concrete ``workflow`` / ``advanced-chat`` mode (one
|
||||
tiny LLM call) before planning so the rest of the pipeline runs against
|
||||
a concrete mode. The resolved mode is echoed back under the result's
|
||||
``mode`` key.
|
||||
|
||||
``current_graph`` is the existing draft graph for the cmd+k `/refine`
|
||||
flow — when present the generator refines it instead of creating a new
|
||||
graph from scratch. ``None`` is the `/create` path.
|
||||
@@ -54,6 +66,109 @@ class WorkflowGeneratorService:
|
||||
controller can map them to existing HTTP error envelopes (same
|
||||
envelope as ``/rule-generate``).
|
||||
"""
|
||||
resolved_mode = cls._resolve_mode(
|
||||
tenant_id=tenant_id, mode=mode, instruction=instruction, model_config=model_config
|
||||
)
|
||||
model_instance, model_parameters, tool_catalogue_text, installed_tools = cls._resolve_generation_context(
|
||||
tenant_id=tenant_id, model_config=model_config
|
||||
)
|
||||
|
||||
return WorkflowGenerator.generate_workflow_graph(
|
||||
model_instance=model_instance,
|
||||
model_parameters=model_parameters,
|
||||
provider=model_config.provider,
|
||||
model_name=model_config.name,
|
||||
model_mode=model_config.mode.value,
|
||||
mode=resolved_mode,
|
||||
instruction=instruction,
|
||||
ideal_output=ideal_output,
|
||||
tool_catalogue_text=tool_catalogue_text,
|
||||
installed_tools=installed_tools,
|
||||
current_graph=current_graph,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def generate_workflow_graph_stream(
|
||||
cls,
|
||||
*,
|
||||
tenant_id: str,
|
||||
mode: WorkflowGenerationModeRequest,
|
||||
instruction: str,
|
||||
model_config: ModelConfig,
|
||||
ideal_output: str = "",
|
||||
current_graph: dict[str, Any] | None = None,
|
||||
) -> Iterator[tuple[str, dict[str, Any]]]:
|
||||
"""
|
||||
Streaming sibling of ``generate_workflow_graph``.
|
||||
|
||||
Resolves the same model instance / tool catalogue / concrete mode, then
|
||||
delegates to ``WorkflowGenerator.generate_workflow_graph_stream`` and
|
||||
yields its ``(event_name, payload)`` tuples through to the controller's
|
||||
SSE writer. Provider-init / invoke errors raised while resolving the
|
||||
model instance propagate to the caller (the controller emits them as a
|
||||
single ``result`` SSE event).
|
||||
"""
|
||||
resolved_mode = cls._resolve_mode(
|
||||
tenant_id=tenant_id, mode=mode, instruction=instruction, model_config=model_config
|
||||
)
|
||||
model_instance, model_parameters, tool_catalogue_text, installed_tools = cls._resolve_generation_context(
|
||||
tenant_id=tenant_id, model_config=model_config
|
||||
)
|
||||
|
||||
yield from WorkflowGenerator.generate_workflow_graph_stream(
|
||||
model_instance=model_instance,
|
||||
model_parameters=model_parameters,
|
||||
provider=model_config.provider,
|
||||
model_name=model_config.name,
|
||||
model_mode=model_config.mode.value,
|
||||
mode=resolved_mode,
|
||||
instruction=instruction,
|
||||
ideal_output=ideal_output,
|
||||
tool_catalogue_text=tool_catalogue_text,
|
||||
installed_tools=installed_tools,
|
||||
current_graph=current_graph,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _resolve_mode(
|
||||
cls,
|
||||
*,
|
||||
tenant_id: str,
|
||||
mode: WorkflowGenerationModeRequest,
|
||||
instruction: str,
|
||||
model_config: ModelConfig,
|
||||
) -> WorkflowGenerationMode:
|
||||
"""Resolve the request mode into a concrete generation mode.
|
||||
|
||||
``"auto"`` triggers a one-word LLM classification using the model the
|
||||
user already picked; everything else passes through unchanged. The
|
||||
classifier never raises (defaults to ``advanced-chat``), so ``auto``
|
||||
never blocks generation.
|
||||
"""
|
||||
if mode == "auto":
|
||||
return LLMGenerator.classify_workflow_mode(
|
||||
tenant_id=tenant_id, instruction=instruction, model_config=model_config
|
||||
)
|
||||
return mode
|
||||
|
||||
@classmethod
|
||||
def _resolve_generation_context(
|
||||
cls,
|
||||
*,
|
||||
tenant_id: str,
|
||||
model_config: ModelConfig,
|
||||
) -> tuple[ModelInstance, dict[str, Any], str, set[tuple[str, str]] | None]:
|
||||
"""Resolve the model instance, completion params, and tool catalogue.
|
||||
|
||||
Build the installed-tool catalogue for this tenant so the planner /
|
||||
builder can pick concrete tools instead of inventing names, AND so the
|
||||
runner's validator can reject hallucinated tool names BEFORE the user
|
||||
clicks Apply. A failure here (plugin daemon unreachable, etc.) must not
|
||||
block generation — log and fall back to the no-tool path, which also
|
||||
disables tool validation in the runner (``None`` sentinel rather than
|
||||
empty set, so we don't reject every tool node just because we couldn't
|
||||
enumerate the catalogue).
|
||||
"""
|
||||
model_manager = ModelManager.for_tenant(tenant_id=tenant_id)
|
||||
model_instance = model_manager.get_model_instance(
|
||||
tenant_id=tenant_id,
|
||||
@@ -64,14 +179,6 @@ class WorkflowGeneratorService:
|
||||
|
||||
model_parameters: dict[str, Any] = dict(model_config.completion_params or {})
|
||||
|
||||
# Build the installed-tool catalogue for this tenant so the planner/
|
||||
# builder can pick concrete tools instead of inventing names, AND so
|
||||
# the runner's validator can reject hallucinated tool names BEFORE
|
||||
# the user clicks Apply. A failure here (plugin daemon unreachable,
|
||||
# etc.) must not block generation — log and fall back to the no-tool
|
||||
# path, which also disables tool validation in the runner (None
|
||||
# sentinel rather than empty set, so we don't reject every tool
|
||||
# node just because we couldn't enumerate the catalogue).
|
||||
tool_catalogue_text = ""
|
||||
installed_tools: set[tuple[str, str]] | None = None
|
||||
try:
|
||||
@@ -81,16 +188,4 @@ class WorkflowGeneratorService:
|
||||
except Exception:
|
||||
logger.exception("Workflow generator: failed to build tool catalogue for tenant %s", tenant_id)
|
||||
|
||||
return WorkflowGenerator.generate_workflow_graph(
|
||||
model_instance=model_instance,
|
||||
model_parameters=model_parameters,
|
||||
provider=model_config.provider,
|
||||
model_name=model_config.name,
|
||||
model_mode=model_config.mode.value,
|
||||
mode=mode,
|
||||
instruction=instruction,
|
||||
ideal_output=ideal_output,
|
||||
tool_catalogue_text=tool_catalogue_text,
|
||||
installed_tools=installed_tools,
|
||||
current_graph=current_graph,
|
||||
)
|
||||
return model_instance, model_parameters, tool_catalogue_text, installed_tools
|
||||
|
||||
Reference in New Issue
Block a user