mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-17 02:25:54 +08:00
feat(api): Save output variables as draft variables in single stepping
This commit is contained in:
parent
ecaf4b0e51
commit
f3fc3cf530
@ -4,3 +4,7 @@ class MoreLikeThisDisabledError(Exception):
|
|||||||
|
|
||||||
class WorkflowHashNotEqualError(Exception):
|
class WorkflowHashNotEqualError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class IsDraftWorkflowError(Exception):
|
||||||
|
pass
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
import json
|
import json
|
||||||
import time
|
import logging
|
||||||
from collections.abc import Callable, Generator, Sequence
|
from collections.abc import Callable, Generator, Sequence
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
|
from inspect import isgenerator
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
import time
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from core.app.apps.advanced_chat.app_config_manager import AdvancedChatAppConfigManager
|
from core.app.apps.advanced_chat.app_config_manager import AdvancedChatAppConfigManager
|
||||||
from core.app.apps.workflow.app_config_manager import WorkflowAppConfigManager
|
from core.app.apps.workflow.app_config_manager import WorkflowAppConfigManager
|
||||||
|
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||||
from core.repositories import SQLAlchemyWorkflowNodeExecutionRepository
|
from core.repositories import SQLAlchemyWorkflowNodeExecutionRepository
|
||||||
from core.variables import Variable
|
from core.variables import Variable
|
||||||
from core.workflow.entities.node_entities import NodeRunResult
|
from core.workflow.entities.node_entities import NodeRunMetadataKey, NodeRunResult
|
||||||
from core.workflow.entities.node_execution_entities import NodeExecution, NodeExecutionStatus
|
from core.workflow.entities.node_execution_entities import NodeExecution, NodeExecutionStatus
|
||||||
from core.workflow.errors import WorkflowNodeRunFailedError
|
from core.workflow.errors import WorkflowNodeRunFailedError
|
||||||
from core.workflow.graph_engine.entities.event import InNodeEvent
|
from core.workflow.graph_engine.entities.event import InNodeEvent
|
||||||
@ -35,10 +38,10 @@ from models.workflow import (
|
|||||||
WorkflowNodeExecutionTriggeredFrom,
|
WorkflowNodeExecutionTriggeredFrom,
|
||||||
WorkflowType,
|
WorkflowType,
|
||||||
)
|
)
|
||||||
from services.errors.app import WorkflowHashNotEqualError
|
from services.errors.app import IsDraftWorkflowError, WorkflowHashNotEqualError
|
||||||
from services.workflow.workflow_converter import WorkflowConverter
|
from services.workflow.workflow_converter import WorkflowConverter
|
||||||
|
|
||||||
from .errors.workflow_service import DraftWorkflowDeletionError, WorkflowInUseError
|
from .errors.workflow_service import DraftWorkflowDeletionError, WorkflowInUseError
|
||||||
|
from .workflow_draft_variable_service import WorkflowDraftVariableService, should_save_output_variables_for_draft
|
||||||
|
|
||||||
|
|
||||||
class WorkflowService:
|
class WorkflowService:
|
||||||
@ -89,6 +92,21 @@ class WorkflowService:
|
|||||||
# return draft workflow
|
# return draft workflow
|
||||||
return workflow
|
return workflow
|
||||||
|
|
||||||
|
def get_published_workflow_by_id(self, app_model: App, workflow_id: str) -> Optional[Workflow]:
|
||||||
|
# fetch published workflow by workflow_id
|
||||||
|
workflow = (
|
||||||
|
db.session.query(Workflow)
|
||||||
|
.filter(
|
||||||
|
Workflow.tenant_id == app_model.tenant_id,
|
||||||
|
Workflow.app_id == app_model.id,
|
||||||
|
Workflow.id == workflow_id,
|
||||||
|
)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if workflow.version == Workflow.VERSION_DRAFT:
|
||||||
|
raise IsDraftWorkflowError(f"Workflow is draft version, id={workflow_id}")
|
||||||
|
return workflow
|
||||||
|
|
||||||
def get_published_workflow(self, app_model: App) -> Optional[Workflow]:
|
def get_published_workflow(self, app_model: App) -> Optional[Workflow]:
|
||||||
"""
|
"""
|
||||||
Get published workflow
|
Get published workflow
|
||||||
@ -227,7 +245,7 @@ class WorkflowService:
|
|||||||
tenant_id=app_model.tenant_id,
|
tenant_id=app_model.tenant_id,
|
||||||
app_id=app_model.id,
|
app_id=app_model.id,
|
||||||
type=draft_workflow.type,
|
type=draft_workflow.type,
|
||||||
version=str(datetime.now(UTC).replace(tzinfo=None)),
|
version=Workflow.version_from_datetime(datetime.now(UTC).replace(tzinfo=None)),
|
||||||
graph=draft_workflow.graph,
|
graph=draft_workflow.graph,
|
||||||
features=draft_workflow.features,
|
features=draft_workflow.features,
|
||||||
created_by=account.id,
|
created_by=account.id,
|
||||||
@ -291,8 +309,17 @@ class WorkflowService:
|
|||||||
if not draft_workflow:
|
if not draft_workflow:
|
||||||
raise ValueError("Workflow not initialized")
|
raise ValueError("Workflow not initialized")
|
||||||
|
|
||||||
|
# conv_vars = common_helpers.get_conversation_variables()
|
||||||
|
|
||||||
# run draft workflow node
|
# run draft workflow node
|
||||||
start_at = time.perf_counter()
|
start_at = time.perf_counter()
|
||||||
|
with Session(bind=db.engine) as session:
|
||||||
|
# TODO(QunatumGhost): inject conversation variables
|
||||||
|
# to variable pool.
|
||||||
|
draft_var_srv = WorkflowDraftVariableService(session)
|
||||||
|
|
||||||
|
conv_vars_list = draft_var_srv.list_conversation_variables(app_id=app_model.id)
|
||||||
|
conv_var_mapping = {v.name: v.get_value().value for v in conv_vars_list.variables}
|
||||||
|
|
||||||
node_execution = self._handle_node_run_result(
|
node_execution = self._handle_node_run_result(
|
||||||
invoke_node_fn=lambda: WorkflowEntry.single_step_run(
|
invoke_node_fn=lambda: WorkflowEntry.single_step_run(
|
||||||
@ -300,6 +327,7 @@ class WorkflowService:
|
|||||||
node_id=node_id,
|
node_id=node_id,
|
||||||
user_inputs=user_inputs,
|
user_inputs=user_inputs,
|
||||||
user_id=account.id,
|
user_id=account.id,
|
||||||
|
conversation_variables=conv_var_mapping,
|
||||||
),
|
),
|
||||||
start_at=start_at,
|
start_at=start_at,
|
||||||
node_id=node_id,
|
node_id=node_id,
|
||||||
@ -319,6 +347,27 @@ class WorkflowService:
|
|||||||
|
|
||||||
# Convert node_execution to WorkflowNodeExecution after save
|
# Convert node_execution to WorkflowNodeExecution after save
|
||||||
workflow_node_execution = repository.to_db_model(node_execution)
|
workflow_node_execution = repository.to_db_model(node_execution)
|
||||||
|
output = workflow_node_execution.outputs_dict or {}
|
||||||
|
|
||||||
|
exec_metadata = workflow_node_execution.execution_metadata_dict or {}
|
||||||
|
|
||||||
|
should_save = should_save_output_variables_for_draft(
|
||||||
|
invoke_from=InvokeFrom.DEBUGGER,
|
||||||
|
loop_id=exec_metadata.get(NodeRunMetadataKey.LOOP_ID, None),
|
||||||
|
iteration_id=exec_metadata.get(NodeRunMetadataKey.ITERATION_ID, None),
|
||||||
|
)
|
||||||
|
if not should_save:
|
||||||
|
return workflow_node_execution
|
||||||
|
# TODO(QuantumGhost): single step does not include loop_id or iteration_id in execution_metadata.
|
||||||
|
with Session(bind=db.engine) as session:
|
||||||
|
draft_var_srv = WorkflowDraftVariableService(session)
|
||||||
|
draft_var_srv.save_output_variables(
|
||||||
|
app_id=app_model.id,
|
||||||
|
node_id=workflow_node_execution.node_id,
|
||||||
|
node_type=NodeType(workflow_node_execution.node_type),
|
||||||
|
output=output,
|
||||||
|
)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
return workflow_node_execution
|
return workflow_node_execution
|
||||||
|
|
||||||
@ -353,6 +402,7 @@ class WorkflowService:
|
|||||||
) -> NodeExecution:
|
) -> NodeExecution:
|
||||||
try:
|
try:
|
||||||
node_instance, generator = invoke_node_fn()
|
node_instance, generator = invoke_node_fn()
|
||||||
|
generator = _inspect_generator(generator)
|
||||||
|
|
||||||
node_run_result: NodeRunResult | None = None
|
node_run_result: NodeRunResult | None = None
|
||||||
for event in generator:
|
for event in generator:
|
||||||
@ -559,3 +609,19 @@ class WorkflowService:
|
|||||||
|
|
||||||
session.delete(workflow)
|
session.delete(workflow)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _inspect_generator(gen: Generator[Any] | Any) -> Any:
|
||||||
|
if not isgenerator(gen):
|
||||||
|
return gen
|
||||||
|
|
||||||
|
def wrapper():
|
||||||
|
for item in gen:
|
||||||
|
logging.getLogger(__name__).info(
|
||||||
|
"received generator item, type=%s, value=%s",
|
||||||
|
type(item),
|
||||||
|
item,
|
||||||
|
)
|
||||||
|
yield item
|
||||||
|
|
||||||
|
return wrapper()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user