From 7d984ce3e83513e3649dc2ee884d37a831646e9a Mon Sep 17 00:00:00 2001 From: chariri Date: Fri, 26 Jun 2026 03:01:28 +0900 Subject: [PATCH] refactor(api): migrate console workflow endpoints to BaseModel --- api/controllers/console/app/workflow.py | 342 ++++---- .../console/app/workflow_app_log.py | 6 +- .../console/app/workflow_comment.py | 12 +- .../console/app/workflow_draft_variable.py | 446 +++++----- api/controllers/console/app/workflow_run.py | 32 +- .../rag_pipeline_draft_variable.py | 193 +++-- .../rag_pipeline/rag_pipeline_workflow.py | 196 +++-- .../console/snippets/snippet_workflow.py | 94 +- .../snippet_workflow_draft_variable.py | 152 ++-- api/fields/member_fields.py | 18 +- api/fields/workflow_run_fields.py | 49 +- api/openapi/markdown/console-openapi.md | 802 +++++++++++------- api/openapi/markdown/service-openapi.md | 4 +- api/services/workflow_service.py | 3 + .../app/test_workflow_draft_variable.py | 767 +++++++++++++++++ .../test_workflow_human_input_debug_api.py | 14 +- .../console/app/test_workflow_run_api.py | 35 +- .../app/workflow_draft_variables_test.py | 413 --------- .../test_rag_pipeline_draft_variable.py | 219 ++++- .../console/snippets/test_snippet_workflow.py | 268 ++++-- .../test_snippet_workflow_draft_variable.py | 337 ++++++-- .../api/console/account/types.gen.ts | 20 +- .../generated/api/console/account/zod.gen.ts | 24 +- .../generated/api/console/apps/orpc.gen.ts | 754 ++++------------ .../generated/api/console/apps/types.gen.ts | 483 ++--------- .../generated/api/console/apps/zod.gen.ts | 622 +++++--------- .../generated/api/console/rag/orpc.gen.ts | 355 ++------ .../generated/api/console/rag/types.gen.ts | 500 ++++++----- .../generated/api/console/rag/zod.gen.ts | 609 +++++++------ .../api/console/snippets/orpc.gen.ts | 74 +- .../api/console/snippets/types.gen.ts | 163 ++-- .../generated/api/console/snippets/zod.gen.ts | 199 ++--- .../api/console/workspaces/types.gen.ts | 10 +- .../api/console/workspaces/zod.gen.ts | 14 +- .../generated/api/service/types.gen.ts | 4 +- .../generated/api/service/zod.gen.ts | 6 +- packages/contracts/openapi-ts.api.config.ts | 78 +- 37 files changed, 4177 insertions(+), 4140 deletions(-) create mode 100644 api/tests/unit_tests/controllers/console/app/test_workflow_draft_variable.py delete mode 100644 api/tests/unit_tests/controllers/console/app/workflow_draft_variables_test.py diff --git a/api/controllers/console/app/workflow.py b/api/controllers/console/app/workflow.py index aff32035233..e92d3266d9e 100644 --- a/api/controllers/console/app/workflow.py +++ b/api/controllers/console/app/workflow.py @@ -1,19 +1,19 @@ import json import logging -from collections.abc import Sequence +from collections.abc import Mapping, Sequence from datetime import datetime -from typing import Any, NotRequired, TypedDict, cast +from typing import Any, Literal, NotRequired, TypedDict, cast from flask import abort, request -from flask_restx import Resource, fields -from pydantic import AliasChoices, BaseModel, Field, RootModel, ValidationError, field_validator +from flask_restx import Resource +from pydantic import AliasChoices, BaseModel, Field, ValidationError, field_validator from sqlalchemy.orm import Session, sessionmaker from werkzeug.exceptions import BadRequest, Forbidden, InternalServerError, NotFound import services from controllers.common.controller_schemas import DefaultBlockConfigQuery, WorkflowListQuery, WorkflowUpdatePayload from controllers.common.errors import InvalidArgumentError -from controllers.common.fields import GeneratedAppResponse, NewAppResponse, SimpleResultResponse +from controllers.common.fields import NewAppResponse, SimpleResultResponse from controllers.common.schema import ( query_params_from_model, register_response_schema_model, @@ -21,11 +21,7 @@ from controllers.common.schema import ( register_schema_models, ) from controllers.console import console_ns -from controllers.console.app.error import ( - ConversationCompletedError, - DraftWorkflowNotExist, - DraftWorkflowNotSync, -) +from controllers.console.app.error import ConversationCompletedError, DraftWorkflowNotExist, DraftWorkflowNotSync from controllers.console.app.permission_keys import get_app_permission_keys from controllers.console.app.wraps import get_app_model from controllers.console.wraps import ( @@ -58,7 +54,7 @@ from extensions.ext_database import db from extensions.ext_redis import redis_client from factories import file_factory, variable_factory from fields.base import ResponseModel -from fields.member_fields import SimpleAccount +from fields.member_fields import SimpleAccountResponse from fields.workflow_run_fields import WorkflowRunNodeExecutionResponse from graphon.enums import NodeType from graphon.file import File @@ -69,7 +65,7 @@ from graphon.variables import SecretVariable, SegmentType, VariableBase from graphon.variables.exc import VariableError from libs import helper from libs.datetime_utils import naive_utc_now -from libs.helper import TimestampField, dump_response, to_timestamp, uuid_value +from libs.helper import dump_response, to_timestamp, uuid_value from libs.login import login_required from models import Account, App from models.model import AppMode @@ -103,20 +99,16 @@ class SyncDraftWorkflowPayload(BaseModel): graph: dict[str, Any] features: dict[str, Any] hash: str | None = None - environment_variables: list[dict[str, Any]] = Field( - default_factory=list, - ) - conversation_variables: list[dict[str, Any]] = Field( - default_factory=list, - ) + environment_variables: list[dict[str, Any]] = Field(default_factory=list) + conversation_variables: list[dict[str, Any]] = Field(default_factory=list) class BaseWorkflowRunPayload(BaseModel): - files: list[dict[str, Any]] | None = Field(default=None) + files: list[dict[str, Any]] | None = None class AdvancedChatWorkflowRunPayload(BaseWorkflowRunPayload): - inputs: dict[str, Any] | None = Field(default=None) + inputs: dict[str, Any] | None = None query: str = "" conversation_id: str | None = None parent_message_id: str | None = None @@ -130,11 +122,11 @@ class AdvancedChatWorkflowRunPayload(BaseWorkflowRunPayload): class IterationNodeRunPayload(BaseModel): - inputs: dict[str, Any] | None = Field(default=None) + inputs: dict[str, Any] | None = None class LoopNodeRunPayload(BaseModel): - inputs: dict[str, Any] | None = Field(default=None) + inputs: dict[str, Any] | None = None class DraftWorkflowRunPayload(BaseWorkflowRunPayload): @@ -159,10 +151,7 @@ class ConvertToWorkflowPayload(BaseModel): class WorkflowFeaturesPayload(BaseModel): - features: dict[str, Any] = Field( - ..., - description="Workflow feature configuration", - ) + features: dict[str, Any] = Field(..., description="Workflow feature configuration") class WorkflowOnlineUsersPayload(BaseModel): @@ -197,7 +186,7 @@ class PipelineVariableResponse(ResponseModel): max_length: int | None = None required: bool unit: str | None = None - default_value: Any = Field(default=None) + default_value: Any = None options: list[str] | None = None placeholder: str | None = None tooltips: str | None = None @@ -220,21 +209,17 @@ class WorkflowEnvironmentVariableResponse(ResponseModel): class WorkflowResponse(ResponseModel): id: str - graph: dict[str, Any] = Field( - validation_alias=AliasChoices("graph_dict", "graph"), - ) - features: dict[str, Any] = Field( - validation_alias=AliasChoices("features_dict", "features"), - ) + graph: dict[str, Any] = Field(validation_alias=AliasChoices("graph_dict", "graph")) + features: dict[str, Any] = Field(validation_alias=AliasChoices("features_dict", "features")) hash: str = Field(validation_alias=AliasChoices("unique_hash", "hash")) version: str marked_name: str marked_comment: str - created_by: SimpleAccount | None = Field( + created_by: SimpleAccountResponse | None = Field( default=None, validation_alias=AliasChoices("created_by_account", "created_by") ) created_at: int - updated_by: SimpleAccount | None = Field( + updated_by: SimpleAccountResponse | None = Field( default=None, validation_alias=AliasChoices("updated_by_account", "updated_by") ) updated_at: int @@ -267,6 +252,53 @@ class WorkflowPaginationResponse(ResponseModel): has_more: bool +class SyncDraftWorkflowResponse(ResponseModel): + result: str + hash: str + updated_at: int + + +class PublishWorkflowResponse(ResponseModel): + result: str + created_at: int + + +class HumanInputUserActionResponse(ResponseModel): + id: str + title: str + button_style: str = "default" + + +class HumanInputFormPreviewResponse(ResponseModel): + # Draft previews are shape-compatible with live human_input_required events, + # but they do not persist a form or mint recipient tokens (no expiration_time) + type_: Literal["human_input_required"] = Field(default="human_input_required", alias="TYPE") + form_id: str + form_content: str + inputs: list[Mapping[str, Any]] = Field(default_factory=list) + actions: list[HumanInputUserActionResponse] = Field(default_factory=list) + node_id: str + node_title: str + resolved_default_values: Mapping[str, Any] = Field(default_factory=dict) + display_in_ui: bool = Field(default=False, description="Always false for draft preview responses.") + form_token: str | None = Field(default=None, description="Always null for draft preview responses.") + expiration_time: int | None = Field(default=None, description="Always null for draft preview responses.") + + +class HumanInputDeliveryTestResponse(ResponseModel): + pass + + +class TriggerDebugWaitingResponse(ResponseModel): + status: Literal["waiting"] + retry_in: int + + +class TriggerDebugErrorResponse(ResponseModel): + status: Literal["error"] + error: str | None = None + + class WorkflowOnlineUser(ResponseModel): user_id: str username: str @@ -282,46 +314,6 @@ class WorkflowOnlineUsersResponse(ResponseModel): data: list[WorkflowOnlineUsersByApp] -class WorkflowPublishResponse(ResponseModel): - result: str - created_at: int - - -class WorkflowRestoreResponse(ResponseModel): - result: str - hash: str - updated_at: int - - -class DefaultBlockConfigsResponse(RootModel[list[dict[str, Any]]]): - root: list[dict[str, Any]] - - -class DefaultBlockConfigResponse(RootModel[dict[str, Any]]): - root: dict[str, Any] - - -class HumanInputFormPreviewResponse(ResponseModel): - form_id: str - node_id: str - node_title: str - form_content: str - inputs: list[dict[str, Any]] = Field(default_factory=list) - actions: list[dict[str, Any]] = Field(default_factory=list) - display_in_ui: bool | None = None - form_token: str | None = None - resolved_default_values: dict[str, Any] = Field(default_factory=dict) - expiration_time: int | None = None - - -class HumanInputFormSubmitResponse(RootModel[dict[str, Any]]): - root: dict[str, Any] - - -class EmptyObjectResponse(RootModel[dict[str, Any]]): - root: dict[str, Any] - - class DraftWorkflowTriggerRunPayload(BaseModel): node_id: str @@ -356,19 +348,19 @@ register_response_schema_models( WorkflowEnvironmentVariableResponse, WorkflowResponse, WorkflowPaginationResponse, + SyncDraftWorkflowResponse, + PublishWorkflowResponse, + HumanInputUserActionResponse, + HumanInputFormPreviewResponse, + HumanInputDeliveryTestResponse, + TriggerDebugWaitingResponse, + TriggerDebugErrorResponse, WorkflowOnlineUser, WorkflowOnlineUsersByApp, WorkflowOnlineUsersResponse, - WorkflowPublishResponse, - WorkflowRestoreResponse, - DefaultBlockConfigsResponse, - DefaultBlockConfigResponse, - HumanInputFormPreviewResponse, - HumanInputFormSubmitResponse, - EmptyObjectResponse, - GeneratedAppResponse, NewAppResponse, SimpleResultResponse, + WorkflowRunNodeExecutionResponse, ) @@ -426,6 +418,14 @@ def _serialize_environment_variable(value: Any) -> EnvironmentVariableResponseDi return value +def _trigger_debug_waiting_response() -> dict[str, int | str]: + return TriggerDebugWaitingResponse(status="waiting", retry_in=LISTENING_RETRY_IN).model_dump(mode="json") + + +def _trigger_debug_error_response(error: str | None = None) -> dict[str, str]: + return TriggerDebugErrorResponse(status="error", error=error).model_dump(mode="json", exclude_none=True) + + @console_ns.route("/apps//workflows/draft") class DraftWorkflowApi(Resource): @console_ns.doc("get_draft_workflow") @@ -475,14 +475,7 @@ class DraftWorkflowApi(Resource): @console_ns.response( 200, "Draft workflow synced successfully", - console_ns.model( - "SyncDraftWorkflowResponse", - { - "result": fields.String, - "hash": fields.String, - "updated_at": fields.String, - }, - ), + console_ns.models[SyncDraftWorkflowResponse.__name__], ) @console_ns.response(400, "Invalid workflow configuration") @console_ns.response(403, "Permission denied") @@ -535,11 +528,11 @@ class DraftWorkflowApi(Resource): except VariableError as e: raise InvalidArgumentError(description=str(e)) - return { - "result": "success", - "hash": workflow.unique_hash, - "updated_at": TimestampField().format(workflow.updated_at or workflow.created_at), - } + return SyncDraftWorkflowResponse( + result="success", + hash=workflow.unique_hash, + updated_at=to_timestamp(workflow.updated_at or workflow.created_at), + ).model_dump(mode="json") @console_ns.route("/apps//advanced-chat/workflows/draft/run") @@ -548,7 +541,7 @@ class AdvancedChatDraftWorkflowRunApi(Resource): @console_ns.doc(description="Run draft workflow for advanced chat application") @console_ns.doc(params={"app_id": "Application ID"}) @console_ns.expect(console_ns.models[AdvancedChatWorkflowRunPayload.__name__]) - @console_ns.response(200, "Workflow run started successfully", console_ns.models[GeneratedAppResponse.__name__]) + @console_ns.response(200, "Workflow run started successfully") @console_ns.response(400, "Invalid request parameters") @console_ns.response(403, "Permission denied") @setup_required @@ -574,6 +567,7 @@ class AdvancedChatDraftWorkflowRunApi(Resource): app_model=app_model, user=current_user, args=args, invoke_from=InvokeFrom.DEBUGGER, streaming=True ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except services.errors.conversation.ConversationNotExistsError: raise NotFound("Conversation Not Exists.") @@ -594,11 +588,7 @@ class AdvancedChatDraftRunIterationNodeApi(Resource): @console_ns.doc(description="Run draft workflow iteration node for advanced chat") @console_ns.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) @console_ns.expect(console_ns.models[IterationNodeRunPayload.__name__]) - @console_ns.response( - 200, - "Iteration node run started successfully", - console_ns.models[GeneratedAppResponse.__name__], - ) + @console_ns.response(200, "Iteration node run started successfully") @console_ns.response(403, "Permission denied") @console_ns.response(404, "Node not found") @setup_required @@ -619,6 +609,7 @@ class AdvancedChatDraftRunIterationNodeApi(Resource): app_model=app_model, user=current_user, node_id=node_id, args=args, streaming=True ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except services.errors.conversation.ConversationNotExistsError: raise NotFound("Conversation Not Exists.") @@ -637,11 +628,7 @@ class WorkflowDraftRunIterationNodeApi(Resource): @console_ns.doc(description="Run draft workflow iteration node") @console_ns.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) @console_ns.expect(console_ns.models[IterationNodeRunPayload.__name__]) - @console_ns.response( - 200, - "Workflow iteration node run started successfully", - console_ns.models[GeneratedAppResponse.__name__], - ) + @console_ns.response(200, "Workflow iteration node run started successfully") @console_ns.response(403, "Permission denied") @console_ns.response(404, "Node not found") @setup_required @@ -662,6 +649,7 @@ class WorkflowDraftRunIterationNodeApi(Resource): app_model=app_model, user=current_user, node_id=node_id, args=args, streaming=True ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except services.errors.conversation.ConversationNotExistsError: raise NotFound("Conversation Not Exists.") @@ -680,7 +668,7 @@ class AdvancedChatDraftRunLoopNodeApi(Resource): @console_ns.doc(description="Run draft workflow loop node for advanced chat") @console_ns.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) @console_ns.expect(console_ns.models[LoopNodeRunPayload.__name__]) - @console_ns.response(200, "Loop node run started successfully", console_ns.models[GeneratedAppResponse.__name__]) + @console_ns.response(200, "Loop node run started successfully") @console_ns.response(403, "Permission denied") @console_ns.response(404, "Node not found") @setup_required @@ -701,6 +689,7 @@ class AdvancedChatDraftRunLoopNodeApi(Resource): app_model=app_model, user=current_user, node_id=node_id, args=args, streaming=True ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except services.errors.conversation.ConversationNotExistsError: raise NotFound("Conversation Not Exists.") @@ -719,11 +708,7 @@ class WorkflowDraftRunLoopNodeApi(Resource): @console_ns.doc(description="Run draft workflow loop node") @console_ns.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) @console_ns.expect(console_ns.models[LoopNodeRunPayload.__name__]) - @console_ns.response( - 200, - "Workflow loop node run started successfully", - console_ns.models[GeneratedAppResponse.__name__], - ) + @console_ns.response(200, "Workflow loop node run started successfully") @console_ns.response(403, "Permission denied") @console_ns.response(404, "Node not found") @setup_required @@ -744,6 +729,7 @@ class WorkflowDraftRunLoopNodeApi(Resource): app_model=app_model, user=current_user, node_id=node_id, args=args, streaming=True ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except services.errors.conversation.ConversationNotExistsError: raise NotFound("Conversation Not Exists.") @@ -764,10 +750,7 @@ class HumanInputFormPreviewPayload(BaseModel): class HumanInputFormSubmitPayload(BaseModel): - form_inputs: dict[str, Any] = Field( - ..., - description="Values the user provides for the form's own fields", - ) + form_inputs: dict[str, Any] = Field(..., description="Values the user provides for the form's own fields") inputs: dict[str, Any] = Field( ..., description="Values used to fill missing upstream variables referenced in form_content", @@ -797,7 +780,11 @@ class AdvancedChatDraftHumanInputFormPreviewApi(Resource): @console_ns.doc(description="Get human input form preview for advanced chat workflow") @console_ns.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) @console_ns.expect(console_ns.models[HumanInputFormPreviewPayload.__name__]) - @console_ns.response(200, "Human input form preview", console_ns.models[HumanInputFormPreviewResponse.__name__]) + @console_ns.response( + 200, + "Human input form preview retrieved", + console_ns.models[HumanInputFormPreviewResponse.__name__], + ) @setup_required @login_required @account_initialization_required @@ -819,7 +806,7 @@ class AdvancedChatDraftHumanInputFormPreviewApi(Resource): node_id=node_id, inputs=inputs, ) - return jsonable_encoder(preview) + return dump_response(HumanInputFormPreviewResponse, preview) @console_ns.route("/apps//advanced-chat/workflows/draft/human-input/nodes//form/run") @@ -830,8 +817,7 @@ class AdvancedChatDraftHumanInputFormRunApi(Resource): @console_ns.expect(console_ns.models[HumanInputFormSubmitPayload.__name__]) @console_ns.response( 200, - "Human input form submission result", - console_ns.models[HumanInputFormSubmitResponse.__name__], + "Human input form submitted", ) @setup_required @login_required @@ -854,6 +840,7 @@ class AdvancedChatDraftHumanInputFormRunApi(Resource): inputs=args.inputs, action=args.action, ) + # TODO: typing here for result return jsonable_encoder(result) @@ -863,7 +850,11 @@ class WorkflowDraftHumanInputFormPreviewApi(Resource): @console_ns.doc(description="Get human input form preview for workflow") @console_ns.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) @console_ns.expect(console_ns.models[HumanInputFormPreviewPayload.__name__]) - @console_ns.response(200, "Human input form preview", console_ns.models[HumanInputFormPreviewResponse.__name__]) + @console_ns.response( + 200, + "Human input form preview retrieved", + console_ns.models[HumanInputFormPreviewResponse.__name__], + ) @setup_required @login_required @account_initialization_required @@ -885,7 +876,7 @@ class WorkflowDraftHumanInputFormPreviewApi(Resource): node_id=node_id, inputs=inputs, ) - return jsonable_encoder(preview) + return dump_response(HumanInputFormPreviewResponse, preview) @console_ns.route("/apps//workflows/draft/human-input/nodes//form/run") @@ -896,8 +887,7 @@ class WorkflowDraftHumanInputFormRunApi(Resource): @console_ns.expect(console_ns.models[HumanInputFormSubmitPayload.__name__]) @console_ns.response( 200, - "Human input form submission result", - console_ns.models[HumanInputFormSubmitResponse.__name__], + "Human input form submitted", ) @setup_required @login_required @@ -920,6 +910,7 @@ class WorkflowDraftHumanInputFormRunApi(Resource): inputs=args.inputs, action=args.action, ) + # TODO: typing here return jsonable_encoder(result) @@ -929,7 +920,11 @@ class WorkflowDraftHumanInputDeliveryTestApi(Resource): @console_ns.doc(description="Test human input delivery for workflow") @console_ns.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) @console_ns.expect(console_ns.models[HumanInputDeliveryTestPayload.__name__]) - @console_ns.response(200, "Human input delivery test result", console_ns.models[EmptyObjectResponse.__name__]) + @console_ns.response( + 200, + "Human input delivery tested", + console_ns.models[HumanInputDeliveryTestResponse.__name__], + ) @setup_required @login_required @account_initialization_required @@ -950,7 +945,7 @@ class WorkflowDraftHumanInputDeliveryTestApi(Resource): delivery_method_id=args.delivery_method_id, inputs=args.inputs, ) - return jsonable_encoder({}) + return HumanInputDeliveryTestResponse().model_dump(mode="json") @console_ns.route("/apps//workflows/draft/run") @@ -959,11 +954,7 @@ class DraftWorkflowRunApi(Resource): @console_ns.doc(description="Run draft workflow") @console_ns.doc(params={"app_id": "Application ID"}) @console_ns.expect(console_ns.models[DraftWorkflowRunPayload.__name__]) - @console_ns.response( - 200, - "Draft workflow run started successfully", - console_ns.models[GeneratedAppResponse.__name__], - ) + @console_ns.response(200, "Draft workflow run started successfully") @console_ns.response(403, "Permission denied") @setup_required @login_required @@ -991,6 +982,7 @@ class DraftWorkflowRunApi(Resource): streaming=True, ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except InvokeRateLimitError as ex: raise InvokeRateLimitHttpError(ex.description) @@ -1021,7 +1013,7 @@ class WorkflowTaskStopApi(Resource): # New graph engine command channel mechanism GraphEngineManager(redis_client).send_stop_command(task_id) - return {"result": "success"} + return SimpleResultResponse(result="success").model_dump(mode="json") @console_ns.route("/apps//workflows/draft/nodes//run") @@ -1109,7 +1101,7 @@ class PublishedWorkflowApi(Resource): return dump_response(WorkflowResponse, workflow) @console_ns.expect(console_ns.models[PublishWorkflowPayload.__name__]) - @console_ns.response(200, "Workflow published successfully", console_ns.models[WorkflowPublishResponse.__name__]) + @console_ns.response(200, "Workflow published successfully", console_ns.models[PublishWorkflowResponse.__name__]) @setup_required @login_required @account_initialization_required @@ -1141,12 +1133,9 @@ class PublishedWorkflowApi(Resource): app_model_in_session.updated_by = current_user.id app_model_in_session.updated_at = naive_utc_now() - workflow_created_at = TimestampField().format(workflow.created_at) + workflow_created_at = to_timestamp(workflow.created_at) - return { - "result": "success", - "created_at": workflow_created_at, - } + return PublishWorkflowResponse(result="success", created_at=workflow_created_at).model_dump(mode="json") @console_ns.route("/apps//workflows/default-workflow-block-configs") @@ -1157,7 +1146,6 @@ class DefaultBlockConfigsApi(Resource): @console_ns.response( 200, "Default block configurations retrieved successfully", - console_ns.models[DefaultBlockConfigsResponse.__name__], ) @setup_required @login_required @@ -1179,13 +1167,12 @@ class DefaultBlockConfigApi(Resource): @console_ns.doc("get_default_block_config") @console_ns.doc(description="Get default block configuration by type") @console_ns.doc(params={"app_id": "Application ID", "block_type": "Block type"}) + @console_ns.doc(params=query_params_from_model(DefaultBlockConfigQuery)) @console_ns.response( 200, "Default block configuration retrieved successfully", - console_ns.models[DefaultBlockConfigResponse.__name__], ) @console_ns.response(404, "Block type not found") - @console_ns.doc(params=query_params_from_model(DefaultBlockConfigQuery)) @setup_required @login_required @account_initialization_required @@ -1279,15 +1266,14 @@ class WorkflowFeaturesApi(Resource): workflow_service = WorkflowService() workflow_service.update_draft_workflow_features(app_model=app_model, features=features, account=current_user) - return {"result": "success"} + return SimpleResultResponse(result="success").model_dump(mode="json") @console_ns.route("/apps//workflows") class PublishedAllWorkflowApi(Resource): - @console_ns.doc(params=query_params_from_model(WorkflowListQuery)) @console_ns.doc("get_all_published_workflows") @console_ns.doc(description="Get all published workflows for an application") - @console_ns.doc(params={"app_id": "Application ID"}) + @console_ns.doc(params={"app_id": "Application ID", **query_params_from_model(WorkflowListQuery)}) @console_ns.response( 200, "Published workflows retrieved successfully", @@ -1340,7 +1326,11 @@ class DraftWorkflowRestoreApi(Resource): @console_ns.doc("restore_workflow_to_draft") @console_ns.doc(description="Restore a published workflow version into the draft workflow") @console_ns.doc(params={"app_id": "Application ID", "workflow_id": "Published workflow ID"}) - @console_ns.response(200, "Workflow restored successfully", console_ns.models[WorkflowRestoreResponse.__name__]) + @console_ns.response( + 200, + "Workflow restored successfully", + console_ns.models[SyncDraftWorkflowResponse.__name__], + ) @console_ns.response(400, "Source workflow must be published") @console_ns.response(404, "Workflow not found") @setup_required @@ -1366,11 +1356,11 @@ class DraftWorkflowRestoreApi(Resource): except ValueError as exc: raise BadRequest(str(exc)) from exc - return { - "result": "success", - "hash": workflow.unique_hash, - "updated_at": TimestampField().format(workflow.updated_at or workflow.created_at), - } + return SyncDraftWorkflowResponse( + result="success", + hash=workflow.unique_hash, + updated_at=to_timestamp(workflow.updated_at or workflow.created_at), + ).model_dump(mode="json") @console_ns.route("/apps//workflows/") @@ -1422,6 +1412,7 @@ class WorkflowByIdApi(Resource): return dump_response(WorkflowResponse, workflow) + @console_ns.response(204, "Workflow deleted successfully") @setup_required @login_required @account_initialization_required @@ -1480,7 +1471,7 @@ class DraftWorkflowNodeLastRunApi(Resource): ) if node_exec is None: raise NotFound("last run not found") - return WorkflowRunNodeExecutionResponse.model_validate(node_exec, from_attributes=True).model_dump(mode="json") + return dump_response(WorkflowRunNodeExecutionResponse, node_exec) @console_ns.route("/apps//workflows/draft/trigger/run") @@ -1493,19 +1484,8 @@ class DraftWorkflowTriggerRunApi(Resource): @console_ns.doc("poll_draft_workflow_trigger_run") @console_ns.doc(description="Poll for trigger events and execute full workflow when event arrives") @console_ns.doc(params={"app_id": "Application ID"}) - @console_ns.expect( - console_ns.model( - "DraftWorkflowTriggerRunRequest", - { - "node_id": fields.String(required=True, description="Node ID"), - }, - ) - ) - @console_ns.response( - 200, - "Trigger event received and workflow executed successfully", - console_ns.models[GeneratedAppResponse.__name__], - ) + @console_ns.expect(console_ns.models[DraftWorkflowTriggerRunPayload.__name__]) + @console_ns.response(200, "Trigger event received and workflow executed successfully") @console_ns.response(403, "Permission denied") @console_ns.response(500, "Internal server error") @setup_required @@ -1537,10 +1517,11 @@ class DraftWorkflowTriggerRunApi(Resource): try: event = poller.poll() if not event: - return jsonable_encoder({"status": "waiting", "retry_in": LISTENING_RETRY_IN}) + return _trigger_debug_waiting_response() workflow_args = dict(event.workflow_args) workflow_args[SKIP_PREPARE_USER_INPUTS_KEY] = True + # response-contract:ignore compact_generate_response return helper.compact_generate_response( AppGenerateService.generate( app_model=app_model, @@ -1554,7 +1535,7 @@ class DraftWorkflowTriggerRunApi(Resource): except InvokeRateLimitError as ex: raise InvokeRateLimitHttpError(ex.description) except PluginInvokeError as e: - return jsonable_encoder({"status": "error", "error": e.to_user_friendly_error()}), 400 + return _trigger_debug_error_response(e.to_user_friendly_error()), 400 except Exception as e: logger.exception("Error polling trigger debug event") raise e @@ -1573,7 +1554,7 @@ class DraftWorkflowTriggerNodeApi(Resource): @console_ns.response( 200, "Trigger event received and node executed successfully", - console_ns.models[GeneratedAppResponse.__name__], + console_ns.models[WorkflowRunNodeExecutionResponse.__name__], ) @console_ns.response(403, "Permission denied") @console_ns.response(500, "Internal server error") @@ -1617,12 +1598,12 @@ class DraftWorkflowTriggerNodeApi(Resource): ) event = poller.poll() except PluginInvokeError as e: - return jsonable_encoder({"status": "error", "error": e.to_user_friendly_error()}), 400 + return _trigger_debug_error_response(e.to_user_friendly_error()), 400 except Exception as e: logger.exception("Error polling trigger debug event") raise e if not event: - return jsonable_encoder({"status": "waiting", "retry_in": LISTENING_RETRY_IN}) + return _trigger_debug_waiting_response() raw_files = event.workflow_args.get("files") files = _parse_file(draft_workflow, raw_files if isinstance(raw_files, list) else None) @@ -1636,12 +1617,10 @@ class DraftWorkflowTriggerNodeApi(Resource): query="", files=files, ) - return jsonable_encoder(node_execution) + return dump_response(WorkflowRunNodeExecutionResponse, node_execution) except Exception as e: logger.exception("Error running draft workflow trigger node") - return jsonable_encoder( - {"status": "error", "error": "An unexpected error occurred while running the node."} - ), 400 + return _trigger_debug_error_response("An unexpected error occurred while running the node."), 400 @console_ns.route("/apps//workflows/draft/trigger/run-all") @@ -1655,7 +1634,7 @@ class DraftWorkflowTriggerRunAllApi(Resource): @console_ns.doc(description="Full workflow debug when the start node is a trigger") @console_ns.doc(params={"app_id": "Application ID"}) @console_ns.expect(console_ns.models[DraftWorkflowTriggerRunAllPayload.__name__]) - @console_ns.response(200, "Workflow executed successfully", console_ns.models[GeneratedAppResponse.__name__]) + @console_ns.response(200, "Workflow executed successfully") @console_ns.response(403, "Permission denied") @console_ns.response(500, "Internal server error") @setup_required @@ -1685,12 +1664,12 @@ class DraftWorkflowTriggerRunAllApi(Resource): node_ids=node_ids, ) except PluginInvokeError as e: - return jsonable_encoder({"status": "error", "error": e.to_user_friendly_error()}), 400 + return _trigger_debug_error_response(e.to_user_friendly_error()), 400 except Exception as e: logger.exception("Error polling trigger debug event") raise e if trigger_debug_event is None: - return jsonable_encoder({"status": "waiting", "retry_in": LISTENING_RETRY_IN}) + return _trigger_debug_waiting_response() try: workflow_args = dict(trigger_debug_event.workflow_args) @@ -1704,16 +1683,13 @@ class DraftWorkflowTriggerRunAllApi(Resource): streaming=True, root_node_id=trigger_debug_event.node_id, ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except InvokeRateLimitError as ex: raise InvokeRateLimitHttpError(ex.description) except Exception: logger.exception("Error running draft workflow trigger run-all") - return jsonable_encoder( - { - "status": "error", - } - ), 400 + return _trigger_debug_error_response(), 400 @console_ns.route("/apps/workflows/online-users") @@ -1738,7 +1714,7 @@ class WorkflowOnlineUsersApi(Resource): raise BadRequest(f"Maximum {MAX_WORKFLOW_ONLINE_USERS_REQUEST_IDS} app_ids are allowed per request.") if not app_ids: - return {"data": []} + return WorkflowOnlineUsersResponse(data=[]).model_dump(mode="json") workflow_service = WorkflowService() accessible_app_ids = workflow_service.get_accessible_app_ids(app_ids, current_tenant_id) diff --git a/api/controllers/console/app/workflow_app_log.py b/api/controllers/console/app/workflow_app_log.py index b3426e8f6ea..92c70995b9e 100644 --- a/api/controllers/console/app/workflow_app_log.py +++ b/api/controllers/console/app/workflow_app_log.py @@ -20,7 +20,7 @@ from controllers.console.wraps import ( from extensions.ext_database import db from fields.base import ResponseModel from fields.end_user_fields import SimpleEndUser -from fields.member_fields import SimpleAccount +from fields.member_fields import SimpleAccountResponse from graphon.enums import WorkflowExecutionStatus from libs.helper import to_timestamp from libs.login import login_required @@ -115,7 +115,7 @@ class WorkflowAppLogPartialResponse(ResponseModel): details: Any = None created_from: str | None = None created_by_role: str | None = None - created_by_account: SimpleAccount | None = None + created_by_account: SimpleAccountResponse | None = None created_by_end_user: SimpleEndUser | None = None created_at: int | None = None @@ -129,7 +129,7 @@ class WorkflowArchivedLogPartialResponse(ResponseModel): id: str workflow_run: WorkflowRunForArchivedLogResponse | None = None trigger_metadata: Any = None - created_by_account: SimpleAccount | None = None + created_by_account: SimpleAccountResponse | None = None created_by_end_user: SimpleEndUser | None = None created_at: int | None = None diff --git a/api/controllers/console/app/workflow_comment.py b/api/controllers/console/app/workflow_comment.py index c70f00dcfa1..20c05d2952b 100644 --- a/api/controllers/console/app/workflow_comment.py +++ b/api/controllers/console/app/workflow_comment.py @@ -2,7 +2,7 @@ import logging from datetime import datetime from flask_restx import Resource -from pydantic import BaseModel, Field, TypeAdapter, computed_field, field_validator +from pydantic import BaseModel, Field, computed_field, field_validator from controllers.common.schema import register_response_schema_models, register_schema_models from controllers.console import console_ns @@ -19,7 +19,7 @@ from controllers.console.wraps import ( ) from extensions.ext_database import db from fields.base import ResponseModel -from fields.member_fields import AccountWithRole +from fields.member_fields import AccountWithRoleResponse from libs.helper import build_avatar_url, dump_response, to_timestamp from libs.login import login_required from models import Account, App @@ -52,7 +52,7 @@ class WorkflowCommentReplyPayload(BaseModel): class WorkflowCommentMentionUsersPayload(BaseModel): - users: list[AccountWithRole] + users: list[AccountWithRoleResponse] class WorkflowCommentAccount(ResponseModel): @@ -189,7 +189,7 @@ class WorkflowCommentReplyUpdate(ResponseModel): register_schema_models( console_ns, - AccountWithRole, + AccountWithRoleResponse, WorkflowCommentMentionUsersPayload, WorkflowCommentCreatePayload, WorkflowCommentUpdatePayload, @@ -491,6 +491,4 @@ class WorkflowCommentMentionUsersApi(Resource): if current_tenant is None: raise ValueError("current tenant is required") members = TenantService.get_tenant_members(current_tenant, session=db.session) - users = TypeAdapter(list[AccountWithRole]).validate_python(members, from_attributes=True) - response = WorkflowCommentMentionUsersPayload(users=users) - return response.model_dump(mode="json"), 200 + return dump_response(WorkflowCommentMentionUsersPayload, {"users": members}), 200 diff --git a/api/controllers/console/app/workflow_draft_variable.py b/api/controllers/console/app/workflow_draft_variable.py index ff82572b87e..5811b3b217a 100644 --- a/api/controllers/console/app/workflow_draft_variable.py +++ b/api/controllers/console/app/workflow_draft_variable.py @@ -1,11 +1,11 @@ import logging from collections.abc import Callable from functools import wraps -from typing import Any, Concatenate, TypedDict, override +from typing import Any, Concatenate, Self, override from uuid import UUID from flask import Response, request -from flask_restx import Resource, fields, marshal, marshal_with +from flask_restx import Resource from pydantic import BaseModel, Field from sqlalchemy.orm import sessionmaker @@ -47,28 +47,6 @@ logger = logging.getLogger(__name__) _file_access_controller = DatabaseFileAccessController() -class OpaqueRawField(fields.Raw): - @override - def schema(self) -> dict[str, object]: - return {"type": "object"} - - -class JsonValueRawField(fields.Raw): - @override - def schema(self) -> dict[str, object]: - return { - "anyOf": [ - {"type": "string"}, - {"type": "integer"}, - {"type": "number"}, - {"type": "boolean"}, - {"type": "object", "additionalProperties": True}, - {"type": "array", "items": {}}, - {"type": "null"}, - ] - } - - class WorkflowDraftVariableListQuery(BaseModel): page: int = Field(default=1, ge=1, le=100_000, description="Page number") limit: int = Field(default=20, ge=1, le=100, description="Items per page") @@ -76,24 +54,111 @@ class WorkflowDraftVariableListQuery(BaseModel): class WorkflowDraftVariableUpdatePayload(BaseModel): name: str | None = Field(default=None, description="Variable name") - value: Any | None = Field(default=None, description="Variable value") + value: Any = Field(default=None, description="Variable value") class ConversationVariableUpdatePayload(BaseModel): conversation_variables: list[dict[str, Any]] = Field( - ..., - description="Conversation variables for the draft workflow", + ..., description="Conversation variables for the draft workflow" ) class EnvironmentVariableUpdatePayload(BaseModel): - environment_variables: list[dict[str, Any]] = Field( - ..., - description="Environment variables for the draft workflow", - ) + environment_variables: list[dict[str, Any]] = Field(..., description="Environment variables for the draft workflow") -class EnvironmentVariableItemResponse(ResponseModel): +class WorkflowDraftVariableFullContentResponse(ResponseModel): + size_bytes: int | None + value_type: str + length: int | None + download_url: str + + @classmethod + def from_workflow_draft_variable(cls, variable: WorkflowDraftVariable) -> Self | None: + if not variable.is_truncated(): + return None + + variable_file = variable.variable_file + assert variable_file is not None + + return cls( + size_bytes=variable_file.size, + value_type=str(variable_file.value_type.exposed_type()), + length=variable_file.length, + download_url=file_helpers.get_signed_file_url(variable_file.upload_file_id, as_attachment=True), + ) + + +class WorkflowDraftVariableWithoutValueResponse(ResponseModel): + id: str + type: str + name: str + description: str + selector: list[str] + value_type: str + edited: bool + visible: bool + is_truncated: bool + + @classmethod + def from_workflow_draft_variable(cls, variable: WorkflowDraftVariable) -> Self: + return cls( + id=variable.id, + type=variable.get_variable_type().value, + name=variable.name, + description=variable.description, + selector=variable.get_selector(), + value_type=_serialize_variable_type(variable), + edited=variable.edited, + visible=variable.visible, + is_truncated=variable.file_id is not None, + ) + + +class WorkflowDraftVariableResponse(WorkflowDraftVariableWithoutValueResponse): + value: Any + full_content: WorkflowDraftVariableFullContentResponse | None + + @classmethod + @override + def from_workflow_draft_variable(cls, variable: WorkflowDraftVariable) -> Self: + without_value = WorkflowDraftVariableWithoutValueResponse.from_workflow_draft_variable(variable) + return cls( + **without_value.model_dump(), + value=_serialize_var_value(variable), + full_content=WorkflowDraftVariableFullContentResponse.from_workflow_draft_variable(variable), + ) + + +class WorkflowDraftVariableListWithoutValueResponse(ResponseModel): + items: list[WorkflowDraftVariableWithoutValueResponse] + total: int | None + + @classmethod + def from_workflow_draft_variable_list(cls, variable_list: WorkflowDraftVariableList) -> Self: + return cls( + items=[ + WorkflowDraftVariableWithoutValueResponse.from_workflow_draft_variable(variable) + for variable in variable_list.variables + ], + total=variable_list.total, + ) + + +class WorkflowDraftVariableListResponse(ResponseModel): + items: list[WorkflowDraftVariableResponse] + + @classmethod + def from_workflow_draft_variable_list(cls, variable_list: WorkflowDraftVariableList) -> Self: + return cls( + items=[ + WorkflowDraftVariableResponse.from_workflow_draft_variable(variable) + for variable in variable_list.variables + ], + ) + + +class WorkflowDraftEnvironmentVariableResponse(ResponseModel): id: str type: str name: str @@ -106,8 +171,8 @@ class EnvironmentVariableItemResponse(ResponseModel): editable: bool -class EnvironmentVariableListResponse(ResponseModel): - items: list[EnvironmentVariableItemResponse] +class WorkflowDraftEnvironmentVariableListResponse(ResponseModel): + items: list[WorkflowDraftEnvironmentVariableResponse] register_schema_models( @@ -117,22 +182,33 @@ register_schema_models( ConversationVariableUpdatePayload, EnvironmentVariableUpdatePayload, ) -register_response_schema_models(console_ns, SimpleResultResponse, EnvironmentVariableListResponse) + +register_response_schema_models( + console_ns, + WorkflowDraftVariableFullContentResponse, + WorkflowDraftVariableWithoutValueResponse, + WorkflowDraftVariableResponse, + WorkflowDraftVariableListWithoutValueResponse, + WorkflowDraftVariableListResponse, + WorkflowDraftEnvironmentVariableResponse, + WorkflowDraftEnvironmentVariableListResponse, + SimpleResultResponse, +) -def _convert_values_to_json_serializable_object(value: Segment): +def _convert_values_to_json_serializable_object(value: Segment) -> Any: match value: case FileSegment(): return value.value.model_dump() case ArrayFileSegment(): - return [i.model_dump() for i in value.value] + return [file.model_dump() for file in value.value] case SegmentGroup(): return [_convert_values_to_json_serializable_object(i) for i in value.value] case _: return value.value -def _serialize_var_value(variable: WorkflowDraftVariable): +def _serialize_var_value(variable: WorkflowDraftVariable) -> Any: value = variable.get_value() # create a copy of the value to avoid affecting the model cache. value = value.model_copy(deep=True) @@ -153,30 +229,6 @@ def _serialize_variable_type(workflow_draft_var: WorkflowDraftVariable) -> str: return str(value_type.exposed_type()) -class FullContentDict(TypedDict): - size_bytes: int | None - value_type: str - length: int | None - download_url: str - - -def _serialize_full_content(variable: WorkflowDraftVariable) -> FullContentDict | None: - """Serialize full_content information for large variables.""" - if not variable.is_truncated(): - return None - - variable_file = variable.variable_file - assert variable_file is not None - - result: FullContentDict = { - "size_bytes": variable_file.size, - "value_type": str(variable_file.value_type.exposed_type()), - "length": variable_file.length, - "download_url": file_helpers.get_signed_file_url(variable_file.upload_file_id, as_attachment=True), - } - return result - - def ensure_variable_access( variable: WorkflowDraftVariable | None, app_id: str, @@ -190,83 +242,21 @@ def ensure_variable_access( return variable -_WORKFLOW_DRAFT_VARIABLE_WITHOUT_VALUE_FIELDS = { - "id": fields.String, - "type": fields.String(attribute=lambda model: model.get_variable_type()), - "name": fields.String, - "description": fields.String, - "selector": fields.List(fields.String, attribute=lambda model: model.get_selector()), - "value_type": fields.String(attribute=_serialize_variable_type), - "edited": fields.Boolean(attribute=lambda model: model.edited), - "visible": fields.Boolean, - "is_truncated": fields.Boolean(attribute=lambda model: model.file_id is not None), -} +def validate_node_id(node_id: str) -> None: + if node_id in [ + CONVERSATION_VARIABLE_NODE_ID, + SYSTEM_VARIABLE_NODE_ID, + ]: + # NOTE(QuantumGhost): While we store the system and conversation variables as node variables + # with specific `node_id` in database, we still want to make the API separated. By disallowing + # accessing system and conversation variables in `WorkflowDraftNodeVariableListApi`, + # we mitigate the risk that user of the API depending on the implementation detail of the API. + # + # ref: [Hyrum's Law](https://www.hyrumslaw.com/) -_WORKFLOW_DRAFT_VARIABLE_FIELDS = { - **_WORKFLOW_DRAFT_VARIABLE_WITHOUT_VALUE_FIELDS, - "value": JsonValueRawField(attribute=_serialize_var_value), - "full_content": OpaqueRawField(attribute=_serialize_full_content), -} - -_WORKFLOW_DRAFT_ENV_VARIABLE_FIELDS = { - "id": fields.String, - "type": fields.String(attribute=lambda _: "env"), - "name": fields.String, - "description": fields.String, - "selector": fields.List(fields.String, attribute=lambda model: model.get_selector()), - "value_type": fields.String(attribute=_serialize_variable_type), - "edited": fields.Boolean(attribute=lambda model: model.edited), - "visible": fields.Boolean, -} - -_WORKFLOW_DRAFT_ENV_VARIABLE_LIST_FIELDS = { - "items": fields.List(fields.Nested(_WORKFLOW_DRAFT_ENV_VARIABLE_FIELDS)), -} - - -def _get_items(var_list: WorkflowDraftVariableList) -> list[WorkflowDraftVariable]: - return var_list.variables - - -_WORKFLOW_DRAFT_VARIABLE_LIST_WITHOUT_VALUE_FIELDS = { - "items": fields.List(fields.Nested(_WORKFLOW_DRAFT_VARIABLE_WITHOUT_VALUE_FIELDS), attribute=_get_items), - "total": fields.Integer, -} - -_WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS = { - "items": fields.List(fields.Nested(_WORKFLOW_DRAFT_VARIABLE_FIELDS), attribute=_get_items), -} - -# Register models for flask_restx to avoid dict type issues in Swagger -workflow_draft_variable_without_value_model = console_ns.model( - "WorkflowDraftVariableWithoutValue", _WORKFLOW_DRAFT_VARIABLE_WITHOUT_VALUE_FIELDS -) - -workflow_draft_variable_model = console_ns.model("WorkflowDraftVariable", _WORKFLOW_DRAFT_VARIABLE_FIELDS) - -workflow_draft_env_variable_model = console_ns.model("WorkflowDraftEnvVariable", _WORKFLOW_DRAFT_ENV_VARIABLE_FIELDS) - -workflow_draft_env_variable_list_fields_copy = _WORKFLOW_DRAFT_ENV_VARIABLE_LIST_FIELDS.copy() -workflow_draft_env_variable_list_fields_copy["items"] = fields.List(fields.Nested(workflow_draft_env_variable_model)) -workflow_draft_env_variable_list_model = console_ns.model( - "WorkflowDraftEnvVariableList", workflow_draft_env_variable_list_fields_copy -) - -workflow_draft_variable_list_without_value_fields_copy = _WORKFLOW_DRAFT_VARIABLE_LIST_WITHOUT_VALUE_FIELDS.copy() -workflow_draft_variable_list_without_value_fields_copy["items"] = fields.List( - fields.Nested(workflow_draft_variable_without_value_model), attribute=_get_items -) -workflow_draft_variable_list_without_value_model = console_ns.model( - "WorkflowDraftVariableListWithoutValue", workflow_draft_variable_list_without_value_fields_copy -) - -workflow_draft_variable_list_fields_copy = _WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS.copy() -workflow_draft_variable_list_fields_copy["items"] = fields.List( - fields.Nested(workflow_draft_variable_model), attribute=_get_items -) -workflow_draft_variable_list_model = console_ns.model( - "WorkflowDraftVariableList", workflow_draft_variable_list_fields_copy -) + raise InvalidArgumentError( + f"invalid node_id, please use correspond api for conversation and system variables, node_id={node_id}", + ) def _api_prerequisite[T, **P, R]( @@ -296,23 +286,42 @@ def _api_prerequisite[T, **P, R]( return wrapper +def _get_variable_list(app_model: App, node_id: str, current_user_id: str) -> WorkflowDraftVariableList: + with sessionmaker(bind=db.engine, expire_on_commit=False).begin() as session: + draft_var_srv = WorkflowDraftVariableService( + session=session, + ) + if node_id == CONVERSATION_VARIABLE_NODE_ID: + draft_vars = draft_var_srv.list_conversation_variables(app_model.id, user_id=current_user_id) + elif node_id == SYSTEM_VARIABLE_NODE_ID: + draft_vars = draft_var_srv.list_system_variables(app_model.id, user_id=current_user_id) + else: + draft_vars = draft_var_srv.list_node_variables( + app_id=app_model.id, + node_id=node_id, + user_id=current_user_id, + ) + return draft_vars + + @console_ns.route("/apps//workflows/draft/variables") class WorkflowVariableCollectionApi(Resource): - @console_ns.doc(params=query_params_from_model(WorkflowDraftVariableListQuery)) @console_ns.doc("get_workflow_variables") @console_ns.doc(description="Get draft workflow variables") @console_ns.doc(params={"app_id": "Application ID"}) - @console_ns.doc(params={"page": "Page number (1-100000)", "limit": "Number of items per page (1-100)"}) + @console_ns.doc(params=query_params_from_model(WorkflowDraftVariableListQuery)) @console_ns.response( - 200, "Workflow variables retrieved successfully", workflow_draft_variable_list_without_value_model + 200, + "Workflow variables retrieved successfully", + console_ns.models[WorkflowDraftVariableListWithoutValueResponse.__name__], ) @_api_prerequisite - @marshal_with(workflow_draft_variable_list_without_value_model) @rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_VIEW_LAYOUT) def get(self, current_user: Account, app_model: App): """ Get draft workflow """ + # response-contract:ignore constructed Pydantic response args = WorkflowDraftVariableListQuery.model_validate(request.args.to_dict(flat=True)) # fetch draft workflow by app_model @@ -333,7 +342,9 @@ class WorkflowVariableCollectionApi(Resource): user_id=current_user.id, ) - return workflow_vars + return WorkflowDraftVariableListWithoutValueResponse.from_workflow_draft_variable_list( + workflow_vars + ).model_dump(mode="json") @console_ns.doc("delete_workflow_variables") @console_ns.doc(description="Delete all draft workflow variables") @@ -345,24 +356,7 @@ class WorkflowVariableCollectionApi(Resource): ) draft_var_srv.delete_user_workflow_variables(app_model.id, user_id=current_user.id) db.session.commit() - return Response("", 204) - - -def validate_node_id(node_id: str) -> None: - if node_id in [ - CONVERSATION_VARIABLE_NODE_ID, - SYSTEM_VARIABLE_NODE_ID, - ]: - # NOTE(QuantumGhost): While we store the system and conversation variables as node variables - # with specific `node_id` in database, we still want to make the API separated. By disallowing - # accessing system and conversation variables in `WorkflowDraftNodeVariableListApi`, - # we mitigate the risk that user of the API depending on the implementation detail of the API. - # - # ref: [Hyrum's Law](https://www.hyrumslaw.com/) - - raise InvalidArgumentError( - f"invalid node_id, please use correspond api for conversation and system variables, node_id={node_id}", - ) + return "", 204 @console_ns.route("/apps//workflows/draft/nodes//variables") @@ -370,11 +364,15 @@ class NodeVariableCollectionApi(Resource): @console_ns.doc("get_node_variables") @console_ns.doc(description="Get variables for a specific node") @console_ns.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) - @console_ns.response(200, "Node variables retrieved successfully", workflow_draft_variable_list_model) + @console_ns.response( + 200, + "Node variables retrieved successfully", + console_ns.models[WorkflowDraftVariableListResponse.__name__], + ) @_api_prerequisite - @marshal_with(workflow_draft_variable_list_model) @rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_VIEW_LAYOUT) def get(self, current_user: Account, app_model: App, node_id: str): + # response-contract:ignore constructed Pydantic response validate_node_id(node_id) with sessionmaker(bind=db.engine, expire_on_commit=False).begin() as session: draft_var_srv = WorkflowDraftVariableService( @@ -382,7 +380,7 @@ class NodeVariableCollectionApi(Resource): ) node_vars = draft_var_srv.list_node_variables(app_model.id, node_id, user_id=current_user.id) - return node_vars + return WorkflowDraftVariableListResponse.from_workflow_draft_variable_list(node_vars).model_dump(mode="json") @console_ns.doc("delete_node_variables") @console_ns.doc(description="Delete all variables for a specific node") @@ -393,7 +391,7 @@ class NodeVariableCollectionApi(Resource): srv = WorkflowDraftVariableService(db.session()) srv.delete_node_variables(app_model.id, node_id, user_id=current_user.id) db.session.commit() - return Response("", 204) + return "", 204 @console_ns.route("/apps//workflows/draft/variables/") @@ -404,12 +402,14 @@ class VariableApi(Resource): @console_ns.doc("get_variable") @console_ns.doc(description="Get a specific workflow variable") @console_ns.doc(params={"app_id": "Application ID", "variable_id": "Variable ID"}) - @console_ns.response(200, "Variable retrieved successfully", workflow_draft_variable_model) + @console_ns.response( + 200, "Variable retrieved successfully", console_ns.models[WorkflowDraftVariableResponse.__name__] + ) @console_ns.response(404, "Variable not found") @_api_prerequisite - @marshal_with(workflow_draft_variable_model) @rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_VIEW_LAYOUT) def get(self, current_user: Account, app_model: App, variable_id: UUID): + # response-contract:ignore constructed Pydantic response draft_var_srv = WorkflowDraftVariableService( session=db.session(), ) @@ -420,16 +420,18 @@ class VariableApi(Resource): variable_id=variable_id_str, current_user_id=current_user.id, ) - return variable + return WorkflowDraftVariableResponse.from_workflow_draft_variable(variable).model_dump(mode="json") @console_ns.doc("update_variable") @console_ns.doc(description="Update a workflow variable") @console_ns.expect(console_ns.models[WorkflowDraftVariableUpdatePayload.__name__]) - @console_ns.response(200, "Variable updated successfully", workflow_draft_variable_model) + @console_ns.response( + 200, "Variable updated successfully", console_ns.models[WorkflowDraftVariableResponse.__name__] + ) @console_ns.response(404, "Variable not found") @_api_prerequisite - @marshal_with(workflow_draft_variable_model) def patch(self, current_user: Account, app_model: App, variable_id: UUID): + # response-contract:ignore constructed Pydantic response # Request payload for file types: # # Local File: @@ -467,15 +469,16 @@ class VariableApi(Resource): new_name = args_model.name raw_value = args_model.value if new_name is None and raw_value is None: - return variable + return WorkflowDraftVariableResponse.from_workflow_draft_variable(variable).model_dump(mode="json") new_value = None if raw_value is not None: + new_value_input: Any match variable.value_type: case SegmentType.FILE: if not isinstance(raw_value, dict): raise InvalidArgumentError(description=f"expected dict for file, got {type(raw_value)}") - raw_value = build_from_mapping( + new_value_input = build_from_mapping( mapping=raw_value, tenant_id=app_model.tenant_id, access_controller=_file_access_controller, @@ -483,19 +486,22 @@ class VariableApi(Resource): case SegmentType.ARRAY_FILE: if not isinstance(raw_value, list): raise InvalidArgumentError(description=f"expected list for files, got {type(raw_value)}") - if len(raw_value) > 0 and not isinstance(raw_value[0], dict): - raise InvalidArgumentError(description=f"expected dict for files[0], got {type(raw_value)}") - raw_value = build_from_mappings( + for index, item in enumerate(raw_value): + if not isinstance(item, dict): + raise InvalidArgumentError( + description=f"expected dict for files[{index}], got {type(item)}" + ) + new_value_input = build_from_mappings( mappings=raw_value, tenant_id=app_model.tenant_id, access_controller=_file_access_controller, ) case _: - pass - new_value = build_segment_with_type(variable.value_type, raw_value) + new_value_input = raw_value + new_value = build_segment_with_type(variable.value_type, new_value_input) draft_var_srv.update_variable(variable, name=new_name, value=new_value) db.session.commit() - return variable + return WorkflowDraftVariableResponse.from_workflow_draft_variable(variable).model_dump(mode="json") @console_ns.doc("delete_variable") @console_ns.doc(description="Delete a workflow variable") @@ -515,7 +521,7 @@ class VariableApi(Resource): ) draft_var_srv.delete_variable(variable) db.session.commit() - return Response("", 204) + return "", 204 @console_ns.route("/apps//workflows/draft/variables//reset") @@ -523,11 +529,12 @@ class VariableResetApi(Resource): @console_ns.doc("reset_variable") @console_ns.doc(description="Reset a workflow variable to its default value") @console_ns.doc(params={"app_id": "Application ID", "variable_id": "Variable ID"}) - @console_ns.response(200, "Variable reset successfully", workflow_draft_variable_model) + @console_ns.response(200, "Variable reset successfully", console_ns.models[WorkflowDraftVariableResponse.__name__]) @console_ns.response(204, "Variable reset (no content)") @console_ns.response(404, "Variable not found") @_api_prerequisite def put(self, current_user: Account, app_model: App, variable_id: UUID): + # response-contract:ignore constructed Pydantic response draft_var_srv = WorkflowDraftVariableService( session=db.session(), ) @@ -549,27 +556,8 @@ class VariableResetApi(Resource): resetted = draft_var_srv.reset_variable(draft_workflow, variable) db.session.commit() if resetted is None: - return Response("", 204) - else: - return marshal(resetted, workflow_draft_variable_model) - - -def _get_variable_list(app_model: App, node_id: str, current_user_id: str) -> WorkflowDraftVariableList: - with sessionmaker(bind=db.engine, expire_on_commit=False).begin() as session: - draft_var_srv = WorkflowDraftVariableService( - session=session, - ) - if node_id == CONVERSATION_VARIABLE_NODE_ID: - draft_vars = draft_var_srv.list_conversation_variables(app_model.id, user_id=current_user_id) - elif node_id == SYSTEM_VARIABLE_NODE_ID: - draft_vars = draft_var_srv.list_system_variables(app_model.id, user_id=current_user_id) - else: - draft_vars = draft_var_srv.list_node_variables( - app_id=app_model.id, - node_id=node_id, - user_id=current_user_id, - ) - return draft_vars + return "", 204 + return WorkflowDraftVariableResponse.from_workflow_draft_variable(resetted).model_dump(mode="json") @console_ns.route("/apps//workflows/draft/conversation-variables") @@ -577,12 +565,16 @@ class ConversationVariableCollectionApi(Resource): @console_ns.doc("get_conversation_variables") @console_ns.doc(description="Get conversation variables for workflow") @console_ns.doc(params={"app_id": "Application ID"}) - @console_ns.response(200, "Conversation variables retrieved successfully", workflow_draft_variable_list_model) + @console_ns.response( + 200, + "Conversation variables retrieved successfully", + console_ns.models[WorkflowDraftVariableListResponse.__name__], + ) @console_ns.response(404, "Draft workflow not found") @_api_prerequisite - @marshal_with(workflow_draft_variable_list_model) @rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_VIEW_LAYOUT) def get(self, current_user: Account, app_model: App): + # response-contract:ignore constructed Pydantic response # NOTE(QuantumGhost): Prefill conversation variables into the draft variables table # so their IDs can be returned to the caller. workflow_srv = WorkflowService() @@ -592,7 +584,9 @@ class ConversationVariableCollectionApi(Resource): draft_var_srv = WorkflowDraftVariableService(db.session()) draft_var_srv.prefill_conversation_variable_default_values(draft_workflow, user_id=current_user.id) db.session.commit() - return _get_variable_list(app_model, CONVERSATION_VARIABLE_NODE_ID, current_user.id) + return WorkflowDraftVariableListResponse.from_workflow_draft_variable_list( + _get_variable_list(app_model, CONVERSATION_VARIABLE_NODE_ID, current_user.id) + ).model_dump(mode="json") @console_ns.expect(console_ns.models[ConversationVariableUpdatePayload.__name__]) @console_ns.doc("update_conversation_variables") @@ -626,7 +620,7 @@ class ConversationVariableCollectionApi(Resource): conversation_variables=conversation_variables, ) - return {"result": "success"} + return SimpleResultResponse(result="success").model_dump(mode="json") @console_ns.route("/apps//workflows/draft/system-variables") @@ -634,12 +628,18 @@ class SystemVariableCollectionApi(Resource): @console_ns.doc("get_system_variables") @console_ns.doc(description="Get system variables for workflow") @console_ns.doc(params={"app_id": "Application ID"}) - @console_ns.response(200, "System variables retrieved successfully", workflow_draft_variable_list_model) + @console_ns.response( + 200, + "System variables retrieved successfully", + console_ns.models[WorkflowDraftVariableListResponse.__name__], + ) @_api_prerequisite - @marshal_with(workflow_draft_variable_list_model) @rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_VIEW_LAYOUT) def get(self, current_user: Account, app_model: App): - return _get_variable_list(app_model, SYSTEM_VARIABLE_NODE_ID, current_user.id) + # response-contract:ignore constructed Pydantic response + return WorkflowDraftVariableListResponse.from_workflow_draft_variable_list( + _get_variable_list(app_model, SYSTEM_VARIABLE_NODE_ID, current_user.id) + ).model_dump(mode="json") @console_ns.route("/apps//workflows/draft/environment-variables") @@ -650,7 +650,7 @@ class EnvironmentVariableCollectionApi(Resource): @console_ns.response( 200, "Environment variables retrieved successfully", - console_ns.models[EnvironmentVariableListResponse.__name__], + console_ns.models[WorkflowDraftEnvironmentVariableListResponse.__name__], ) @console_ns.response(404, "Draft workflow not found") @_api_prerequisite @@ -669,22 +669,22 @@ class EnvironmentVariableCollectionApi(Resource): env_vars_list = [] for v in env_vars: env_vars_list.append( - { - "id": v.id, - "type": "env", - "name": v.name, - "description": v.description, - "selector": v.selector, - "value_type": str(v.value_type.exposed_type()), - "value": v.value, + WorkflowDraftEnvironmentVariableResponse( + id=v.id, + type="env", + name=v.name, + description=v.description, + selector=list(v.selector), + value_type=str(v.value_type.exposed_type()), + value=v.value, # Do not track edited for env vars. - "edited": False, - "visible": True, - "editable": True, - } + edited=False, + visible=True, + editable=True, + ) ) - return {"items": env_vars_list} + return WorkflowDraftEnvironmentVariableListResponse(items=env_vars_list).model_dump(mode="json") @console_ns.expect(console_ns.models[EnvironmentVariableUpdatePayload.__name__]) @console_ns.doc("update_environment_variables") @@ -718,4 +718,4 @@ class EnvironmentVariableCollectionApi(Resource): environment_variables=environment_variables, ) - return {"result": "success"} + return SimpleResultResponse(result="success").model_dump(mode="json") diff --git a/api/controllers/console/app/workflow_run.py b/api/controllers/console/app/workflow_run.py index 374537229ca..520aa374f7b 100644 --- a/api/controllers/console/app/workflow_run.py +++ b/api/controllers/console/app/workflow_run.py @@ -37,7 +37,7 @@ from graphon.entities.pause_reason import HumanInputRequired from graphon.enums import WorkflowExecutionStatus from libs.archive_storage import ArchiveStorageNotConfiguredError, get_archive_storage from libs.custom_inputs import time_duration -from libs.helper import uuid_value +from libs.helper import dump_response, uuid_value from libs.login import login_required from models import Account, App, AppMode, WorkflowArchiveLog, WorkflowRunTriggeredFrom from models.workflow import WorkflowRun @@ -183,9 +183,7 @@ class AdvancedChatAppWorkflowRunListApi(Resource): app_model=app_model, args=args, triggered_from=triggered_from ) - return AdvancedChatWorkflowRunPaginationResponse.model_validate(result, from_attributes=True).model_dump( - mode="json" - ) + return dump_response(AdvancedChatWorkflowRunPaginationResponse, result) @console_ns.route("/apps//workflow-runs//export") @@ -232,14 +230,9 @@ class WorkflowRunExportApi(Resource): expires_in=EXPORT_SIGNED_URL_EXPIRE_SECONDS, ) expires_at = datetime.now(UTC) + timedelta(seconds=EXPORT_SIGNED_URL_EXPIRE_SECONDS) - response = WorkflowRunExportResponse.model_validate( - { - "status": "success", - "presigned_url": presigned_url, - "presigned_url_expires_at": expires_at.isoformat(), - } - ) - return response.model_dump(mode="json"), 200 + return WorkflowRunExportResponse( + status="success", presigned_url=presigned_url, presigned_url_expires_at=expires_at.isoformat() + ).model_dump(mode="json"), 200 @console_ns.route("/apps//advanced-chat/workflow-runs/count") @@ -322,7 +315,7 @@ class WorkflowRunListApi(Resource): app_model=app_model, args=args, triggered_from=triggered_from ) - return WorkflowRunPaginationResponse.model_validate(result, from_attributes=True).model_dump(mode="json") + return dump_response(WorkflowRunPaginationResponse, result) @console_ns.route("/apps//workflow-runs/count") @@ -393,7 +386,7 @@ class WorkflowRunDetailApi(Resource): if workflow_run is None: raise NotFoundError("Workflow run not found") - return WorkflowRunDetailResponse.model_validate(workflow_run, from_attributes=True).model_dump(mode="json") + return dump_response(WorkflowRunDetailResponse, workflow_run) @console_ns.route("/apps//workflow-runs//node-executions") @@ -471,8 +464,7 @@ class ConsoleWorkflowPauseDetailsApi(Resource): # Check if workflow is suspended is_paused = workflow_run.status == WorkflowExecutionStatus.PAUSED if not is_paused: - empty_response = WorkflowPauseDetailsResponse(paused_at=None, paused_nodes=[]) - return empty_response.model_dump(mode="json"), 200 + return WorkflowPauseDetailsResponse(paused_at=None, paused_nodes=[]).model_dump(mode="json"), 200 pause_entity = workflow_run_repo.get_workflow_pause(workflow_run_id) pause_reasons = pause_entity.get_pause_reasons() if pause_entity else [] @@ -500,8 +492,6 @@ class ConsoleWorkflowPauseDetailsApi(Resource): else: raise AssertionError("unimplemented.") - response = WorkflowPauseDetailsResponse( - paused_at=paused_at.isoformat() + "Z" if paused_at else None, - paused_nodes=paused_nodes, - ) - return response.model_dump(mode="json"), 200 + return WorkflowPauseDetailsResponse( + paused_at=paused_at.isoformat() + "Z" if paused_at else None, paused_nodes=paused_nodes + ).model_dump(mode="json"), 200 diff --git a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py index af417f24dfe..90015482403 100644 --- a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py +++ b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py @@ -1,27 +1,27 @@ import logging from collections.abc import Callable from functools import wraps -from typing import Any, Concatenate, NoReturn +from typing import Any, Concatenate from uuid import UUID from flask import Response, request -from flask_restx import Resource, marshal, marshal_with +from flask_restx import Resource from pydantic import BaseModel, Field from sqlalchemy.orm import sessionmaker from werkzeug.exceptions import Forbidden from controllers.common.errors import InvalidArgumentError, NotFoundError -from controllers.common.schema import query_params_from_model, register_schema_models +from controllers.common.schema import register_response_schema_models, register_schema_models from controllers.console import console_ns from controllers.console.app.error import ( DraftWorkflowNotExist, ) from controllers.console.app.workflow_draft_variable import ( - _WORKFLOW_DRAFT_VARIABLE_FIELDS, # type: ignore[private-usage] - EnvironmentVariableListResponse, - workflow_draft_variable_list_model, - workflow_draft_variable_list_without_value_model, - workflow_draft_variable_model, + WorkflowDraftVariableListResponse, + WorkflowDraftVariableListWithoutValueResponse, + WorkflowDraftVariableResponse, + WorkflowDraftVariableUpdatePayload, + validate_node_id, ) from controllers.console.datasets.wraps import get_rag_pipeline from controllers.console.wraps import account_initialization_required, setup_required, with_current_user @@ -30,6 +30,7 @@ from core.workflow.variable_prefixes import CONVERSATION_VARIABLE_NODE_ID, SYSTE from extensions.ext_database import db from factories.file_factory import build_from_mapping, build_from_mappings from factories.variable_factory import build_segment_with_type +from fields.base import ResponseModel from graphon.variables.types import SegmentType from libs.login import login_required from models import Account @@ -46,12 +47,36 @@ class PaginationQuery(BaseModel): limit: int = Field(default=20, ge=1, le=100) -class WorkflowDraftVariablePatchPayload(BaseModel): - name: str | None = None - value: Any | None = None +class WorkflowDraftVariablePatchPayload(WorkflowDraftVariableUpdatePayload): + value: Any = None + + +class RagPipelineEnvironmentVariableResponse(ResponseModel): + id: str + type: str + name: str + description: str + selector: list[str] + value_type: str + value: Any + edited: bool + visible: bool + editable: bool + + +class RagPipelineEnvironmentVariableListResponse(ResponseModel): + items: list[RagPipelineEnvironmentVariableResponse] register_schema_models(console_ns, PaginationQuery, WorkflowDraftVariablePatchPayload) +register_response_schema_models( + console_ns, + WorkflowDraftVariableResponse, + WorkflowDraftVariableListResponse, + WorkflowDraftVariableListWithoutValueResponse, + RagPipelineEnvironmentVariableResponse, + RagPipelineEnvironmentVariableListResponse, +) def _api_prerequisite[T, **P, R]( @@ -83,14 +108,12 @@ def _api_prerequisite[T, **P, R]( @console_ns.route("/rag/pipelines//workflows/draft/variables") class RagPipelineVariableCollectionApi(Resource): - @console_ns.doc(params=query_params_from_model(PaginationQuery)) @console_ns.response( 200, - "Workflow variables retrieved successfully", - workflow_draft_variable_list_without_value_model, + "Variables retrieved successfully", + console_ns.models[WorkflowDraftVariableListWithoutValueResponse.__name__], ) @_api_prerequisite - @marshal_with(workflow_draft_variable_list_without_value_model) def get(self, current_user: Account, pipeline: Pipeline): """ Get draft workflow @@ -115,9 +138,11 @@ class RagPipelineVariableCollectionApi(Resource): user_id=current_user.id, ) - return workflow_vars + return WorkflowDraftVariableListWithoutValueResponse.from_workflow_draft_variable_list( + workflow_vars + ).model_dump(mode="json") - @console_ns.response(204, "Workflow variables deleted successfully") + @console_ns.response(204, "Variables deleted successfully") @_api_prerequisite def delete(self, current_user: Account, pipeline: Pipeline): draft_var_srv = WorkflowDraftVariableService( @@ -125,32 +150,17 @@ class RagPipelineVariableCollectionApi(Resource): ) draft_var_srv.delete_user_workflow_variables(pipeline.id, user_id=current_user.id) db.session.commit() - return Response("", 204) - - -def validate_node_id(node_id: str) -> NoReturn | None: - if node_id in [ - CONVERSATION_VARIABLE_NODE_ID, - SYSTEM_VARIABLE_NODE_ID, - ]: - # NOTE(QuantumGhost): While we store the system and conversation variables as node variables - # with specific `node_id` in database, we still want to make the API separated. By disallowing - # accessing system and conversation variables in `WorkflowDraftNodeVariableListApi`, - # we mitigate the risk that user of the API depending on the implementation detail of the API. - # - # ref: [Hyrum's Law](https://www.hyrumslaw.com/) - - raise InvalidArgumentError( - f"invalid node_id, please use correspond api for conversation and system variables, node_id={node_id}", - ) - return None + return "", 204 @console_ns.route("/rag/pipelines//workflows/draft/nodes//variables") class RagPipelineNodeVariableCollectionApi(Resource): - @console_ns.response(200, "Node variables retrieved successfully", workflow_draft_variable_list_model) + @console_ns.response( + 200, + "Node variables retrieved successfully", + console_ns.models[WorkflowDraftVariableListResponse.__name__], + ) @_api_prerequisite - @marshal_with(workflow_draft_variable_list_model) def get(self, current_user: Account, pipeline: Pipeline, node_id: str): validate_node_id(node_id) with sessionmaker(bind=db.engine, expire_on_commit=False).begin() as session: @@ -159,7 +169,7 @@ class RagPipelineNodeVariableCollectionApi(Resource): ) node_vars = draft_var_srv.list_node_variables(pipeline.id, node_id, user_id=current_user.id) - return node_vars + return WorkflowDraftVariableListResponse.from_workflow_draft_variable_list(node_vars).model_dump(mode="json") @console_ns.response(204, "Node variables deleted successfully") @_api_prerequisite @@ -168,17 +178,17 @@ class RagPipelineNodeVariableCollectionApi(Resource): srv = WorkflowDraftVariableService(db.session()) srv.delete_node_variables(pipeline.id, node_id, user_id=current_user.id) db.session.commit() - return Response("", 204) + return "", 204 @console_ns.route("/rag/pipelines//workflows/draft/variables/") class RagPipelineVariableApi(Resource): - _PATCH_NAME_FIELD = "name" - _PATCH_VALUE_FIELD = "value" - - @console_ns.response(200, "Variable retrieved successfully", workflow_draft_variable_model) + @console_ns.response( + 200, + "Variable retrieved successfully", + console_ns.models[WorkflowDraftVariableResponse.__name__], + ) @_api_prerequisite - @marshal_with(workflow_draft_variable_model) def get(self, _current_user: Account, pipeline: Pipeline, variable_id: UUID): draft_var_srv = WorkflowDraftVariableService( session=db.session(), @@ -189,11 +199,14 @@ class RagPipelineVariableApi(Resource): raise NotFoundError(description=f"variable not found, id={variable_id_str}") if variable.app_id != pipeline.id: raise NotFoundError(description=f"variable not found, id={variable_id_str}") - return variable + return WorkflowDraftVariableResponse.from_workflow_draft_variable(variable).model_dump(mode="json") - @console_ns.response(200, "Variable updated successfully", workflow_draft_variable_model) + @console_ns.response( + 200, + "Variable updated successfully", + console_ns.models[WorkflowDraftVariableResponse.__name__], + ) @_api_prerequisite - @marshal_with(workflow_draft_variable_model) @console_ns.expect(console_ns.models[WorkflowDraftVariablePatchPayload.__name__]) def patch(self, _current_user: Account, pipeline: Pipeline, variable_id: UUID): # Request payload for file types: @@ -221,7 +234,6 @@ class RagPipelineVariableApi(Resource): session=db.session(), ) payload = WorkflowDraftVariablePatchPayload.model_validate(console_ns.payload or {}) - args = payload.model_dump(exclude_none=True) variable_id_str = str(variable_id) variable = draft_var_srv.get_variable(variable_id=variable_id_str) @@ -230,18 +242,22 @@ class RagPipelineVariableApi(Resource): if variable.app_id != pipeline.id: raise NotFoundError(description=f"variable not found, id={variable_id_str}") - new_name = args.get(self._PATCH_NAME_FIELD, None) - raw_value = args.get(self._PATCH_VALUE_FIELD, None) + new_name = payload.name + raw_value = payload.value if new_name is None and raw_value is None: - return variable + return WorkflowDraftVariableResponse.from_workflow_draft_variable(variable).model_dump(mode="json") + # TODO: duplication w/ controllers/console/app/workflow_draft_variable.py(L462) + # extract if proper (need to find a better location to prevent accident + # behavioral change) new_value = None if raw_value is not None: + new_value_input: Any match variable.value_type: case SegmentType.FILE: if not isinstance(raw_value, dict): raise InvalidArgumentError(description=f"expected dict for file, got {type(raw_value)}") - raw_value = build_from_mapping( + new_value_input = build_from_mapping( mapping=raw_value, tenant_id=pipeline.tenant_id, access_controller=_file_access_controller, @@ -249,19 +265,22 @@ class RagPipelineVariableApi(Resource): case SegmentType.ARRAY_FILE: if not isinstance(raw_value, list): raise InvalidArgumentError(description=f"expected list for files, got {type(raw_value)}") - if len(raw_value) > 0 and not isinstance(raw_value[0], dict): - raise InvalidArgumentError(description=f"expected dict for files[0], got {type(raw_value)}") - raw_value = build_from_mappings( + for index, item in enumerate(raw_value): + if not isinstance(item, dict): + raise InvalidArgumentError( + description=f"expected dict for files[{index}], got {type(item)}" + ) + new_value_input = build_from_mappings( mappings=raw_value, tenant_id=pipeline.tenant_id, access_controller=_file_access_controller, ) case _: - pass - new_value = build_segment_with_type(variable.value_type, raw_value) + new_value_input = raw_value + new_value = build_segment_with_type(variable.value_type, new_value_input) draft_var_srv.update_variable(variable, name=new_name, value=new_value) db.session.commit() - return variable + return WorkflowDraftVariableResponse.from_workflow_draft_variable(variable).model_dump(mode="json") @console_ns.response(204, "Variable deleted successfully") @_api_prerequisite @@ -277,13 +296,17 @@ class RagPipelineVariableApi(Resource): raise NotFoundError(description=f"variable not found, id={variable_id_str}") draft_var_srv.delete_variable(variable) db.session.commit() - return Response("", 204) + return "", 204 @console_ns.route("/rag/pipelines//workflows/draft/variables//reset") class RagPipelineVariableResetApi(Resource): - @console_ns.response(200, "Variable reset successfully", workflow_draft_variable_model) - @console_ns.response(204, "Variable reset (no content)") + @console_ns.response( + 200, + "Variable reset successfully", + console_ns.models[WorkflowDraftVariableResponse.__name__], + ) + @console_ns.response(204, "Variable reset to empty state") @_api_prerequisite def put(self, _current_user: Account, pipeline: Pipeline, variable_id: UUID): draft_var_srv = WorkflowDraftVariableService( @@ -306,9 +329,8 @@ class RagPipelineVariableResetApi(Resource): resetted = draft_var_srv.reset_variable(draft_workflow, variable) db.session.commit() if resetted is None: - return Response("", 204) - else: - return marshal(resetted, _WORKFLOW_DRAFT_VARIABLE_FIELDS) + return "", 204 + return WorkflowDraftVariableResponse.from_workflow_draft_variable(resetted).model_dump(mode="json") def _get_variable_list(pipeline: Pipeline, node_id: str, current_user_id: str) -> WorkflowDraftVariableList: @@ -327,11 +349,16 @@ def _get_variable_list(pipeline: Pipeline, node_id: str, current_user_id: str) - @console_ns.route("/rag/pipelines//workflows/draft/system-variables") class RagPipelineSystemVariableCollectionApi(Resource): - @console_ns.response(200, "System variables retrieved successfully", workflow_draft_variable_list_model) + @console_ns.response( + 200, + "System variables retrieved successfully", + console_ns.models[WorkflowDraftVariableListResponse.__name__], + ) @_api_prerequisite - @marshal_with(workflow_draft_variable_list_model) def get(self, current_user: Account, pipeline: Pipeline): - return _get_variable_list(pipeline, SYSTEM_VARIABLE_NODE_ID, current_user.id) + return WorkflowDraftVariableListResponse.from_workflow_draft_variable_list( + _get_variable_list(pipeline, SYSTEM_VARIABLE_NODE_ID, current_user.id) + ).model_dump(mode="json") @console_ns.route("/rag/pipelines//workflows/draft/environment-variables") @@ -339,7 +366,7 @@ class RagPipelineEnvironmentVariableCollectionApi(Resource): @console_ns.response( 200, "Environment variables retrieved successfully", - console_ns.models[EnvironmentVariableListResponse.__name__], + console_ns.models[RagPipelineEnvironmentVariableListResponse.__name__], ) @_api_prerequisite def get(self, _current_user: Account, pipeline: Pipeline): @@ -353,22 +380,22 @@ class RagPipelineEnvironmentVariableCollectionApi(Resource): raise DraftWorkflowNotExist() env_vars = workflow.environment_variables - env_vars_list = [] + env_vars_list: list[RagPipelineEnvironmentVariableResponse] = [] for v in env_vars: env_vars_list.append( - { - "id": v.id, - "type": "env", - "name": v.name, - "description": v.description, - "selector": v.selector, - "value_type": v.value_type.value, - "value": v.value, + RagPipelineEnvironmentVariableResponse( + id=v.id, + type="env", + name=v.name, + description=v.description, + selector=list(v.selector), + value_type=v.value_type.value, + value=v.value, # Do not track edited for env vars. - "edited": False, - "visible": True, - "editable": True, - } + edited=False, + visible=True, + editable=True, + ) ) - return {"items": env_vars_list} + return RagPipelineEnvironmentVariableListResponse(items=env_vars_list).model_dump(mode="json") diff --git a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py index fdc55ea9737..adf0a0006be 100644 --- a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py +++ b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py @@ -1,5 +1,6 @@ import json import logging +from collections.abc import Mapping from typing import Any, Literal, cast from uuid import UUID @@ -21,8 +22,6 @@ from controllers.console.app.error import ( ) from controllers.console.app.workflow import ( RESTORE_SOURCE_WORKFLOW_MUST_BE_PUBLISHED_MESSAGE, - DefaultBlockConfigResponse, - DefaultBlockConfigsResponse, WorkflowPaginationResponse, WorkflowResponse, ) @@ -41,6 +40,7 @@ from controllers.web.error import InvokeRateLimitError as InvokeRateLimitHttpErr from core.app.apps.base_app_queue_manager import AppQueueManager from core.app.apps.pipeline.pipeline_generator import PipelineGenerator from core.app.entities.app_invoke_entities import InvokeFrom +from core.plugin.entities.plugin_daemon import PluginDatasourceProviderEntity from extensions.ext_database import db from factories import variable_factory from fields.base import ResponseModel @@ -50,9 +50,8 @@ from fields.workflow_run_fields import ( WorkflowRunNodeExecutionResponse, WorkflowRunPaginationResponse, ) -from graphon.model_runtime.utils.encoders import jsonable_encoder from libs import helper -from libs.helper import TimestampField, UUIDStrOrEmpty, dump_response +from libs.helper import UUIDStrOrEmpty, dump_response, to_timestamp from libs.login import login_required from models import Account from models.dataset import Pipeline @@ -72,14 +71,14 @@ logger = logging.getLogger(__name__) class DraftWorkflowSyncPayload(BaseModel): graph: dict[str, Any] hash: str | None = None - environment_variables: list[dict[str, Any]] | None = Field(default=None) - conversation_variables: list[dict[str, Any]] | None = Field(default=None) - rag_pipeline_variables: list[dict[str, Any]] | None = Field(default=None) - features: dict[str, Any] | None = Field(default=None) + environment_variables: list[dict[str, Any]] | None = None + conversation_variables: list[dict[str, Any]] | None = None + rag_pipeline_variables: list[dict[str, Any]] | None = None + features: dict[str, Any] | None = None class NodeRunPayload(BaseModel): - inputs: dict[str, Any] | None = Field(default=None) + inputs: dict[str, Any] | None = None class NodeRunRequiredPayload(BaseModel): @@ -136,14 +135,30 @@ class RagPipelineWorkflowPublishResponse(ResponseModel): created_at: int -class RagPipelineOpaqueResponse(RootModel[Any]): - root: Any - - -class RagPipelineStepParametersResponse(ResponseModel): +class RagPipelineVariablesResponse(ResponseModel): + # TODO: Replace Any with a response model that mirrors graphon.variables.variables.RAGPipelineVariable. variables: Any +class DatasourcePluginListResponse(RootModel[list[PluginDatasourceProviderEntity]]): + pass + + +class RagPipelineRecommendedPluginResponse(ResponseModel): + installed_recommended_plugins: list[Mapping[str, object]] = Field( + description="Installed tool provider payloads. Shape follows the tool provider serializer." + ) + uninstalled_recommended_plugins: list[Mapping[str, object]] = Field( + description="Marketplace plugin manifest payloads returned by the marketplace service." + ) + + +class RagPipelineTransformResponse(ResponseModel): + pipeline_id: str + dataset_id: str + status: str + + register_schema_models( console_ns, DraftWorkflowSyncPayload, @@ -162,10 +177,10 @@ register_schema_models( ) register_response_schema_models( console_ns, - DefaultBlockConfigResponse, - DefaultBlockConfigsResponse, - RagPipelineOpaqueResponse, - RagPipelineStepParametersResponse, + DatasourcePluginListResponse, + RagPipelineRecommendedPluginResponse, + RagPipelineTransformResponse, + RagPipelineVariablesResponse, RagPipelineWorkflowPublishResponse, RagPipelineWorkflowSyncResponse, SimpleResultResponse, @@ -254,17 +269,16 @@ class DraftRagPipelineApi(Resource): except WorkflowHashNotEqualError: raise DraftWorkflowNotSync() - return { - "result": "success", - "hash": workflow.unique_hash, - "updated_at": TimestampField().format(workflow.updated_at or workflow.created_at), - } + return RagPipelineWorkflowSyncResponse( + result="success", + hash=workflow.unique_hash, + updated_at=to_timestamp(workflow.updated_at or workflow.created_at), + ).model_dump(mode="json") @console_ns.route("/rag/pipelines//workflows/draft/iteration/nodes//run") class RagPipelineDraftRunIterationNodeApi(Resource): @console_ns.expect(console_ns.models[NodeRunPayload.__name__]) - @console_ns.response(200, "Success", console_ns.models[RagPipelineOpaqueResponse.__name__]) @setup_required @login_required @account_initialization_required @@ -284,6 +298,7 @@ class RagPipelineDraftRunIterationNodeApi(Resource): pipeline=pipeline, user=current_user, node_id=node_id, args=args, streaming=True ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except services.errors.conversation.ConversationNotExistsError: raise NotFound("Conversation Not Exists.") @@ -299,7 +314,6 @@ class RagPipelineDraftRunIterationNodeApi(Resource): @console_ns.route("/rag/pipelines//workflows/draft/loop/nodes//run") class RagPipelineDraftRunLoopNodeApi(Resource): @console_ns.expect(console_ns.models[NodeRunPayload.__name__]) - @console_ns.response(200, "Success", console_ns.models[RagPipelineOpaqueResponse.__name__]) @setup_required @login_required @account_initialization_required @@ -319,6 +333,7 @@ class RagPipelineDraftRunLoopNodeApi(Resource): pipeline=pipeline, user=current_user, node_id=node_id, args=args, streaming=True ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except services.errors.conversation.ConversationNotExistsError: raise NotFound("Conversation Not Exists.") @@ -334,7 +349,6 @@ class RagPipelineDraftRunLoopNodeApi(Resource): @console_ns.route("/rag/pipelines//workflows/draft/run") class DraftRagPipelineRunApi(Resource): @console_ns.expect(console_ns.models[DraftWorkflowRunPayload.__name__]) - @console_ns.response(200, "Success", console_ns.models[RagPipelineOpaqueResponse.__name__]) @setup_required @login_required @account_initialization_required @@ -358,6 +372,7 @@ class DraftRagPipelineRunApi(Resource): streaming=True, ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except InvokeRateLimitError as ex: raise InvokeRateLimitHttpError(ex.description) @@ -366,7 +381,6 @@ class DraftRagPipelineRunApi(Resource): @console_ns.route("/rag/pipelines//workflows/published/run") class PublishedRagPipelineRunApi(Resource): @console_ns.expect(console_ns.models[PublishedWorkflowRunPayload.__name__]) - @console_ns.response(200, "Success", console_ns.models[RagPipelineOpaqueResponse.__name__]) @setup_required @login_required @account_initialization_required @@ -391,6 +405,7 @@ class PublishedRagPipelineRunApi(Resource): streaming=streaming, ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except InvokeRateLimitError as ex: raise InvokeRateLimitHttpError(ex.description) @@ -399,7 +414,6 @@ class PublishedRagPipelineRunApi(Resource): @console_ns.route("/rag/pipelines//workflows/published/datasource/nodes//run") class RagPipelinePublishedDatasourceNodeRunApi(Resource): @console_ns.expect(console_ns.models[DatasourceNodeRunPayload.__name__]) - @console_ns.response(200, "Success", console_ns.models[RagPipelineOpaqueResponse.__name__]) @setup_required @login_required @account_initialization_required @@ -414,6 +428,7 @@ class RagPipelinePublishedDatasourceNodeRunApi(Resource): payload = DatasourceNodeRunPayload.model_validate(console_ns.payload or {}) rag_pipeline_service = RagPipelineService() + # response-contract:ignore compact_generate_response return helper.compact_generate_response( PipelineGenerator.convert_to_event_stream( rag_pipeline_service.run_datasource_workflow_node( @@ -432,7 +447,6 @@ class RagPipelinePublishedDatasourceNodeRunApi(Resource): @console_ns.route("/rag/pipelines//workflows/draft/datasource/nodes//run") class RagPipelineDraftDatasourceNodeRunApi(Resource): @console_ns.expect(console_ns.models[DatasourceNodeRunPayload.__name__]) - @console_ns.response(200, "Success", console_ns.models[RagPipelineOpaqueResponse.__name__]) @setup_required @login_required @edit_permission_required @@ -447,6 +461,7 @@ class RagPipelineDraftDatasourceNodeRunApi(Resource): payload = DatasourceNodeRunPayload.model_validate(console_ns.payload or {}) rag_pipeline_service = RagPipelineService() + # response-contract:ignore compact_generate_response return helper.compact_generate_response( PipelineGenerator.convert_to_event_stream( rag_pipeline_service.run_datasource_workflow_node( @@ -492,9 +507,7 @@ class RagPipelineDraftNodeRunApi(Resource): if workflow_node_execution is None: raise ValueError("Workflow node execution not found") - return WorkflowRunNodeExecutionResponse.model_validate( - workflow_node_execution, from_attributes=True - ).model_dump(mode="json") + return dump_response(WorkflowRunNodeExecutionResponse, workflow_node_execution) @console_ns.route("/rag/pipelines//workflow-runs/tasks//stop") @@ -513,7 +526,7 @@ class RagPipelineTaskStopApi(Resource): """ AppQueueManager.set_stop_flag(task_id, InvokeFrom.DEBUGGER, current_user.id) - return {"result": "success"} + return SimpleResultResponse(result="success").model_dump(mode="json") @console_ns.route("/rag/pipelines//workflows/publish") @@ -567,20 +580,18 @@ class PublishedRagPipelineApi(Resource): pipeline.is_published = True pipeline.workflow_id = workflow.id db.session.commit() - workflow_created_at = TimestampField().format(workflow.created_at) + workflow_created_at = to_timestamp(workflow.created_at) - return { - "result": "success", - "created_at": workflow_created_at, - } + return RagPipelineWorkflowPublishResponse(result="success", created_at=workflow_created_at).model_dump( + mode="json" + ) @console_ns.route("/rag/pipelines//workflows/default-workflow-block-configs") class DefaultRagPipelineBlockConfigsApi(Resource): @console_ns.response( 200, - "Default block configs retrieved successfully", - console_ns.models[DefaultBlockConfigsResponse.__name__], + "Default workflow block configurations retrieved successfully", ) @setup_required @login_required @@ -602,8 +613,7 @@ class DefaultRagPipelineBlockConfigApi(Resource): @console_ns.doc(params=query_params_from_model(DefaultBlockConfigQuery)) @console_ns.response( 200, - "Default block config retrieved successfully", - console_ns.models[DefaultBlockConfigResponse.__name__], + "Default workflow block configuration retrieved successfully", ) @setup_required @login_required @@ -631,13 +641,13 @@ class DefaultRagPipelineBlockConfigApi(Resource): @console_ns.route("/rag/pipelines//workflows") class PublishedAllRagPipelineApi(Resource): - @console_ns.doc(params=query_params_from_model(WorkflowListQuery)) @console_ns.response( 200, "Published workflows retrieved successfully", console_ns.models[WorkflowPaginationResponse.__name__], ) @console_ns.response(403, "Permission denied") + @console_ns.doc(params=query_params_from_model(WorkflowListQuery)) @setup_required @login_required @account_initialization_required @@ -671,14 +681,15 @@ class PublishedAllRagPipelineApi(Resource): named_only=named_only, ) - return WorkflowPaginationResponse.model_validate( + return dump_response( + WorkflowPaginationResponse, { "items": workflows, "page": page, "limit": limit, "has_more": has_more, - } - ).model_dump(mode="json") + }, + ) @console_ns.route("/rag/pipelines//workflows//restore") @@ -706,11 +717,11 @@ class RagPipelineDraftWorkflowRestoreApi(Resource): except WorkflowNotFoundError as exc: raise NotFound(str(exc)) from exc - return { - "result": "success", - "hash": workflow.unique_hash, - "updated_at": TimestampField().format(workflow.updated_at or workflow.created_at), - } + return RagPipelineWorkflowSyncResponse( + result="success", + hash=workflow.unique_hash, + updated_at=to_timestamp(workflow.updated_at or workflow.created_at), + ).model_dump(mode="json") @console_ns.route("/rag/pipelines//workflows/") @@ -784,13 +795,17 @@ class RagPipelineByIdApi(Resource): except ValueError as e: raise NotFound(str(e)) - return None, 204 + return "", 204 @console_ns.route("/rag/pipelines//workflows/published/processing/parameters") class PublishedRagPipelineSecondStepApi(Resource): @console_ns.doc(params=query_params_from_model(NodeIdQuery)) - @console_ns.response(200, "Success", console_ns.models[RagPipelineStepParametersResponse.__name__]) + @console_ns.response( + 200, + "Second step parameters retrieved successfully", + console_ns.models[RagPipelineVariablesResponse.__name__], + ) @setup_required @login_required @account_initialization_required @@ -805,15 +820,17 @@ class PublishedRagPipelineSecondStepApi(Resource): node_id = query.node_id rag_pipeline_service = RagPipelineService() variables = rag_pipeline_service.get_second_step_parameters(pipeline=pipeline, node_id=node_id, is_draft=False) - return { - "variables": variables, - } + return dump_response(RagPipelineVariablesResponse, {"variables": variables}) @console_ns.route("/rag/pipelines//workflows/published/pre-processing/parameters") class PublishedRagPipelineFirstStepApi(Resource): @console_ns.doc(params=query_params_from_model(NodeIdQuery)) - @console_ns.response(200, "Success", console_ns.models[RagPipelineStepParametersResponse.__name__]) + @console_ns.response( + 200, + "First step parameters retrieved successfully", + console_ns.models[RagPipelineVariablesResponse.__name__], + ) @setup_required @login_required @account_initialization_required @@ -828,15 +845,17 @@ class PublishedRagPipelineFirstStepApi(Resource): node_id = query.node_id rag_pipeline_service = RagPipelineService() variables = rag_pipeline_service.get_first_step_parameters(pipeline=pipeline, node_id=node_id, is_draft=False) - return { - "variables": variables, - } + return dump_response(RagPipelineVariablesResponse, {"variables": variables}) @console_ns.route("/rag/pipelines//workflows/draft/pre-processing/parameters") class DraftRagPipelineFirstStepApi(Resource): @console_ns.doc(params=query_params_from_model(NodeIdQuery)) - @console_ns.response(200, "Success", console_ns.models[RagPipelineStepParametersResponse.__name__]) + @console_ns.response( + 200, + "First step parameters retrieved successfully", + console_ns.models[RagPipelineVariablesResponse.__name__], + ) @setup_required @login_required @account_initialization_required @@ -851,15 +870,17 @@ class DraftRagPipelineFirstStepApi(Resource): node_id = query.node_id rag_pipeline_service = RagPipelineService() variables = rag_pipeline_service.get_first_step_parameters(pipeline=pipeline, node_id=node_id, is_draft=True) - return { - "variables": variables, - } + return dump_response(RagPipelineVariablesResponse, {"variables": variables}) @console_ns.route("/rag/pipelines//workflows/draft/processing/parameters") class DraftRagPipelineSecondStepApi(Resource): @console_ns.doc(params=query_params_from_model(NodeIdQuery)) - @console_ns.response(200, "Success", console_ns.models[RagPipelineStepParametersResponse.__name__]) + @console_ns.response( + 200, + "Second step parameters retrieved successfully", + console_ns.models[RagPipelineVariablesResponse.__name__], + ) @setup_required @login_required @account_initialization_required @@ -875,9 +896,7 @@ class DraftRagPipelineSecondStepApi(Resource): rag_pipeline_service = RagPipelineService() variables = rag_pipeline_service.get_second_step_parameters(pipeline=pipeline, node_id=node_id, is_draft=True) - return { - "variables": variables, - } + return dump_response(RagPipelineVariablesResponse, {"variables": variables}) @console_ns.route("/rag/pipelines//workflow-runs") @@ -910,7 +929,7 @@ class RagPipelineWorkflowRunListApi(Resource): rag_pipeline_service = RagPipelineService() result = rag_pipeline_service.get_rag_pipeline_paginate_workflow_runs(pipeline=pipeline, args=args) - return WorkflowRunPaginationResponse.model_validate(result, from_attributes=True).model_dump(mode="json") + return dump_response(WorkflowRunPaginationResponse, result) @console_ns.route("/rag/pipelines//workflow-runs/") @@ -935,7 +954,7 @@ class RagPipelineWorkflowRunDetailApi(Resource): if workflow_run is None: raise NotFound("Workflow run not found") - return WorkflowRunDetailResponse.model_validate(workflow_run, from_attributes=True).model_dump(mode="json") + return dump_response(WorkflowRunDetailResponse, workflow_run) @console_ns.route("/rag/pipelines//workflow-runs//node-executions") @@ -964,20 +983,25 @@ class RagPipelineWorkflowRunNodeExecutionListApi(Resource): user=user, ) - return WorkflowRunNodeExecutionListResponse.model_validate( - {"data": node_executions}, from_attributes=True - ).model_dump(mode="json") + return dump_response(WorkflowRunNodeExecutionListResponse, {"data": node_executions}) @console_ns.route("/rag/pipelines/datasource-plugins") class DatasourceListApi(Resource): - @console_ns.response(200, "Success", console_ns.models[RagPipelineOpaqueResponse.__name__]) + @console_ns.response( + 200, + "Datasource plugins retrieved successfully", + console_ns.models[DatasourcePluginListResponse.__name__], + ) @setup_required @login_required @account_initialization_required @with_current_tenant_id def get(self, current_tenant_id: str): - return jsonable_encoder(RagPipelineManageService.list_rag_pipeline_datasources(current_tenant_id)) + return dump_response( + DatasourcePluginListResponse, + RagPipelineManageService.list_rag_pipeline_datasources(current_tenant_id), + ) @console_ns.route("/rag/pipelines//workflows/draft/nodes//last-run") @@ -1003,12 +1027,16 @@ class RagPipelineWorkflowLastRunApi(Resource): ) if node_exec is None: raise NotFound("last run not found") - return WorkflowRunNodeExecutionResponse.model_validate(node_exec, from_attributes=True).model_dump(mode="json") + return dump_response(WorkflowRunNodeExecutionResponse, node_exec) @console_ns.route("/rag/pipelines/transform/datasets/") class RagPipelineTransformApi(Resource): - @console_ns.response(200, "Success", console_ns.models[RagPipelineOpaqueResponse.__name__]) + @console_ns.response( + 200, + "Dataset transformed successfully", + console_ns.models[RagPipelineTransformResponse.__name__], + ) @setup_required @login_required @account_initialization_required @@ -1020,7 +1048,7 @@ class RagPipelineTransformApi(Resource): dataset_id_str = str(dataset_id) rag_pipeline_transform_service = RagPipelineTransformService() result = rag_pipeline_transform_service.transform_dataset(dataset_id_str, db.session) - return result + return dump_response(RagPipelineTransformResponse, result) @console_ns.route("/rag/pipelines//workflows/draft/datasource/variables-inspect") @@ -1050,15 +1078,17 @@ class RagPipelineDatasourceVariableApi(Resource): args=args, current_user=current_user, ) - return WorkflowRunNodeExecutionResponse.model_validate( - workflow_node_execution, from_attributes=True - ).model_dump(mode="json") + return dump_response(WorkflowRunNodeExecutionResponse, workflow_node_execution) @console_ns.route("/rag/pipelines/recommended-plugins") class RagPipelineRecommendedPluginApi(Resource): @console_ns.doc(params=query_params_from_model(RagPipelineRecommendedPluginQuery)) - @console_ns.response(200, "Success", console_ns.models[RagPipelineOpaqueResponse.__name__]) + @console_ns.response( + 200, + "Recommended plugins retrieved successfully", + console_ns.models[RagPipelineRecommendedPluginResponse.__name__], + ) @setup_required @login_required @account_initialization_required @@ -1069,4 +1099,4 @@ class RagPipelineRecommendedPluginApi(Resource): rag_pipeline_service = RagPipelineService() recommended_plugins = rag_pipeline_service.get_recommended_plugins(query.type, current_user, current_tenant_id) - return recommended_plugins + return dump_response(RagPipelineRecommendedPluginResponse, recommended_plugins) diff --git a/api/controllers/console/snippets/snippet_workflow.py b/api/controllers/console/snippets/snippet_workflow.py index 0b8dc264a68..17416ee8610 100644 --- a/api/controllers/console/snippets/snippet_workflow.py +++ b/api/controllers/console/snippets/snippet_workflow.py @@ -8,20 +8,18 @@ from pydantic import BaseModel, Field from sqlalchemy.orm import Session, sessionmaker from werkzeug.exceptions import BadRequest, InternalServerError, NotFound -from controllers.common.fields import GeneratedAppResponse, SimpleResultResponse +from controllers.common.fields import EventStreamResponse, SimpleResultResponse from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models from controllers.console import console_ns from controllers.console.app.error import DraftWorkflowNotExist, DraftWorkflowNotSync from controllers.console.app.workflow import ( RESTORE_SOURCE_WORKFLOW_MUST_BE_PUBLISHED_MESSAGE, - DefaultBlockConfigsResponse, + PublishWorkflowResponse, + SyncDraftWorkflowResponse, WorkflowPaginationResponse, - WorkflowPublishResponse, WorkflowResponse, - WorkflowRestoreResponse, ) from controllers.console.snippets.payloads import ( - PublishWorkflowPayload, SnippetDraftNodeRunPayload, SnippetDraftRunPayload, SnippetDraftSyncPayload, @@ -43,6 +41,7 @@ from core.app.apps.base_app_queue_manager import AppQueueManager from core.app.entities.app_invoke_entities import InvokeFrom from extensions.ext_database import db from extensions.ext_redis import redis_client +from fields.base import ResponseModel from fields.workflow_run_fields import ( WorkflowRunDetailResponse, WorkflowRunNodeExecutionListResponse, @@ -51,7 +50,7 @@ from fields.workflow_run_fields import ( ) from graphon.graph_engine.manager import GraphEngineManager from libs import helper -from libs.helper import TimestampField +from libs.helper import dump_response, to_timestamp from libs.login import current_account_with_tenant, login_required from models import Account from models.snippet import CustomizedSnippet @@ -76,7 +75,7 @@ class SnippetWorkflowResponse(WorkflowResponse): input_fields: list[dict] = Field(default_factory=list) -class SnippetDraftConfigResponse(BaseModel): +class SnippetDraftConfigResponse(ResponseModel): parallel_depth_limit: int @@ -96,19 +95,17 @@ register_schema_models( SnippetLoopNodeRunPayload, SnippetWorkflowListQuery, WorkflowRunQuery, - PublishWorkflowPayload, ) register_response_schema_models( console_ns, - DefaultBlockConfigsResponse, - GeneratedAppResponse, + EventStreamResponse, SimpleResultResponse, SnippetDraftConfigResponse, SnippetWorkflowResponse, SnippetWorkflowPaginationResponse, - WorkflowPublishResponse, + PublishWorkflowResponse, WorkflowPaginationResponse, - WorkflowRestoreResponse, + SyncDraftWorkflowResponse, WorkflowRunPaginationResponse, WorkflowRunDetailResponse, WorkflowRunNodeExecutionListResponse, @@ -175,7 +172,7 @@ class SnippetDraftWorkflowApi(Resource): raise DraftWorkflowNotExist() workflow.conversation_variables = [] - response = SnippetWorkflowResponse.model_validate(workflow, from_attributes=True).model_dump(mode="json") + response = dump_response(SnippetWorkflowResponse, workflow) response["input_fields"] = snippet.input_fields_list return response @@ -184,7 +181,7 @@ class SnippetDraftWorkflowApi(Resource): @console_ns.response( 200, "Draft workflow synced successfully", - console_ns.models[WorkflowRestoreResponse.__name__], + console_ns.models[SyncDraftWorkflowResponse.__name__], ) @console_ns.response(400, "Hash mismatch") @setup_required @@ -214,11 +211,11 @@ class SnippetDraftWorkflowApi(Resource): except ValueError as e: return {"message": str(e)}, 400 - return { - "result": "success", - "hash": workflow.unique_hash, - "updated_at": TimestampField().format(workflow.updated_at or workflow.created_at), - } + return SyncDraftWorkflowResponse( + result="success", + hash=workflow.unique_hash, + updated_at=to_timestamp(workflow.updated_at or workflow.created_at), + ).model_dump(mode="json") @console_ns.route("/snippets//workflows/draft/config") @@ -237,9 +234,7 @@ class SnippetDraftConfigApi(Resource): @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.SNIPPETS_MANAGE, resource_required=False) def get(self, snippet: CustomizedSnippet): """Get snippet draft workflow configuration limits.""" - return { - "parallel_depth_limit": 3, - } + return SnippetDraftConfigResponse(parallel_depth_limit=3).model_dump(mode="json") @console_ns.route("/snippets//workflows/publish") @@ -268,13 +263,12 @@ class SnippetPublishedWorkflowApi(Resource): if not workflow: return None - response = SnippetWorkflowResponse.model_validate(workflow, from_attributes=True).model_dump(mode="json") + response = dump_response(SnippetWorkflowResponse, workflow) response["input_fields"] = snippet.input_fields_list return response @console_ns.doc("publish_snippet_workflow") - @console_ns.expect(console_ns.models.get(PublishWorkflowPayload.__name__)) - @console_ns.response(200, "Workflow published successfully", console_ns.models[WorkflowPublishResponse.__name__]) + @console_ns.response(200, "Workflow published successfully", console_ns.models[PublishWorkflowResponse.__name__]) @console_ns.response(400, "No draft workflow found") @setup_required @login_required @@ -297,25 +291,18 @@ class SnippetPublishedWorkflowApi(Resource): snippet=snippet, account=current_user, ) - workflow_created_at = TimestampField().format(workflow.created_at) + workflow_created_at = to_timestamp(workflow.created_at) session.commit() except ValueError as e: return {"message": str(e)}, 400 - return { - "result": "success", - "created_at": workflow_created_at, - } + return PublishWorkflowResponse(result="success", created_at=workflow_created_at).model_dump(mode="json") @console_ns.route("/snippets//workflows/default-workflow-block-configs") class SnippetDefaultBlockConfigsApi(Resource): @console_ns.doc("get_snippet_default_block_configs") - @console_ns.response( - 200, - "Default block configs retrieved successfully", - console_ns.models[DefaultBlockConfigsResponse.__name__], - ) + @console_ns.response(200, "Default block configs retrieved successfully") @setup_required @login_required @account_initialization_required @@ -377,7 +364,7 @@ class SnippetDraftWorkflowRestoreApi(Resource): @console_ns.doc("restore_snippet_workflow_to_draft") @console_ns.doc(description="Restore a published snippet workflow version into the draft workflow") @console_ns.doc(params={"snippet_id": "Snippet ID", "workflow_id": "Published workflow ID"}) - @console_ns.response(200, "Workflow restored successfully", console_ns.models[WorkflowRestoreResponse.__name__]) + @console_ns.response(200, "Workflow restored successfully", console_ns.models[SyncDraftWorkflowResponse.__name__]) @console_ns.response(400, "Source workflow must be published") @console_ns.response(404, "Workflow not found") @setup_required @@ -406,11 +393,11 @@ class SnippetDraftWorkflowRestoreApi(Resource): except ValueError as exc: raise BadRequest(str(exc)) from exc - return { - "result": "success", - "hash": workflow.unique_hash, - "updated_at": TimestampField().format(workflow.updated_at or workflow.created_at), - } + return SyncDraftWorkflowResponse( + result="success", + hash=workflow.unique_hash, + updated_at=to_timestamp(workflow.updated_at or workflow.created_at), + ).model_dump(mode="json") @console_ns.route("/snippets//workflow-runs") @@ -442,7 +429,7 @@ class SnippetWorkflowRunsApi(Resource): snippet_service = _snippet_service() result = snippet_service.get_snippet_workflow_runs(snippet=snippet, args=args) - return WorkflowRunPaginationResponse.model_validate(result, from_attributes=True).model_dump(mode="json") + return dump_response(WorkflowRunPaginationResponse, result) @console_ns.route("/snippets//workflow-runs/") @@ -468,7 +455,7 @@ class SnippetWorkflowRunDetailApi(Resource): if not workflow_run: raise NotFound("Workflow run not found") - return WorkflowRunDetailResponse.model_validate(workflow_run, from_attributes=True).model_dump(mode="json") + return dump_response(WorkflowRunDetailResponse, workflow_run) @console_ns.route("/snippets//workflow-runs//node-executions") @@ -493,9 +480,7 @@ class SnippetWorkflowRunNodeExecutionsApi(Resource): run_id=run_id, ) - return WorkflowRunNodeExecutionListResponse.model_validate( - {"data": node_executions}, from_attributes=True - ).model_dump(mode="json") + return dump_response(WorkflowRunNodeExecutionListResponse, {"data": node_executions}) @console_ns.route("/snippets//workflows/draft/nodes//run") @@ -546,9 +531,7 @@ class SnippetDraftNodeRunApi(Resource): session_maker=_snippet_session_maker(), ) - return WorkflowRunNodeExecutionResponse.model_validate( - workflow_node_execution, from_attributes=True - ).model_dump(mode="json") + return dump_response(WorkflowRunNodeExecutionResponse, workflow_node_execution) @console_ns.route("/snippets//workflows/draft/nodes//last-run") @@ -584,7 +567,7 @@ class SnippetDraftNodeLastRunApi(Resource): if node_exec is None: raise NotFound("Node last run not found") - return WorkflowRunNodeExecutionResponse.model_validate(node_exec, from_attributes=True).model_dump(mode="json") + return dump_response(WorkflowRunNodeExecutionResponse, node_exec) @console_ns.route("/snippets//workflows/draft/iteration/nodes//run") @@ -596,7 +579,7 @@ class SnippetDraftRunIterationNodeApi(Resource): @console_ns.response( 200, "Iteration node run started successfully (SSE stream)", - console_ns.models[GeneratedAppResponse.__name__], + console_ns.models[EventStreamResponse.__name__], ) @console_ns.response(404, "Snippet or draft workflow not found") @setup_required @@ -627,6 +610,7 @@ class SnippetDraftRunIterationNodeApi(Resource): session_maker=_snippet_session_maker(), ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except ValueError as e: raise e @@ -644,7 +628,7 @@ class SnippetDraftRunLoopNodeApi(Resource): @console_ns.response( 200, "Loop node run started successfully (SSE stream)", - console_ns.models[GeneratedAppResponse.__name__], + console_ns.models[EventStreamResponse.__name__], ) @console_ns.response(404, "Snippet or draft workflow not found") @setup_required @@ -675,6 +659,7 @@ class SnippetDraftRunLoopNodeApi(Resource): session_maker=_snippet_session_maker(), ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except ValueError as e: raise e @@ -690,7 +675,7 @@ class SnippetDraftWorkflowRunApi(Resource): @console_ns.response( 200, "Draft workflow run started successfully (SSE stream)", - console_ns.models[GeneratedAppResponse.__name__], + console_ns.models[EventStreamResponse.__name__], ) @console_ns.response(404, "Snippet or draft workflow not found") @setup_required @@ -722,6 +707,7 @@ class SnippetDraftWorkflowRunApi(Resource): session_maker=_snippet_session_maker(), ) + # response-contract:ignore compact_generate_response return helper.compact_generate_response(response) except ValueError as e: raise e @@ -757,4 +743,4 @@ class SnippetWorkflowTaskStopApi(Resource): # New graph engine command channel mechanism GraphEngineManager(redis_client).send_stop_command(task_id) - return {"result": "success"} + return SimpleResultResponse(result="success").model_dump(mode="json") diff --git a/api/controllers/console/snippets/snippet_workflow_draft_variable.py b/api/controllers/console/snippets/snippet_workflow_draft_variable.py index 4befd259666..013a9ad942d 100644 --- a/api/controllers/console/snippets/snippet_workflow_draft_variable.py +++ b/api/controllers/console/snippets/snippet_workflow_draft_variable.py @@ -15,7 +15,7 @@ from functools import wraps from typing import Any, Concatenate from flask import Response, request -from flask_restx import Resource, marshal, marshal_with +from flask_restx import Resource from sqlalchemy.orm import Session, sessionmaker from controllers.common.errors import InvalidArgumentError, NotFoundError @@ -23,14 +23,15 @@ from controllers.common.schema import query_params_from_model from controllers.console import console_ns from controllers.console.app.error import DraftWorkflowNotExist from controllers.console.app.workflow_draft_variable import ( - EnvironmentVariableListResponse, + WorkflowDraftEnvironmentVariableListResponse, + WorkflowDraftEnvironmentVariableResponse, WorkflowDraftVariableListQuery, + WorkflowDraftVariableListResponse, + WorkflowDraftVariableListWithoutValueResponse, + WorkflowDraftVariableResponse, WorkflowDraftVariableUpdatePayload, ensure_variable_access, validate_node_id, - workflow_draft_variable_list_model, - workflow_draft_variable_list_without_value_model, - workflow_draft_variable_model, ) from controllers.console.snippets.snippet_workflow import get_snippet from controllers.console.wraps import ( @@ -101,12 +102,11 @@ class SnippetWorkflowVariableCollectionApi(Resource): @console_ns.response( 200, "Workflow variables retrieved successfully", - workflow_draft_variable_list_without_value_model, + console_ns.models[WorkflowDraftVariableListWithoutValueResponse.__name__], ) @_snippet_draft_var_prerequisite - @marshal_with(workflow_draft_variable_list_without_value_model) @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.SNIPPETS_MANAGE, resource_required=False) - def get(self, current_user: Account, snippet: CustomizedSnippet) -> WorkflowDraftVariableList: + def get(self, current_user: Account, snippet: CustomizedSnippet) -> dict[str, Any]: args = WorkflowDraftVariableListQuery.model_validate(request.args.to_dict(flat=True)) # type: ignore snippet_service = _snippet_service() @@ -123,7 +123,9 @@ class SnippetWorkflowVariableCollectionApi(Resource): exclude_node_ids=_SNIPPET_EXCLUDED_DRAFT_VARIABLE_NODE_IDS, ) - return workflow_vars + return WorkflowDraftVariableListWithoutValueResponse.from_workflow_draft_variable_list( + workflow_vars + ).model_dump(mode="json") @console_ns.doc("delete_snippet_workflow_variables") @console_ns.doc(description="Delete all draft workflow variables for the current user (snippet scope)") @@ -132,49 +134,53 @@ class SnippetWorkflowVariableCollectionApi(Resource): @rbac_permission_required( RBACResourceScope.WORKSPACE, RBACPermission.SNIPPETS_CREATE_AND_MODIFY, resource_required=False ) - def delete(self, current_user: Account, snippet: CustomizedSnippet) -> Response: + def delete(self, current_user: Account, snippet: CustomizedSnippet) -> tuple[str, int]: draft_var_srv = WorkflowDraftVariableService(session=db.session()) draft_var_srv.delete_user_workflow_variables(snippet.id, user_id=current_user.id) db.session.commit() - return Response("", 204) + return "", 204 @console_ns.route("/snippets//workflows/draft/nodes//variables") class SnippetNodeVariableCollectionApi(Resource): @console_ns.doc("get_snippet_node_variables") @console_ns.doc(description="Get variables for a specific node (snippet draft workflow)") - @console_ns.response(200, "Node variables retrieved successfully", workflow_draft_variable_list_model) + @console_ns.response( + 200, + "Node variables retrieved successfully", + console_ns.models[WorkflowDraftVariableListResponse.__name__], + ) @_snippet_draft_var_prerequisite - @marshal_with(workflow_draft_variable_list_model) - def get(self, current_user: Account, snippet: CustomizedSnippet, node_id: str) -> WorkflowDraftVariableList: + def get(self, current_user: Account, snippet: CustomizedSnippet, node_id: str) -> dict[str, Any]: validate_node_id(node_id) with Session(bind=db.engine, expire_on_commit=False) as session: draft_var_srv = WorkflowDraftVariableService(session=session) node_vars = draft_var_srv.list_node_variables(snippet.id, node_id, user_id=current_user.id) - return node_vars + return WorkflowDraftVariableListResponse.from_workflow_draft_variable_list(node_vars).model_dump(mode="json") @console_ns.doc("delete_snippet_node_variables") @console_ns.doc(description="Delete all variables for a specific node (snippet draft workflow)") @console_ns.response(204, "Node variables deleted successfully") @_snippet_draft_var_prerequisite - def delete(self, current_user: Account, snippet: CustomizedSnippet, node_id: str) -> Response: + def delete(self, current_user: Account, snippet: CustomizedSnippet, node_id: str): validate_node_id(node_id) srv = WorkflowDraftVariableService(db.session()) srv.delete_node_variables(snippet.id, node_id, user_id=current_user.id) db.session.commit() - return Response("", 204) + return "", 204 @console_ns.route("/snippets//workflows/draft/variables/") class SnippetVariableApi(Resource): @console_ns.doc("get_snippet_workflow_variable") @console_ns.doc(description="Get a specific draft workflow variable (snippet scope)") - @console_ns.response(200, "Variable retrieved successfully", workflow_draft_variable_model) + @console_ns.response( + 200, "Variable retrieved successfully", console_ns.models[WorkflowDraftVariableResponse.__name__] + ) @console_ns.response(404, "Variable not found") @_snippet_draft_var_prerequisite - @marshal_with(workflow_draft_variable_model) - def get(self, current_user: Account, snippet: CustomizedSnippet, variable_id: str) -> WorkflowDraftVariable: + def get(self, current_user: Account, snippet: CustomizedSnippet, variable_id: str) -> dict[str, Any]: draft_var_srv = WorkflowDraftVariableService(session=db.session()) variable = ensure_variable_access( variable=draft_var_srv.get_variable(variable_id=variable_id), @@ -183,16 +189,17 @@ class SnippetVariableApi(Resource): current_user_id=current_user.id, ) _ensure_snippet_draft_variable_row_allowed(variable=variable, variable_id=variable_id) - return variable + return WorkflowDraftVariableResponse.from_workflow_draft_variable(variable).model_dump(mode="json") @console_ns.doc("update_snippet_workflow_variable") @console_ns.doc(description="Update a draft workflow variable (snippet scope)") @console_ns.expect(console_ns.models[WorkflowDraftVariableUpdatePayload.__name__]) - @console_ns.response(200, "Variable updated successfully", workflow_draft_variable_model) + @console_ns.response( + 200, "Variable updated successfully", console_ns.models[WorkflowDraftVariableResponse.__name__] + ) @console_ns.response(404, "Variable not found") @_snippet_draft_var_prerequisite - @marshal_with(workflow_draft_variable_model) - def patch(self, current_user: Account, snippet: CustomizedSnippet, variable_id: str) -> WorkflowDraftVariable: + def patch(self, current_user: Account, snippet: CustomizedSnippet, variable_id: str) -> dict[str, Any]: draft_var_srv = WorkflowDraftVariableService(session=db.session()) args_model = WorkflowDraftVariableUpdatePayload.model_validate(console_ns.payload or {}) @@ -207,39 +214,46 @@ class SnippetVariableApi(Resource): new_name = args_model.name raw_value = args_model.value if new_name is None and raw_value is None: - return variable + return WorkflowDraftVariableResponse.from_workflow_draft_variable(variable).model_dump(mode="json") new_value = None if raw_value is not None: - if variable.value_type == SegmentType.FILE: - if not isinstance(raw_value, dict): - raise InvalidArgumentError(description=f"expected dict for file, got {type(raw_value)}") - raw_value = build_from_mapping( + new_value_input: Any + match variable.value_type: + case SegmentType.FILE: + if not isinstance(raw_value, dict): + raise InvalidArgumentError(description=f"expected dict for file, got {type(raw_value)}") + new_value_input = build_from_mapping( mapping=raw_value, tenant_id=snippet.tenant_id, access_controller=_file_access_controller, ) - elif variable.value_type == SegmentType.ARRAY_FILE: - if not isinstance(raw_value, list): - raise InvalidArgumentError(description=f"expected list for files, got {type(raw_value)}") - if len(raw_value) > 0 and not isinstance(raw_value[0], dict): - raise InvalidArgumentError(description=f"expected dict for files[0], got {type(raw_value)}") - raw_value = build_from_mappings( + case SegmentType.ARRAY_FILE: + if not isinstance(raw_value, list): + raise InvalidArgumentError(description=f"expected list for files, got {type(raw_value)}") + for index, item in enumerate(raw_value): + if not isinstance(item, dict): + raise InvalidArgumentError( + description=f"expected dict for files[{index}], got {type(item)}" + ) + new_value_input = build_from_mappings( mappings=raw_value, tenant_id=snippet.tenant_id, access_controller=_file_access_controller, ) - new_value = build_segment_with_type(variable.value_type, raw_value) + case _: + new_value_input = raw_value + new_value = build_segment_with_type(variable.value_type, new_value_input) draft_var_srv.update_variable(variable, name=new_name, value=new_value) db.session.commit() - return variable + return WorkflowDraftVariableResponse.from_workflow_draft_variable(variable).model_dump(mode="json") @console_ns.doc("delete_snippet_workflow_variable") @console_ns.doc(description="Delete a draft workflow variable (snippet scope)") @console_ns.response(204, "Variable deleted successfully") @console_ns.response(404, "Variable not found") @_snippet_draft_var_prerequisite - def delete(self, current_user: Account, snippet: CustomizedSnippet, variable_id: str) -> Response: + def delete(self, current_user: Account, snippet: CustomizedSnippet, variable_id: str): draft_var_srv = WorkflowDraftVariableService(session=db.session()) variable = ensure_variable_access( variable=draft_var_srv.get_variable(variable_id=variable_id), @@ -250,14 +264,14 @@ class SnippetVariableApi(Resource): _ensure_snippet_draft_variable_row_allowed(variable=variable, variable_id=variable_id) draft_var_srv.delete_variable(variable) db.session.commit() - return Response("", 204) + return "", 204 @console_ns.route("/snippets//workflows/draft/variables//reset") class SnippetVariableResetApi(Resource): @console_ns.doc("reset_snippet_workflow_variable") @console_ns.doc(description="Reset a draft workflow variable to its default value (snippet scope)") - @console_ns.response(200, "Variable reset successfully", workflow_draft_variable_model) + @console_ns.response(200, "Variable reset successfully", console_ns.models[WorkflowDraftVariableResponse.__name__]) @console_ns.response(204, "Variable reset (no content)") @console_ns.response(404, "Variable not found") @_snippet_draft_var_prerequisite @@ -281,7 +295,7 @@ class SnippetVariableResetApi(Resource): db.session.commit() if resetted is None: return Response("", 204) - return marshal(resetted, workflow_draft_variable_model) + return WorkflowDraftVariableResponse.from_workflow_draft_variable(resetted).model_dump(mode="json") @console_ns.route("/snippets//workflows/draft/conversation-variables") @@ -290,11 +304,16 @@ class SnippetConversationVariableCollectionApi(Resource): @console_ns.doc( description="Conversation variables are not used in snippet workflows; returns an empty list for API parity" ) - @console_ns.response(200, "Conversation variables retrieved successfully", workflow_draft_variable_list_model) + @console_ns.response( + 200, + "Conversation variables retrieved successfully", + console_ns.models[WorkflowDraftVariableListResponse.__name__], + ) @_snippet_draft_var_prerequisite - @marshal_with(workflow_draft_variable_list_model) - def get(self, _current_user: Account, snippet: CustomizedSnippet) -> WorkflowDraftVariableList: - return WorkflowDraftVariableList(variables=[]) + def get(self, _current_user: Account, snippet: CustomizedSnippet) -> dict[str, Any]: + return WorkflowDraftVariableListResponse.from_workflow_draft_variable_list( + WorkflowDraftVariableList(variables=[]) + ).model_dump(mode="json") @console_ns.route("/snippets//workflows/draft/system-variables") @@ -303,11 +322,16 @@ class SnippetSystemVariableCollectionApi(Resource): @console_ns.doc( description="System variables are not used in snippet workflows; returns an empty list for API parity" ) - @console_ns.response(200, "System variables retrieved successfully", workflow_draft_variable_list_model) + @console_ns.response( + 200, + "System variables retrieved successfully", + console_ns.models[WorkflowDraftVariableListResponse.__name__], + ) @_snippet_draft_var_prerequisite - @marshal_with(workflow_draft_variable_list_model) - def get(self, _current_user: Account, snippet: CustomizedSnippet) -> WorkflowDraftVariableList: - return WorkflowDraftVariableList(variables=[]) + def get(self, _current_user: Account, snippet: CustomizedSnippet) -> dict[str, Any]: + return WorkflowDraftVariableListResponse.from_workflow_draft_variable_list( + WorkflowDraftVariableList(variables=[]) + ).model_dump(mode="json") @console_ns.route("/snippets//workflows/draft/environment-variables") @@ -317,7 +341,7 @@ class SnippetEnvironmentVariableCollectionApi(Resource): @console_ns.response( 200, "Environment variables retrieved successfully", - console_ns.models[EnvironmentVariableListResponse.__name__], + console_ns.models[WorkflowDraftEnvironmentVariableListResponse.__name__], ) @console_ns.response(404, "Draft workflow not found") @_snippet_draft_var_prerequisite @@ -327,21 +351,21 @@ class SnippetEnvironmentVariableCollectionApi(Resource): if workflow is None: raise DraftWorkflowNotExist() - env_vars_list: list[dict[str, Any]] = [] + env_vars_list: list[WorkflowDraftEnvironmentVariableResponse] = [] for v in workflow.environment_variables: env_vars_list.append( - { - "id": v.id, - "type": "env", - "name": v.name, - "description": v.description, - "selector": v.selector, - "value_type": v.value_type.exposed_type().value, - "value": v.value, - "edited": False, - "visible": True, - "editable": True, - } + WorkflowDraftEnvironmentVariableResponse( + id=v.id, + type="env", + name=v.name, + description=v.description, + selector=list(v.selector), + value_type=v.value_type.exposed_type().value, + value=v.value, + edited=False, + visible=True, + editable=True, + ) ) - return {"items": env_vars_list} + return WorkflowDraftEnvironmentVariableListResponse(items=env_vars_list).model_dump(mode="json") diff --git a/api/fields/member_fields.py b/api/fields/member_fields.py index 9bbcbef8429..80b93f0a24b 100644 --- a/api/fields/member_fields.py +++ b/api/fields/member_fields.py @@ -15,13 +15,13 @@ simple_account_fields = { } -class SimpleAccount(ResponseModel): +class SimpleAccountResponse(ResponseModel): id: str name: str email: str -class _AccountAvatar(ResponseModel): +class _AccountAvatarResponseMixin(ResponseModel): avatar: str | None = None @computed_field(return_type=str | None) # type: ignore[prop-decorator] @@ -30,7 +30,7 @@ class _AccountAvatar(ResponseModel): return build_avatar_url(self.avatar) -class Account(_AccountAvatar): +class AccountResponse(_AccountAvatarResponseMixin): id: str name: str email: str @@ -48,7 +48,7 @@ class Account(_AccountAvatar): return to_timestamp(value) -class AccountWithRole(_AccountAvatar): +class AccountWithRoleResponse(_AccountAvatarResponseMixin): id: str name: str email: str @@ -65,5 +65,11 @@ class AccountWithRole(_AccountAvatar): return to_timestamp(value) -class AccountWithRoleList(ResponseModel): - accounts: list[AccountWithRole] +class AccountWithRoleListResponse(ResponseModel): + accounts: list[AccountWithRoleResponse] + + +SimpleAccount = SimpleAccountResponse +Account = AccountResponse +AccountWithRole = AccountWithRoleResponse +AccountWithRoleList = AccountWithRoleListResponse diff --git a/api/fields/workflow_run_fields.py b/api/fields/workflow_run_fields.py index 53cdfa234f9..be043fbbd6c 100644 --- a/api/fields/workflow_run_fields.py +++ b/api/fields/workflow_run_fields.py @@ -1,53 +1,14 @@ -"""Workflow run response schemas for console APIs. - -Most workflow-run endpoints should document and serialize responses with the -Pydantic models in this module. The remaining Flask-RESTX field dictionaries are -kept only for workflow app-log endpoints that still build legacy log models. -""" - from __future__ import annotations from datetime import datetime from typing import Any -from flask_restx import Namespace, fields from pydantic import AliasChoices, Field, field_validator from fields.base import ResponseModel from fields.end_user_fields import SimpleEndUser -from fields.member_fields import SimpleAccount -from libs.helper import TimestampField, to_timestamp - -workflow_run_for_log_fields = { - "id": fields.String, - "version": fields.String, - "status": fields.String, - "triggered_from": fields.String, - "error": fields.String, - "elapsed_time": fields.Float, - "total_tokens": fields.Integer, - "total_steps": fields.Integer, - "created_at": TimestampField, - "finished_at": TimestampField, - "exceptions_count": fields.Integer, -} - - -def build_workflow_run_for_log_model(api_or_ns: Namespace): - return api_or_ns.model("WorkflowRunForLog", workflow_run_for_log_fields) - - -workflow_run_for_archived_log_fields = { - "id": fields.String, - "status": fields.String, - "triggered_from": fields.String, - "elapsed_time": fields.Float, - "total_tokens": fields.Integer, -} - - -def build_workflow_run_for_archived_log_model(api_or_ns: Namespace): - return api_or_ns.model("WorkflowRunForArchivedLog", workflow_run_for_archived_log_fields) +from fields.member_fields import SimpleAccountResponse +from libs.helper import to_timestamp class WorkflowRunForLogResponse(ResponseModel): @@ -98,7 +59,7 @@ class WorkflowRunForListResponse(ResponseModel): elapsed_time: float | None = None total_tokens: int | None = None total_steps: int | None = None - created_by_account: SimpleAccount | None = None + created_by_account: SimpleAccountResponse | None = None created_at: int | None = None finished_at: int | None = None exceptions_count: int | None = None @@ -158,7 +119,7 @@ class WorkflowRunDetailResponse(ResponseModel): total_tokens: int | None = None total_steps: int | None = None created_by_role: str | None = None - created_by_account: SimpleAccount | None = None + created_by_account: SimpleAccountResponse | None = None created_by_end_user: SimpleEndUser | None = None created_at: int | None = None finished_at: int | None = None @@ -194,7 +155,7 @@ class WorkflowRunNodeExecutionResponse(ResponseModel): extras: Any = None created_at: int | None = None created_by_role: str | None = None - created_by_account: SimpleAccount | None = None + created_by_account: SimpleAccountResponse | None = None created_by_end_user: SimpleEndUser | None = None finished_at: int | None = None inputs_truncated: bool | None = None diff --git a/api/openapi/markdown/console-openapi.md b/api/openapi/markdown/console-openapi.md index b3a0b8a6a71..2e02f841fd8 100644 --- a/api/openapi/markdown/console-openapi.md +++ b/api/openapi/markdown/console-openapi.md @@ -38,7 +38,7 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /account/change-email #### Request Body @@ -77,7 +77,7 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /account/change-email/validity #### Request Body @@ -198,7 +198,7 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /account/interface-theme #### Request Body @@ -211,7 +211,7 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /account/name #### Request Body @@ -224,7 +224,7 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /account/password #### Request Body @@ -237,14 +237,14 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [GET] /account/profile #### Responses | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /account/timezone #### Request Body @@ -257,7 +257,7 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /activate Activate account with invitation token @@ -1433,7 +1433,7 @@ Get human input form preview for advanced chat workflow | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Human input form preview | **application/json**: [HumanInputFormPreviewResponse](#humaninputformpreviewresponse)
| +| 200 | Human input form preview retrieved | **application/json**: [HumanInputFormPreviewResponse](#humaninputformpreviewresponse)
| ### [POST] /apps/{app_id}/advanced-chat/workflows/draft/human-input/nodes/{node_id}/form/run **Submit human input form preview** @@ -1455,9 +1455,9 @@ Submit human input form preview for advanced chat workflow #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Human input form submission result | **application/json**: [HumanInputFormSubmitResponse](#humaninputformsubmitresponse)
| +| Code | Description | +| ---- | ----------- | +| 200 | Human input form submitted | ### [POST] /apps/{app_id}/advanced-chat/workflows/draft/iteration/nodes/{node_id}/run **Run draft workflow iteration node** @@ -1479,11 +1479,11 @@ Run draft workflow iteration node for advanced chat #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Iteration node run started successfully | **application/json**: [GeneratedAppResponse](#generatedappresponse)
| -| 403 | Permission denied | | -| 404 | Node not found | | +| Code | Description | +| ---- | ----------- | +| 200 | Iteration node run started successfully | +| 403 | Permission denied | +| 404 | Node not found | ### [POST] /apps/{app_id}/advanced-chat/workflows/draft/loop/nodes/{node_id}/run **Run draft workflow loop node** @@ -1505,11 +1505,11 @@ Run draft workflow loop node for advanced chat #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Loop node run started successfully | **application/json**: [GeneratedAppResponse](#generatedappresponse)
| -| 403 | Permission denied | | -| 404 | Node not found | | +| Code | Description | +| ---- | ----------- | +| 200 | Loop node run started successfully | +| 403 | Permission denied | +| 404 | Node not found | ### [POST] /apps/{app_id}/advanced-chat/workflows/draft/run **Run draft workflow** @@ -1530,11 +1530,11 @@ Run draft workflow for advanced chat application #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Workflow run started successfully | **application/json**: [GeneratedAppResponse](#generatedappresponse)
| -| 400 | Invalid request parameters | | -| 403 | Permission denied | | +| Code | Description | +| ---- | ----------- | +| 200 | Workflow run started successfully | +| 400 | Invalid request parameters | +| 403 | Permission denied | ### [GET] /apps/{app_id}/agent/drive/files List agent drive entries (read-only inspector; one endpoint for both tabs) @@ -3475,9 +3475,9 @@ Get default block configurations for workflow #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Default block configurations retrieved successfully | **application/json**: [DefaultBlockConfigsResponse](#defaultblockconfigsresponse)
| +| Code | Description | +| ---- | ----------- | +| 200 | Default block configurations retrieved successfully | ### [GET] /apps/{app_id}/workflows/default-workflow-block-configs/{block_type} **Get default block config** @@ -3494,10 +3494,10 @@ Get default block configuration by type #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Default block configuration retrieved successfully | **application/json**: [DefaultBlockConfigResponse](#defaultblockconfigresponse)
| -| 404 | Block type not found | | +| Code | Description | +| ---- | ----------- | +| 200 | Default block configuration retrieved successfully | +| 404 | Block type not found | ### [GET] /apps/{app_id}/workflows/draft **Get draft workflow** @@ -3555,7 +3555,7 @@ Get conversation variables for workflow | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Conversation variables retrieved successfully | **application/json**: [WorkflowDraftVariableList](#workflowdraftvariablelist)
| +| 200 | Conversation variables retrieved successfully | **application/json**: [WorkflowDraftVariableListResponse](#workflowdraftvariablelistresponse)
| | 404 | Draft workflow not found | | ### [POST] /apps/{app_id}/workflows/draft/conversation-variables @@ -3594,7 +3594,7 @@ Get environment variables for workflow | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Environment variables retrieved successfully | **application/json**: [EnvironmentVariableListResponse](#environmentvariablelistresponse)
| +| 200 | Environment variables retrieved successfully | **application/json**: [WorkflowDraftEnvironmentVariableListResponse](#workflowdraftenvironmentvariablelistresponse)
| | 404 | Draft workflow not found | | ### [POST] /apps/{app_id}/workflows/draft/environment-variables @@ -3661,7 +3661,7 @@ Test human input delivery for workflow | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Human input delivery test result | **application/json**: [EmptyObjectResponse](#emptyobjectresponse)
| +| 200 | Human input delivery tested | **application/json**: [HumanInputDeliveryTestResponse](#humaninputdeliverytestresponse)
| ### [POST] /apps/{app_id}/workflows/draft/human-input/nodes/{node_id}/form/preview **Preview human input form content and placeholders** @@ -3685,7 +3685,7 @@ Get human input form preview for workflow | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Human input form preview | **application/json**: [HumanInputFormPreviewResponse](#humaninputformpreviewresponse)
| +| 200 | Human input form preview retrieved | **application/json**: [HumanInputFormPreviewResponse](#humaninputformpreviewresponse)
| ### [POST] /apps/{app_id}/workflows/draft/human-input/nodes/{node_id}/form/run **Submit human input form preview** @@ -3707,9 +3707,9 @@ Submit human input form preview for workflow #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Human input form submission result | **application/json**: [HumanInputFormSubmitResponse](#humaninputformsubmitresponse)
| +| Code | Description | +| ---- | ----------- | +| 200 | Human input form submitted | ### [POST] /apps/{app_id}/workflows/draft/iteration/nodes/{node_id}/run **Run draft workflow iteration node** @@ -3729,11 +3729,11 @@ Submit human input form preview for workflow #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Workflow iteration node run started successfully | **application/json**: [GeneratedAppResponse](#generatedappresponse)
| -| 403 | Permission denied | | -| 404 | Node not found | | +| Code | Description | +| ---- | ----------- | +| 200 | Workflow iteration node run started successfully | +| 403 | Permission denied | +| 404 | Node not found | ### [POST] /apps/{app_id}/workflows/draft/loop/nodes/{node_id}/run **Run draft workflow loop node** @@ -3753,11 +3753,11 @@ Submit human input form preview for workflow #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Workflow loop node run started successfully | **application/json**: [GeneratedAppResponse](#generatedappresponse)
| -| 403 | Permission denied | | -| 404 | Node not found | | +| Code | Description | +| ---- | ----------- | +| 200 | Workflow loop node run started successfully | +| 403 | Permission denied | +| 404 | Node not found | ### [GET] /apps/{app_id}/workflows/draft/nodes/{node_id}/agent-composer #### Parameters @@ -3943,7 +3943,7 @@ Get last run result for draft workflow node | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Trigger event received and node executed successfully | **application/json**: [GeneratedAppResponse](#generatedappresponse)
| +| 200 | Trigger event received and node executed successfully | **application/json**: [WorkflowRunNodeExecutionResponse](#workflowrunnodeexecutionresponse)
| | 403 | Permission denied | | | 500 | Internal server error | | @@ -3977,7 +3977,7 @@ Get variables for a specific node | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Node variables retrieved successfully | **application/json**: [WorkflowDraftVariableList](#workflowdraftvariablelist)
| +| 200 | Node variables retrieved successfully | **application/json**: [WorkflowDraftVariableListResponse](#workflowdraftvariablelistresponse)
| ### [POST] /apps/{app_id}/workflows/draft/run **Run draft workflow** @@ -3996,10 +3996,10 @@ Get variables for a specific node #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Draft workflow run started successfully | **application/json**: [GeneratedAppResponse](#generatedappresponse)
| -| 403 | Permission denied | | +| Code | Description | +| ---- | ----------- | +| 200 | Draft workflow run started successfully | +| 403 | Permission denied | ### [GET] /apps/{app_id}/workflows/draft/runs/{run_id}/node-outputs Snapshot of every node's declared outputs for a draft workflow run. @@ -4085,7 +4085,7 @@ Get system variables for workflow | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | System variables retrieved successfully | **application/json**: [WorkflowDraftVariableList](#workflowdraftvariablelist)
| +| 200 | System variables retrieved successfully | **application/json**: [WorkflowDraftVariableListResponse](#workflowdraftvariablelistresponse)
| ### [POST] /apps/{app_id}/workflows/draft/trigger/run **Poll for trigger events and execute full workflow when event arrives** @@ -4100,15 +4100,15 @@ Get system variables for workflow | Required | Schema | | -------- | ------ | -| Yes | **application/json**: [DraftWorkflowTriggerRunRequest](#draftworkflowtriggerrunrequest)
| +| Yes | **application/json**: [DraftWorkflowTriggerRunPayload](#draftworkflowtriggerrunpayload)
| #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Trigger event received and workflow executed successfully | **application/json**: [GeneratedAppResponse](#generatedappresponse)
| -| 403 | Permission denied | | -| 500 | Internal server error | | +| Code | Description | +| ---- | ----------- | +| 200 | Trigger event received and workflow executed successfully | +| 403 | Permission denied | +| 500 | Internal server error | ### [POST] /apps/{app_id}/workflows/draft/trigger/run-all **Full workflow debug when the start node is a trigger** @@ -4127,11 +4127,11 @@ Get system variables for workflow #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Workflow executed successfully | **application/json**: [GeneratedAppResponse](#generatedappresponse)
| -| 403 | Permission denied | | -| 500 | Internal server error | | +| Code | Description | +| ---- | ----------- | +| 200 | Workflow executed successfully | +| 403 | Permission denied | +| 500 | Internal server error | ### [DELETE] /apps/{app_id}/workflows/draft/variables Delete all draft workflow variables @@ -4165,7 +4165,7 @@ Get draft workflow variables | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Workflow variables retrieved successfully | **application/json**: [WorkflowDraftVariableListWithoutValue](#workflowdraftvariablelistwithoutvalue)
| +| 200 | Workflow variables retrieved successfully | **application/json**: [WorkflowDraftVariableListWithoutValueResponse](#workflowdraftvariablelistwithoutvalueresponse)
| ### [DELETE] /apps/{app_id}/workflows/draft/variables/{variable_id} Delete a workflow variable @@ -4198,7 +4198,7 @@ Get a specific workflow variable | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Variable retrieved successfully | **application/json**: [WorkflowDraftVariable](#workflowdraftvariable)
| +| 200 | Variable retrieved successfully | **application/json**: [WorkflowDraftVariableResponse](#workflowdraftvariableresponse)
| | 404 | Variable not found | | ### [PATCH] /apps/{app_id}/workflows/draft/variables/{variable_id} @@ -4221,7 +4221,7 @@ Update a workflow variable | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Variable updated successfully | **application/json**: [WorkflowDraftVariable](#workflowdraftvariable)
| +| 200 | Variable updated successfully | **application/json**: [WorkflowDraftVariableResponse](#workflowdraftvariableresponse)
| | 404 | Variable not found | | ### [PUT] /apps/{app_id}/workflows/draft/variables/{variable_id}/reset @@ -4238,7 +4238,7 @@ Reset a workflow variable to its default value | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Variable reset successfully | **application/json**: [WorkflowDraftVariable](#workflowdraftvariable)
| +| 200 | Variable reset successfully | **application/json**: [WorkflowDraftVariableResponse](#workflowdraftvariableresponse)
| | 204 | Variable reset (no content) | | | 404 | Variable not found | | @@ -4278,7 +4278,7 @@ Get published workflow for an application | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Workflow published successfully | **application/json**: [WorkflowPublishResponse](#workflowpublishresponse)
| +| 200 | Workflow published successfully | **application/json**: [PublishWorkflowResponse](#publishworkflowresponse)
| ### [GET] /apps/{app_id}/workflows/published/runs/{run_id}/node-outputs Snapshot of every node's declared outputs for a published workflow run. @@ -4423,7 +4423,7 @@ Restore a published workflow version into the draft workflow | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Workflow restored successfully | **application/json**: [WorkflowRestoreResponse](#workflowrestoreresponse)
| +| 200 | Workflow restored successfully | **application/json**: [SyncDraftWorkflowResponse](#syncdraftworkflowresponse)
| | 400 | Source workflow must be published | | | 404 | Workflow not found | | @@ -7231,7 +7231,7 @@ Initiate OAuth login process | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [RagPipelineOpaqueResponse](#ragpipelineopaqueresponse)
| +| 200 | Datasource plugins retrieved successfully | **application/json**: [DatasourcePluginListResponse](#datasourcepluginlistresponse)
| ### [POST] /rag/pipelines/imports #### Request Body @@ -7286,7 +7286,7 @@ Initiate OAuth login process | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [RagPipelineOpaqueResponse](#ragpipelineopaqueresponse)
| +| 200 | Recommended plugins retrieved successfully | **application/json**: [RagPipelineRecommendedPluginResponse](#ragpipelinerecommendedpluginresponse)
| ### [POST] /rag/pipelines/transform/datasets/{dataset_id} #### Parameters @@ -7299,7 +7299,7 @@ Initiate OAuth login process | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [RagPipelineOpaqueResponse](#ragpipelineopaqueresponse)
| +| 200 | Dataset transformed successfully | **application/json**: [RagPipelineTransformResponse](#ragpipelinetransformresponse)
| ### [POST] /rag/pipelines/{pipeline_id}/customized/publish #### Parameters @@ -7430,9 +7430,9 @@ Initiate OAuth login process #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Default block configs retrieved successfully | **application/json**: [DefaultBlockConfigsResponse](#defaultblockconfigsresponse)
| +| Code | Description | +| ---- | ----------- | +| 200 | Default workflow block configurations retrieved successfully | ### [GET] /rag/pipelines/{pipeline_id}/workflows/default-workflow-block-configs/{block_type} **Get default block config** @@ -7447,9 +7447,9 @@ Initiate OAuth login process #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Default block config retrieved successfully | **application/json**: [DefaultBlockConfigResponse](#defaultblockconfigresponse)
| +| Code | Description | +| ---- | ----------- | +| 200 | Default workflow block configuration retrieved successfully | ### [GET] /rag/pipelines/{pipeline_id}/workflows/draft **Get draft rag pipeline's workflow** @@ -7506,9 +7506,9 @@ Initiate OAuth login process #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Success | **application/json**: [RagPipelineOpaqueResponse](#ragpipelineopaqueresponse)
| +| Code | Description | +| ---- | ----------- | +| 200 | Success | ### [POST] /rag/pipelines/{pipeline_id}/workflows/draft/datasource/variables-inspect **Set datasource variables** @@ -7544,7 +7544,7 @@ Initiate OAuth login process | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Environment variables retrieved successfully | **application/json**: [EnvironmentVariableListResponse](#environmentvariablelistresponse)
| +| 200 | Environment variables retrieved successfully | **application/json**: [RagPipelineEnvironmentVariableListResponse](#ragpipelineenvironmentvariablelistresponse)
| ### [POST] /rag/pipelines/{pipeline_id}/workflows/draft/iteration/nodes/{node_id}/run **Run draft workflow iteration node** @@ -7564,9 +7564,9 @@ Initiate OAuth login process #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Success | **application/json**: [RagPipelineOpaqueResponse](#ragpipelineopaqueresponse)
| +| Code | Description | +| ---- | ----------- | +| 200 | Success | ### [POST] /rag/pipelines/{pipeline_id}/workflows/draft/loop/nodes/{node_id}/run **Run draft workflow loop node** @@ -7586,9 +7586,9 @@ Initiate OAuth login process #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Success | **application/json**: [RagPipelineOpaqueResponse](#ragpipelineopaqueresponse)
| +| Code | Description | +| ---- | ----------- | +| 200 | Success | ### [GET] /rag/pipelines/{pipeline_id}/workflows/draft/nodes/{node_id}/last-run #### Parameters @@ -7652,7 +7652,7 @@ Initiate OAuth login process | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Node variables retrieved successfully | **application/json**: [WorkflowDraftVariableList](#workflowdraftvariablelist)
| +| 200 | Node variables retrieved successfully | **application/json**: [WorkflowDraftVariableListResponse](#workflowdraftvariablelistresponse)
| ### [GET] /rag/pipelines/{pipeline_id}/workflows/draft/pre-processing/parameters **Get first step parameters of rag pipeline** @@ -7668,7 +7668,7 @@ Initiate OAuth login process | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [RagPipelineStepParametersResponse](#ragpipelinestepparametersresponse)
| +| 200 | First step parameters retrieved successfully | **application/json**: [RagPipelineVariablesResponse](#ragpipelinevariablesresponse)
| ### [GET] /rag/pipelines/{pipeline_id}/workflows/draft/processing/parameters **Get second step parameters of rag pipeline** @@ -7684,7 +7684,7 @@ Initiate OAuth login process | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [RagPipelineStepParametersResponse](#ragpipelinestepparametersresponse)
| +| 200 | Second step parameters retrieved successfully | **application/json**: [RagPipelineVariablesResponse](#ragpipelinevariablesresponse)
| ### [POST] /rag/pipelines/{pipeline_id}/workflows/draft/run **Run draft workflow** @@ -7703,9 +7703,9 @@ Initiate OAuth login process #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Success | **application/json**: [RagPipelineOpaqueResponse](#ragpipelineopaqueresponse)
| +| Code | Description | +| ---- | ----------- | +| 200 | Success | ### [GET] /rag/pipelines/{pipeline_id}/workflows/draft/system-variables #### Parameters @@ -7718,7 +7718,7 @@ Initiate OAuth login process | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | System variables retrieved successfully | **application/json**: [WorkflowDraftVariableList](#workflowdraftvariablelist)
| +| 200 | System variables retrieved successfully | **application/json**: [WorkflowDraftVariableListResponse](#workflowdraftvariablelistresponse)
| ### [DELETE] /rag/pipelines/{pipeline_id}/workflows/draft/variables #### Parameters @@ -7731,7 +7731,7 @@ Initiate OAuth login process | Code | Description | | ---- | ----------- | -| 204 | Workflow variables deleted successfully | +| 204 | Variables deleted successfully | ### [GET] /rag/pipelines/{pipeline_id}/workflows/draft/variables **Get draft workflow** @@ -7740,15 +7740,13 @@ Initiate OAuth login process | Name | Located in | Description | Required | Schema | | ---- | ---------- | ----------- | -------- | ------ | -| limit | query | | No | integer,
**Default:** 20 | -| page | query | | No | integer,
**Default:** 1 | | pipeline_id | path | | Yes | string (uuid) | #### Responses | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Workflow variables retrieved successfully | **application/json**: [WorkflowDraftVariableListWithoutValue](#workflowdraftvariablelistwithoutvalue)
| +| 200 | Variables retrieved successfully | **application/json**: [WorkflowDraftVariableListWithoutValueResponse](#workflowdraftvariablelistwithoutvalueresponse)
| ### [DELETE] /rag/pipelines/{pipeline_id}/workflows/draft/variables/{variable_id} #### Parameters @@ -7776,7 +7774,7 @@ Initiate OAuth login process | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Variable retrieved successfully | **application/json**: [WorkflowDraftVariable](#workflowdraftvariable)
| +| 200 | Variable retrieved successfully | **application/json**: [WorkflowDraftVariableResponse](#workflowdraftvariableresponse)
| ### [PATCH] /rag/pipelines/{pipeline_id}/workflows/draft/variables/{variable_id} #### Parameters @@ -7796,7 +7794,7 @@ Initiate OAuth login process | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Variable updated successfully | **application/json**: [WorkflowDraftVariable](#workflowdraftvariable)
| +| 200 | Variable updated successfully | **application/json**: [WorkflowDraftVariableResponse](#workflowdraftvariableresponse)
| ### [PUT] /rag/pipelines/{pipeline_id}/workflows/draft/variables/{variable_id}/reset #### Parameters @@ -7810,8 +7808,8 @@ Initiate OAuth login process | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Variable reset successfully | **application/json**: [WorkflowDraftVariable](#workflowdraftvariable)
| -| 204 | Variable reset (no content) | | +| 200 | Variable reset successfully | **application/json**: [WorkflowDraftVariableResponse](#workflowdraftvariableresponse)
| +| 204 | Variable reset to empty state | | ### [GET] /rag/pipelines/{pipeline_id}/workflows/publish **Get published pipeline** @@ -7883,9 +7881,9 @@ Initiate OAuth login process #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Success | **application/json**: [RagPipelineOpaqueResponse](#ragpipelineopaqueresponse)
| +| Code | Description | +| ---- | ----------- | +| 200 | Success | ### [GET] /rag/pipelines/{pipeline_id}/workflows/published/pre-processing/parameters **Get first step parameters of rag pipeline** @@ -7901,7 +7899,7 @@ Initiate OAuth login process | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [RagPipelineStepParametersResponse](#ragpipelinestepparametersresponse)
| +| 200 | First step parameters retrieved successfully | **application/json**: [RagPipelineVariablesResponse](#ragpipelinevariablesresponse)
| ### [GET] /rag/pipelines/{pipeline_id}/workflows/published/processing/parameters **Get second step parameters of rag pipeline** @@ -7917,7 +7915,7 @@ Initiate OAuth login process | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [RagPipelineStepParametersResponse](#ragpipelinestepparametersresponse)
| +| 200 | Second step parameters retrieved successfully | **application/json**: [RagPipelineVariablesResponse](#ragpipelinevariablesresponse)
| ### [POST] /rag/pipelines/{pipeline_id}/workflows/published/run **Run published workflow** @@ -7936,9 +7934,9 @@ Initiate OAuth login process #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Success | **application/json**: [RagPipelineOpaqueResponse](#ragpipelineopaqueresponse)
| +| Code | Description | +| ---- | ----------- | +| 200 | Success | ### [DELETE] /rag/pipelines/{pipeline_id}/workflows/{workflow_id} **Delete a published workflow version that is not currently active on the pipeline** @@ -8192,9 +8190,9 @@ Get all published workflows for a snippet #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Default block configs retrieved successfully | **application/json**: [DefaultBlockConfigsResponse](#defaultblockconfigsresponse)
| +| Code | Description | +| ---- | ----------- | +| 200 | Default block configs retrieved successfully | ### [GET] /snippets/{snippet_id}/workflows/draft **Get draft workflow for snippet** @@ -8231,7 +8229,7 @@ Get all published workflows for a snippet | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Draft workflow synced successfully | **application/json**: [WorkflowRestoreResponse](#workflowrestoreresponse)
| +| 200 | Draft workflow synced successfully | **application/json**: [SyncDraftWorkflowResponse](#syncdraftworkflowresponse)
| | 400 | Hash mismatch | | ### [GET] /snippets/{snippet_id}/workflows/draft/config @@ -8262,7 +8260,7 @@ Conversation variables are not used in snippet workflows; returns an empty list | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Conversation variables retrieved successfully | **application/json**: [WorkflowDraftVariableList](#workflowdraftvariablelist)
| +| 200 | Conversation variables retrieved successfully | **application/json**: [WorkflowDraftVariableListResponse](#workflowdraftvariablelistresponse)
| ### [GET] /snippets/{snippet_id}/workflows/draft/environment-variables Get environment variables from snippet draft workflow graph @@ -8277,7 +8275,7 @@ Get environment variables from snippet draft workflow graph | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Environment variables retrieved successfully | **application/json**: [EnvironmentVariableListResponse](#environmentvariablelistresponse)
| +| 200 | Environment variables retrieved successfully | **application/json**: [WorkflowDraftEnvironmentVariableListResponse](#workflowdraftenvironmentvariablelistresponse)
| | 404 | Draft workflow not found | | ### [POST] /snippets/{snippet_id}/workflows/draft/iteration/nodes/{node_id}/run @@ -8304,7 +8302,7 @@ Returns an SSE event stream with iteration progress and results. | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Iteration node run started successfully (SSE stream) | **application/json**: [GeneratedAppResponse](#generatedappresponse)
| +| 200 | Iteration node run started successfully (SSE stream) | **application/json**: [EventStreamResponse](#eventstreamresponse)
| | 404 | Snippet or draft workflow not found | | ### [POST] /snippets/{snippet_id}/workflows/draft/loop/nodes/{node_id}/run @@ -8331,7 +8329,7 @@ Returns an SSE event stream with loop progress and results. | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Loop node run started successfully (SSE stream) | **application/json**: [GeneratedAppResponse](#generatedappresponse)
| +| 200 | Loop node run started successfully (SSE stream) | **application/json**: [EventStreamResponse](#eventstreamresponse)
| | 404 | Snippet or draft workflow not found | | ### [GET] /snippets/{snippet_id}/workflows/draft/nodes/{node_id}/last-run @@ -8412,7 +8410,7 @@ Get variables for a specific node (snippet draft workflow) | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Node variables retrieved successfully | **application/json**: [WorkflowDraftVariableList](#workflowdraftvariablelist)
| +| 200 | Node variables retrieved successfully | **application/json**: [WorkflowDraftVariableListResponse](#workflowdraftvariablelistresponse)
| ### [POST] /snippets/{snippet_id}/workflows/draft/run **Run draft workflow for snippet** @@ -8436,7 +8434,7 @@ and returns an SSE event stream with execution progress and results. | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Draft workflow run started successfully (SSE stream) | **application/json**: [GeneratedAppResponse](#generatedappresponse)
| +| 200 | Draft workflow run started successfully (SSE stream) | **application/json**: [EventStreamResponse](#eventstreamresponse)
| | 404 | Snippet or draft workflow not found | | ### [GET] /snippets/{snippet_id}/workflows/draft/system-variables @@ -8452,7 +8450,7 @@ System variables are not used in snippet workflows; returns an empty list for AP | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | System variables retrieved successfully | **application/json**: [WorkflowDraftVariableList](#workflowdraftvariablelist)
| +| 200 | System variables retrieved successfully | **application/json**: [WorkflowDraftVariableListResponse](#workflowdraftvariablelistresponse)
| ### [DELETE] /snippets/{snippet_id}/workflows/draft/variables Delete all draft workflow variables for the current user (snippet scope) @@ -8484,7 +8482,7 @@ List draft workflow variables without values (paginated, snippet scope) | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Workflow variables retrieved successfully | **application/json**: [WorkflowDraftVariableListWithoutValue](#workflowdraftvariablelistwithoutvalue)
| +| 200 | Workflow variables retrieved successfully | **application/json**: [WorkflowDraftVariableListWithoutValueResponse](#workflowdraftvariablelistwithoutvalueresponse)
| ### [DELETE] /snippets/{snippet_id}/workflows/draft/variables/{variable_id} Delete a draft workflow variable (snippet scope) @@ -8517,7 +8515,7 @@ Get a specific draft workflow variable (snippet scope) | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Variable retrieved successfully | **application/json**: [WorkflowDraftVariable](#workflowdraftvariable)
| +| 200 | Variable retrieved successfully | **application/json**: [WorkflowDraftVariableResponse](#workflowdraftvariableresponse)
| | 404 | Variable not found | | ### [PATCH] /snippets/{snippet_id}/workflows/draft/variables/{variable_id} @@ -8540,7 +8538,7 @@ Update a draft workflow variable (snippet scope) | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Variable updated successfully | **application/json**: [WorkflowDraftVariable](#workflowdraftvariable)
| +| 200 | Variable updated successfully | **application/json**: [WorkflowDraftVariableResponse](#workflowdraftvariableresponse)
| | 404 | Variable not found | | ### [PUT] /snippets/{snippet_id}/workflows/draft/variables/{variable_id}/reset @@ -8557,7 +8555,7 @@ Reset a draft workflow variable to its default value (snippet scope) | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Variable reset successfully | **application/json**: [WorkflowDraftVariable](#workflowdraftvariable)
| +| 200 | Variable reset successfully | **application/json**: [WorkflowDraftVariableResponse](#workflowdraftvariableresponse)
| | 204 | Variable reset (no content) | | | 404 | Variable not found | | @@ -8586,17 +8584,11 @@ Reset a draft workflow variable to its default value (snippet scope) | ---- | ---------- | ----------- | -------- | ------ | | snippet_id | path | | Yes | string (uuid) | -#### Request Body - -| Required | Schema | -| -------- | ------ | -| Yes | **application/json**: [PublishWorkflowPayload](#publishworkflowpayload)
| - #### Responses | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Workflow published successfully | **application/json**: [WorkflowPublishResponse](#workflowpublishresponse)
| +| 200 | Workflow published successfully | **application/json**: [PublishWorkflowResponse](#publishworkflowresponse)
| | 400 | No draft workflow found | | ### [POST] /snippets/{snippet_id}/workflows/{workflow_id}/restore @@ -8613,7 +8605,7 @@ Reset a draft workflow variable to its default value (snippet scope) | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Workflow restored successfully | **application/json**: [WorkflowRestoreResponse](#workflowrestoreresponse)
| +| 200 | Workflow restored successfully | **application/json**: [SyncDraftWorkflowResponse](#syncdraftworkflowresponse)
| | 400 | Source workflow must be published | | | 404 | Workflow not found | | @@ -9268,7 +9260,7 @@ Increment snippet use count by 1 | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [AccountWithRoleList](#accountwithrolelist)
| +| 200 | Success | **application/json**: [AccountWithRoleListResponse](#accountwithrolelistresponse)
| ### [GET] /workspaces/current/default-model #### Parameters @@ -9477,7 +9469,7 @@ Update a plugin endpoint | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [AccountWithRoleList](#accountwithrolelist)
| +| 200 | Success | **application/json**: [AccountWithRoleListResponse](#accountwithrolelistresponse)
| ### [POST] /workspaces/current/members/invite-email #### Request Body @@ -11928,23 +11920,6 @@ Default namespace | role_name | string | | No | | tenant_id | string | | No | -#### Account - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| avatar | string | | No | -| avatar_url | string | | Yes | -| created_at | integer | | No | -| email | string | | Yes | -| id | string | | Yes | -| interface_language | string | | No | -| interface_theme | string | | No | -| is_password_set | boolean | | Yes | -| last_login_at | integer | | No | -| last_login_ip | string | | No | -| name | string | | Yes | -| timezone | string | | No | - #### AccountAvatarPayload | Name | Type | Description | Required | @@ -12020,13 +11995,36 @@ Default namespace | password | string | | No | | repeat_new_password | string | | Yes | +#### AccountResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| avatar | string | | No | +| avatar_url | string | | Yes | +| created_at | integer | | No | +| email | string | | Yes | +| id | string | | Yes | +| interface_language | string | | No | +| interface_theme | string | | No | +| is_password_set | boolean | | Yes | +| last_login_at | integer | | No | +| last_login_ip | string | | No | +| name | string | | Yes | +| timezone | string | | No | + #### AccountTimezonePayload | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | timezone | string | | Yes | -#### AccountWithRole +#### AccountWithRoleListResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| accounts | [ [AccountWithRoleResponse](#accountwithroleresponse) ] | | Yes | + +#### AccountWithRoleResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | @@ -12041,12 +12039,6 @@ Default namespace | roles | [ object ] | | No | | status | string | | Yes | -#### AccountWithRoleList - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| accounts | [ [AccountWithRole](#accountwithrole) ] | | Yes | - #### ActivateCheckQuery | Name | Type | Description | Required | @@ -12095,7 +12087,7 @@ Default namespace | ---- | ---- | ----------- | -------- | | conversation_id | string | | No | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | elapsed_time | number | | No | | exceptions_count | integer | | No | | finished_at | integer | | No | @@ -13909,6 +13901,12 @@ AppMCPServer Status Enum | use_icon_as_answer_icon | boolean | | No | | workflow | [WorkflowPartial](#workflowpartial) | | No | +#### AppSelectorScope + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| AppSelectorScope | string | | | + #### AppSiteResponse | Name | Type | Description | Required | @@ -15411,6 +15409,25 @@ Model class for provider custom model configuration. | ---- | ---- | ----------- | -------- | | id | string | | Yes | +#### DatasourceEntity + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| description | [I18nObject](#i18nobject) | The label of the datasource | Yes | +| identity | [DatasourceIdentity](#datasourceidentity) | | Yes | +| output_schema | object | | No | +| parameters | [ [DatasourceParameter](#datasourceparameter) ] | | No | + +#### DatasourceIdentity + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| author | string | The author of the datasource | Yes | +| icon | string | | No | +| label | [I18nObject](#i18nobject) | The label of the datasource | Yes | +| name | string | The name of the datasource | Yes | +| provider | string | The provider of the datasource | Yes | + #### DatasourceNodeRunPayload | Name | Type | Description | Required | @@ -15434,6 +15451,70 @@ Model class for provider custom model configuration. | error | string | Error message from OAuth provider | No | | state | string | OAuth state parameter | No | +#### DatasourceParameter + +Overrides type + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| auto_generate | [PluginParameterAutoGenerate](#pluginparameterautogenerate) | | No | +| default | number
integer
string
boolean
[ object ]
object | | No | +| description | [I18nObject](#i18nobject) | The description of the parameter | Yes | +| label | [I18nObject](#i18nobject) | The label presented to the user | Yes | +| max | number
integer | | No | +| min | number
integer | | No | +| name | string | The name of the parameter | Yes | +| options | [ [PluginParameterOption](#pluginparameteroption) ] | | No | +| placeholder | [I18nObject](#i18nobject) | The placeholder presented to the user | No | +| precision | integer | | No | +| required | boolean | | No | +| scope | string | | No | +| template | [PluginParameterTemplate](#pluginparametertemplate) | | No | +| type | [DatasourceParameterType](#datasourceparametertype) | The type of the parameter | Yes | + +#### DatasourceParameterType + +removes TOOLS_SELECTOR from PluginParameterType + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| DatasourceParameterType | string | removes TOOLS_SELECTOR from PluginParameterType | | + +#### DatasourcePluginListResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| DatasourcePluginListResponse | array | | | + +#### DatasourceProviderEntityWithPlugin + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| credentials_schema | [ [ProviderConfig](#providerconfig) ] | | No | +| datasources | [ [DatasourceEntity](#datasourceentity) ] | | No | +| identity | [DatasourceProviderIdentity](#datasourceprovideridentity) | | Yes | +| oauth_schema | [OAuthSchema](#oauthschema) | | No | +| provider_type | [DatasourceProviderType](#datasourceprovidertype) | | Yes | + +#### DatasourceProviderIdentity + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| author | string | The author of the tool | Yes | +| description | [I18nObject](#i18nobject) | The description of the tool | Yes | +| icon | string | The icon of the tool | Yes | +| label | [I18nObject](#i18nobject) | The label of the tool | Yes | +| name | string | The name of the tool | Yes | +| tags | [ [ToolLabelEnum](#toollabelenum) ] | The tags of the tool | No | + +#### DatasourceProviderType + +Enum class for datasource provider + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| DatasourceProviderType | string | Enum class for datasource provider | | + #### DatasourceUpdateNamePayload | Name | Type | Description | Required | @@ -15548,18 +15629,6 @@ Per-output retry configuration that mirrors ``graphon.RetryConfig`` shape. | ---- | ---- | ----------- | -------- | | q | string | | No | -#### DefaultBlockConfigResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| DefaultBlockConfigResponse | object | | | - -#### DefaultBlockConfigsResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| DefaultBlockConfigsResponse | array | | | - #### DefaultModelDataResponse | Name | Type | Description | Required | @@ -15768,12 +15837,6 @@ Request payload for bulk downloading documents as a zip archive. | ---- | ---- | ----------- | -------- | | node_id | string | | Yes | -#### DraftWorkflowTriggerRunRequest - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| node_id | string | Node ID | Yes | - #### EducationActivatePayload | Name | Type | Description | Required | @@ -15883,12 +15946,6 @@ Request payload for bulk downloading documents as a zip archive. | email | string | | Yes | | token | string | | Yes | -#### EmptyObjectResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| EmptyObjectResponse | object | | | - #### EndpointCreatePayload | Name | Type | Description | Required | @@ -15969,27 +16026,6 @@ Request payload for bulk downloading documents as a zip archive. | reason | string | | No | | secret_likely | boolean | | No | -#### EnvironmentVariableItemResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| description | string | | No | -| editable | boolean | | Yes | -| edited | boolean | | Yes | -| id | string | | Yes | -| name | string | | Yes | -| selector | [ string ] | | Yes | -| type | string | | Yes | -| value | | | Yes | -| value_type | string | | Yes | -| visible | boolean | | Yes | - -#### EnvironmentVariableListResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| items | [ [EnvironmentVariableItemResponse](#environmentvariableitemresponse) ] | | Yes | - #### EnvironmentVariableUpdatePayload | Name | Type | Description | Required | @@ -16488,6 +16524,11 @@ Enum class for form type. | delivery_method_id | string | Delivery method ID | Yes | | inputs | object | Values used to fill missing upstream variables referenced in form_content | No | +#### HumanInputDeliveryTestResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | + #### HumanInputFormDefinition | Name | Type | Description | Required | @@ -16513,12 +16554,13 @@ Enum class for form type. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | -| actions | [ object ] | | No | -| display_in_ui | boolean | | No | -| expiration_time | integer | | No | +| TYPE | string,
**Default:** human_input_required | | No | +| actions | [ [HumanInputUserActionResponse](#humaninputuseractionresponse) ] | | No | +| display_in_ui | boolean | Always false for draft preview responses. | No | +| expiration_time | integer | Always null for draft preview responses. | No | | form_content | string | | Yes | | form_id | string | | Yes | -| form_token | string | | No | +| form_token | string | Always null for draft preview responses. | No | | inputs | [ object ] | | No | | node_id | string | | Yes | | node_title | string | | Yes | @@ -16543,12 +16585,6 @@ Enum class for form type. | form_inputs | object | Values the user provides for the form's own fields | Yes | | inputs | object | Values used to fill missing upstream variables referenced in form_content | Yes | -#### HumanInputFormSubmitResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| HumanInputFormSubmitResponse | object | | | - #### HumanInputPauseTypeResponse | Name | Type | Description | Required | @@ -16557,6 +16593,14 @@ Enum class for form type. | form_id | string | | Yes | | type | string | | Yes | +#### HumanInputUserActionResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| button_style | string,
**Default:** default | | No | +| id | string | | Yes | +| title | string | | Yes | + #### I18nObject Model class for i18n object. @@ -17281,6 +17325,12 @@ Enum class for model property key. | ---- | ---- | ----------- | -------- | | payment_link | string | | Yes | +#### ModelSelectorScope + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| ModelSelectorScope | string | | | + #### ModelStatus Enum class for model status. @@ -17571,6 +17621,15 @@ Coarse node-level status used by Inspector to pick a banner. | refresh_token | string | | Yes | | token_type | string | | Yes | +#### OAuthSchema + +OAuth schema + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| client_schema | [ [ProviderConfig](#providerconfig) ] | client schema like client_id, client_secret, etc. | No | +| credentials_schema | [ [ProviderConfig](#providerconfig) ] | credentials schema like access_token, refresh_token, etc. | No | + #### OAuthTokenRequest | Name | Type | Description | Required | @@ -17588,6 +17647,13 @@ Coarse node-level status used by Inspector to pick a banner. | ---- | ---- | ----------- | -------- | | OpaqueObjectResponse | object | | | +#### Option + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| label | [I18nObject](#i18nobject) | The label of the option | Yes | +| value | string | The value of the option | Yes | + #### OutputErrorStrategy Per-output failure handling strategy. @@ -18239,6 +18305,16 @@ Shared permission levels for resources (datasets, credentials, etc.) | ---- | ---- | ----------- | -------- | | PluginDaemonOperationResponse | | | | +#### PluginDatasourceProviderEntity + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| declaration | [DatasourceProviderEntityWithPlugin](#datasourceproviderentitywithplugin) | | Yes | +| is_authorized | boolean | | No | +| plugin_id | string | | Yes | +| plugin_unique_identifier | string | | Yes | +| provider | string | | Yes | + #### PluginDebuggingKeyResponse | Name | Type | Description | Required | @@ -18350,6 +18426,26 @@ Shared permission levels for resources (datasets, credentials, etc.) | message | string | | No | | success | boolean | | Yes | +#### PluginParameterAutoGenerate + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| type | [core__plugin__entities__parameters__PluginParameterAutoGenerate__Type](#core__plugin__entities__parameters__pluginparameterautogenerate__type) | | Yes | + +#### PluginParameterOption + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| icon | string | The icon of the option, can be a url or a base64 encoded image | No | +| label | [I18nObject](#i18nobject) | The label of the option | Yes | +| value | string | The value of the option | Yes | + +#### PluginParameterTemplate + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| enabled | boolean | Whether the parameter is jinja enabled | No | + #### PluginPermissionResponse | Name | Type | Description | Required | @@ -18429,6 +18525,24 @@ Dataset Process Rule Mode | ---- | ---- | ----------- | -------- | | ProcessRuleMode | string | Dataset Process Rule Mode | | +#### ProviderConfig + +Model class for common provider settings like credentials + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| default | integer
string
number
boolean | | No | +| help | [I18nObject](#i18nobject) | | No | +| label | [I18nObject](#i18nobject) | | No | +| multiple | boolean | | No | +| name | string | The name of the credentials | Yes | +| options | [ [Option](#option) ] | | No | +| placeholder | [I18nObject](#i18nobject) | | No | +| required | boolean | | No | +| scope | [AppSelectorScope](#appselectorscope)
[ModelSelectorScope](#modelselectorscope)
[ToolSelectorScope](#toolselectorscope) | | No | +| type | [core__entities__provider_entities__BasicProviderConfig__Type](#core__entities__provider_entities__basicproviderconfig__type) | The type of the credentials | Yes | +| url | string | | No | + #### ProviderCredentialResponse | Name | Type | Description | Required | @@ -18559,11 +18673,17 @@ Model class for provider with models response. #### PublishWorkflowPayload -Payload for publishing snippet workflow. +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| marked_comment | string | | No | +| marked_name | string | | No | + +#### PublishWorkflowResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | -| knowledge_base_setting | object | | No | +| created_at | integer | | Yes | +| result | string | | Yes | #### PublishedWorkflowRunPayload @@ -18640,6 +18760,27 @@ Model class for provider quota configuration. | ---- | ---- | ----------- | -------- | | yaml_content | string | | Yes | +#### RagPipelineEnvironmentVariableListResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| items | [ [RagPipelineEnvironmentVariableResponse](#ragpipelineenvironmentvariableresponse) ] | | Yes | + +#### RagPipelineEnvironmentVariableResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| description | string | | Yes | +| editable | boolean | | Yes | +| edited | boolean | | Yes | +| id | string | | Yes | +| name | string | | Yes | +| selector | [ string ] | | Yes | +| type | string | | Yes | +| value | | | Yes | +| value_type | string | | Yes | +| visible | boolean | | Yes | + #### RagPipelineImportCheckDependenciesResponse | Name | Type | Description | Required | @@ -18672,19 +18813,28 @@ Model class for provider quota configuration. | pipeline_id | string | | No | | status | [ImportStatus](#importstatus) | | Yes | -#### RagPipelineOpaqueResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| RagPipelineOpaqueResponse | | | | - #### RagPipelineRecommendedPluginQuery | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | type | string,
**Default:** all | | No | -#### RagPipelineStepParametersResponse +#### RagPipelineRecommendedPluginResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| installed_recommended_plugins | [ object ] | Installed tool provider payloads. Shape follows the tool provider serializer. | Yes | +| uninstalled_recommended_plugins | [ object ] | Marketplace plugin manifest payloads returned by the marketplace service. | Yes | + +#### RagPipelineTransformResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| dataset_id | string | | Yes | +| pipeline_id | string | | Yes | +| status | string | | Yes | + +#### RagPipelineVariablesResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | @@ -19151,6 +19301,14 @@ Model class for provider quota configuration. | id | string | | Yes | | name | string | | Yes | +#### SimpleAccountResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| email | string | | Yes | +| id | string | | Yes | +| name | string | | Yes | + #### SimpleConversation | Name | Type | Description | Required | @@ -19458,7 +19616,7 @@ Query parameters for listing snippet published workflows. | ---- | ---- | ----------- | -------- | | conversation_variables | [ [WorkflowConversationVariableResponse](#workflowconversationvariableresponse) ] | | Yes | | created_at | integer | | Yes | -| created_by | [SimpleAccount](#simpleaccount) | | No | +| created_by | [SimpleAccountResponse](#simpleaccountresponse) | | No | | environment_variables | [ [WorkflowEnvironmentVariableResponse](#workflowenvironmentvariableresponse) ] | | Yes | | features | object | | Yes | | graph | object | | Yes | @@ -19470,7 +19628,7 @@ Query parameters for listing snippet published workflows. | rag_pipeline_variables | [ [PipelineVariableResponse](#pipelinevariableresponse) ] | | Yes | | tool_published | boolean | | Yes | | updated_at | integer | | Yes | -| updated_by | [SimpleAccount](#simpleaccount) | | No | +| updated_by | [SimpleAccountResponse](#simpleaccountresponse) | | No | | version | string | | Yes | #### StarredAppListQuery @@ -19579,9 +19737,9 @@ Default configuration for form inputs. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | -| hash | string | | No | -| result | string | | No | -| updated_at | string | | No | +| hash | string | | Yes | +| result | string | | Yes | +| updated_at | integer | | Yes | #### SystemConfigurationResponse @@ -19796,6 +19954,12 @@ Tag type | ---- | ---- | ----------- | -------- | | data | [ [TokensPerSecondStatisticItem](#tokenspersecondstatisticitem) ] | | Yes | +#### ToolLabelEnum + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| ToolLabelEnum | string | | | + #### ToolOAuthClientSchemaResponse | Name | Type | Description | Required | @@ -19841,6 +20005,12 @@ Enum class for tool provider | ---- | ---- | ----------- | -------- | | ToolProviderType | string | Enum class for tool provider | | +#### ToolSelectorScope + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| ToolSelectorScope | string | | | + #### TraceAppConfigResponse | Name | Type | Description | Required | @@ -20077,6 +20247,20 @@ Enum class for tool provider | updated_at | long | | No | | updated_by | string | | No | +#### TriggerDebugErrorResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| error | string | | No | +| status | string | | Yes | + +#### TriggerDebugWaitingResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| retry_in | integer | | Yes | +| status | string | | Yes | + #### TriggerOAuthAuthorizeResponse | Name | Type | Description | Required | @@ -20390,7 +20574,7 @@ How a workflow node is bound to an Agent. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | created_by_end_user | [SimpleEndUser](#simpleenduser) | | No | | created_by_role | string | | No | | created_from | string | | No | @@ -20427,7 +20611,7 @@ How a workflow node is bound to an Agent. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | created_by_end_user | [SimpleEndUser](#simpleenduser) | | No | | id | string | | Yes | | trigger_metadata | | | No | @@ -20528,7 +20712,7 @@ How a workflow node is bound to an Agent. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | -| users | [ [AccountWithRole](#accountwithrole) ] | | Yes | +| users | [ [AccountWithRoleResponse](#accountwithroleresponse) ] | | Yes | #### WorkflowCommentReply @@ -20643,46 +20827,35 @@ How a workflow node is bound to an Agent. | ---- | ---- | ----------- | -------- | | data | [ [WorkflowDailyTokenCostStatisticItem](#workflowdailytokencoststatisticitem) ] | | Yes | -#### WorkflowDraftEnvVariable +#### WorkflowDraftEnvironmentVariableListResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| items | [ [WorkflowDraftEnvironmentVariableResponse](#workflowdraftenvironmentvariableresponse) ] | | Yes | + +#### WorkflowDraftEnvironmentVariableResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | description | string | | No | -| edited | boolean | | No | -| id | string | | No | -| name | string | | No | -| selector | [ string ] | | No | -| type | string | | No | -| value_type | string | | No | -| visible | boolean | | No | +| editable | boolean | | Yes | +| edited | boolean | | Yes | +| id | string | | Yes | +| name | string | | Yes | +| selector | [ string ] | | Yes | +| type | string | | Yes | +| value | | | Yes | +| value_type | string | | Yes | +| visible | boolean | | Yes | -#### WorkflowDraftEnvVariableList +#### WorkflowDraftVariableFullContentResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | -| items | [ [WorkflowDraftEnvVariable](#workflowdraftenvvariable) ] | | No | - -#### WorkflowDraftVariable - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| description | string | | No | -| edited | boolean | | No | -| full_content | object | | No | -| id | string | | No | -| is_truncated | boolean | | No | -| name | string | | No | -| selector | [ string ] | | No | -| type | string | | No | -| value | string
integer
number
boolean
object
[ object ] | | No | -| value_type | string | | No | -| visible | boolean | | No | - -#### WorkflowDraftVariableList - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| items | [ [WorkflowDraftVariable](#workflowdraftvariable) ] | | No | +| download_url | string | | Yes | +| length | integer | | Yes | +| size_bytes | integer | | Yes | +| value_type | string | | Yes | #### WorkflowDraftVariableListQuery @@ -20691,20 +20864,42 @@ How a workflow node is bound to an Agent. | limit | integer,
**Default:** 20 | Items per page | No | | page | integer,
**Default:** 1 | Page number | No | -#### WorkflowDraftVariableListWithoutValue +#### WorkflowDraftVariableListResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | -| items | [ [WorkflowDraftVariableWithoutValue](#workflowdraftvariablewithoutvalue) ] | | No | -| total | integer | | No | +| items | [ [WorkflowDraftVariableResponse](#workflowdraftvariableresponse) ] | | Yes | + +#### WorkflowDraftVariableListWithoutValueResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| items | [ [WorkflowDraftVariableWithoutValueResponse](#workflowdraftvariablewithoutvalueresponse) ] | | Yes | +| total | integer | | Yes | #### WorkflowDraftVariablePatchPayload | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | -| name | string | | No | +| name | string | Variable name | No | | value | | | No | +#### WorkflowDraftVariableResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| description | string | | Yes | +| edited | boolean | | Yes | +| full_content | [WorkflowDraftVariableFullContentResponse](#workflowdraftvariablefullcontentresponse) | | Yes | +| id | string | | Yes | +| is_truncated | boolean | | Yes | +| name | string | | Yes | +| selector | [ string ] | | Yes | +| type | string | | Yes | +| value | | | Yes | +| value_type | string | | Yes | +| visible | boolean | | Yes | + #### WorkflowDraftVariableUpdatePayload | Name | Type | Description | Required | @@ -20712,19 +20907,19 @@ How a workflow node is bound to an Agent. | name | string | Variable name | No | | value | | Variable value | No | -#### WorkflowDraftVariableWithoutValue +#### WorkflowDraftVariableWithoutValueResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | -| description | string | | No | -| edited | boolean | | No | -| id | string | | No | -| is_truncated | boolean | | No | -| name | string | | No | -| selector | [ string ] | | No | -| type | string | | No | -| value_type | string | | No | -| visible | boolean | | No | +| description | string | | Yes | +| edited | boolean | | Yes | +| id | string | | Yes | +| is_truncated | boolean | | Yes | +| name | string | | Yes | +| selector | [ string ] | | Yes | +| type | string | | Yes | +| value_type | string | | Yes | +| visible | boolean | | Yes | #### WorkflowEnvironmentVariableResponse @@ -20864,20 +21059,13 @@ can reuse its existing handler. | variable | string | | No | | variable_selector | [ string
integer
number
boolean ] | | No | -#### WorkflowPublishResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| created_at | integer | | Yes | -| result | string | | Yes | - #### WorkflowResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | conversation_variables | [ [WorkflowConversationVariableResponse](#workflowconversationvariableresponse) ] | | Yes | | created_at | integer | | Yes | -| created_by | [SimpleAccount](#simpleaccount) | | No | +| created_by | [SimpleAccountResponse](#simpleaccountresponse) | | No | | environment_variables | [ [WorkflowEnvironmentVariableResponse](#workflowenvironmentvariableresponse) ] | | Yes | | features | object | | Yes | | graph | object | | Yes | @@ -20888,17 +21076,9 @@ can reuse its existing handler. | rag_pipeline_variables | [ [PipelineVariableResponse](#pipelinevariableresponse) ] | | Yes | | tool_published | boolean | | Yes | | updated_at | integer | | Yes | -| updated_by | [SimpleAccount](#simpleaccount) | | No | +| updated_by | [SimpleAccountResponse](#simpleaccountresponse) | | No | | version | string | | Yes | -#### WorkflowRestoreResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| hash | string | | Yes | -| result | string | | Yes | -| updated_at | integer | | Yes | - #### WorkflowRunCountQuery | Name | Type | Description | Required | @@ -20923,7 +21103,7 @@ can reuse its existing handler. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | created_by_end_user | [SimpleEndUser](#simpleenduser) | | No | | created_by_role | string | | No | | elapsed_time | number | | No | @@ -20962,7 +21142,7 @@ can reuse its existing handler. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | elapsed_time | number | | No | | exceptions_count | integer | | No | | finished_at | integer | | No | @@ -21009,7 +21189,7 @@ can reuse its existing handler. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | created_by_end_user | [SimpleEndUser](#simpleenduser) | | No | | created_by_role | string | | No | | elapsed_time | number | | No | @@ -21311,6 +21491,18 @@ Workflow tool configuration | data | [ [RBACRole](#rbacrole) ] | | No | | pagination | [Pagination](#pagination) | | No | +#### core__entities__provider_entities__BasicProviderConfig__Type + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| core__entities__provider_entities__BasicProviderConfig__Type | string | | | + +#### core__plugin__entities__parameters__PluginParameterAutoGenerate__Type + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| core__plugin__entities__parameters__PluginParameterAutoGenerate__Type | string | | | + #### core__tools__entities__common_entities__I18nObject Model class for i18n object. diff --git a/api/openapi/markdown/service-openapi.md b/api/openapi/markdown/service-openapi.md index 8fc5e75e3cf..5c30aefead1 100644 --- a/api/openapi/markdown/service-openapi.md +++ b/api/openapi/markdown/service-openapi.md @@ -3937,7 +3937,7 @@ Model class for provider with models response. | output_variable_name | string | | Yes | | type | string | | No | -#### SimpleAccount +#### SimpleAccountResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | @@ -4147,7 +4147,7 @@ in form definiton, or a variable while the workflow is running. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | created_by_end_user | [SimpleEndUser](#simpleenduser) | | No | | created_by_role | string | | No | | created_from | string | | No | diff --git a/api/services/workflow_service.py b/api/services/workflow_service.py index 262ccc18f83..d5642ccc969 100644 --- a/api/services/workflow_service.py +++ b/api/services/workflow_service.py @@ -1004,6 +1004,9 @@ class WorkflowService: """ Build a human input form preview for a draft workflow. + Preview responses are non-actionable: they mirror the live pause payload + shape without creating a persisted form, recipient token, or expiration. + Args: app_model: Target application model. account: Current account. diff --git a/api/tests/unit_tests/controllers/console/app/test_workflow_draft_variable.py b/api/tests/unit_tests/controllers/console/app/test_workflow_draft_variable.py new file mode 100644 index 00000000000..903fb6df631 --- /dev/null +++ b/api/tests/unit_tests/controllers/console/app/test_workflow_draft_variable.py @@ -0,0 +1,767 @@ +import uuid +from contextlib import nullcontext +from inspect import unwrap +from types import SimpleNamespace +from typing import Any, NamedTuple +from unittest.mock import Mock, patch + +import pytest +from flask import Flask + +from controllers.console.app import workflow_draft_variable as draft_variable_module +from controllers.console.app.workflow_draft_variable import ( + EnvironmentVariableCollectionApi, + NodeVariableCollectionApi, + VariableApi, + WorkflowDraftVariableFullContentResponse, + WorkflowDraftVariableListResponse, + WorkflowDraftVariableListWithoutValueResponse, + WorkflowDraftVariableResponse, + WorkflowDraftVariableWithoutValueResponse, + WorkflowVariableCollectionApi, +) +from core.workflow.variable_prefixes import CONVERSATION_VARIABLE_NODE_ID, SYSTEM_VARIABLE_NODE_ID +from factories.variable_factory import build_segment +from graphon.variables.types import SegmentType +from libs.datetime_utils import naive_utc_now +from libs.uuid_utils import uuidv7 +from models import Account, App, AppMode +from models.workflow import WorkflowDraftVariable, WorkflowDraftVariableFile +from services.workflow_draft_variable_service import WorkflowDraftVariableList + +_TEST_APP_ID = "test_app_id" +_TEST_NODE_EXEC_ID = str(uuid.uuid4()) + + +def _app_model() -> App: + app_model = App() + app_model.id = _TEST_APP_ID + app_model.tenant_id = "tenant-1" + app_model.name = "test app" + app_model.mode = AppMode.WORKFLOW + return app_model + + +def _current_user() -> Account: + account = Account(name="Test User", email="user@example.com") + account.id = "user-1" + return account + + +def _node_variable(*, value: Any = "value") -> WorkflowDraftVariable: + variable = WorkflowDraftVariable.new_node_variable( + app_id=_TEST_APP_ID, + user_id="user-1", + node_id="node-1", + name="node_var", + value=build_segment(value), + node_execution_id=_TEST_NODE_EXEC_ID, + ) + variable.id = str(uuid.uuid4()) + return variable + + +def _assert_raw_payload_matches_model(payload: dict[str, Any], model: type[Any], expected: dict[str, Any]) -> None: + assert payload == expected + assert model.model_validate(payload).model_dump(mode="json") == expected + + +def test_workflow_draft_variable_update_payload_keeps_value_as_json_until_variable_type_is_known() -> None: + payload = draft_variable_module.WorkflowDraftVariableUpdatePayload.model_validate( + {"value": {"transfer_method": "ordinary-object-field", "nested": {"enabled": True}}} + ) + + assert payload.value == {"transfer_method": "ordinary-object-field", "nested": {"enabled": True}} + + +def test_workflow_variable_collection_get_returns_without_value_contract( + app: Flask, monkeypatch: pytest.MonkeyPatch +) -> None: + variable = _node_variable() + captured_args: dict[str, Any] = {} + + class WorkflowService: + def is_workflow_exist(self, *, app_model: Any) -> bool: + captured_args["workflow_app_id"] = app_model.id + return True + + class DraftVariableService: + def __init__(self, *, session: object) -> None: + captured_args["session"] = session + + def list_variables_without_values(self, **kwargs: Any) -> WorkflowDraftVariableList: + captured_args.update(kwargs) + return WorkflowDraftVariableList(variables=[variable], total=None) + + session = object() + monkeypatch.setattr(draft_variable_module, "WorkflowService", WorkflowService) + monkeypatch.setattr(draft_variable_module, "WorkflowDraftVariableService", DraftVariableService) + monkeypatch.setattr(draft_variable_module, "db", SimpleNamespace(engine=object())) + monkeypatch.setattr( + draft_variable_module, + "sessionmaker", + lambda *_args, **_kwargs: SimpleNamespace(begin=lambda: nullcontext(session)), + ) + + api = WorkflowVariableCollectionApi() + handler = unwrap(api.get) + + with app.test_request_context("/apps/app-1/workflows/draft/variables?page=2&limit=3", method="GET"): + payload = handler(api, _current_user(), _app_model()) + + expected_payload = { + "items": [ + { + "id": variable.id, + "type": "node", + "name": "node_var", + "description": "", + "selector": ["node-1", "node_var"], + "value_type": "string", + "edited": False, + "visible": True, + "is_truncated": False, + } + ], + "total": None, + } + + assert captured_args == { + "workflow_app_id": _TEST_APP_ID, + "session": session, + "app_id": _TEST_APP_ID, + "page": 2, + "limit": 3, + "user_id": "user-1", + } + _assert_raw_payload_matches_model(payload, WorkflowDraftVariableListWithoutValueResponse, expected_payload) + + +def test_node_variable_collection_get_returns_value_contract(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: + variable = _node_variable(value=None) + + class DraftVariableService: + def __init__(self, *, session: object) -> None: + pass + + def list_node_variables(self, app_id: str, node_id: str, *, user_id: str) -> WorkflowDraftVariableList: + assert (app_id, node_id, user_id) == (_TEST_APP_ID, "node-1", "user-1") + return WorkflowDraftVariableList(variables=[variable]) + + monkeypatch.setattr(draft_variable_module, "WorkflowDraftVariableService", DraftVariableService) + monkeypatch.setattr(draft_variable_module, "db", SimpleNamespace(engine=object())) + monkeypatch.setattr( + draft_variable_module, + "sessionmaker", + lambda *_args, **_kwargs: SimpleNamespace(begin=lambda: nullcontext(object())), + ) + + api = NodeVariableCollectionApi() + handler = unwrap(api.get) + + with app.test_request_context("/apps/app-1/workflows/draft/nodes/node-1/variables", method="GET"): + payload = handler(api, _current_user(), _app_model(), "node-1") + + expected_payload = { + "items": [ + { + "id": variable.id, + "type": "node", + "name": "node_var", + "description": "", + "selector": ["node-1", "node_var"], + "value_type": "none", + "edited": False, + "visible": True, + "is_truncated": False, + "value": None, + "full_content": None, + } + ] + } + _assert_raw_payload_matches_model(payload, WorkflowDraftVariableListResponse, expected_payload) + + +def test_variable_patch_noop_returns_current_variable_contract(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: + variable = _node_variable(value=42) + update_variable_mock = Mock() + + class DraftVariableService: + def __init__(self, session: object) -> None: + pass + + def get_variable(self, *, variable_id: str) -> WorkflowDraftVariable: + assert variable_id == variable.id + return variable + + def update_variable(self, *args: Any, **kwargs: Any) -> None: + update_variable_mock(*args, **kwargs) + + monkeypatch.setattr(draft_variable_module, "WorkflowDraftVariableService", DraftVariableService) + session = Mock(return_value=object()) + session.commit = Mock() + monkeypatch.setattr(draft_variable_module, "db", SimpleNamespace(session=session)) + + api = VariableApi() + handler = unwrap(api.patch) + + with app.test_request_context(f"/apps/app-1/workflows/draft/variables/{variable.id}", method="PATCH", json={}): + payload = handler(api, _current_user(), _app_model(), uuid.UUID(variable.id)) + + expected_payload = { + "id": variable.id, + "type": "node", + "name": "node_var", + "description": "", + "selector": ["node-1", "node_var"], + "value_type": "number", + "edited": False, + "visible": True, + "is_truncated": False, + "value": 42, + "full_content": None, + } + + update_variable_mock.assert_not_called() + _assert_raw_payload_matches_model(payload, WorkflowDraftVariableResponse, expected_payload) + + +def test_variable_patch_file_value_forwards_raw_mapping_to_file_factory( + app: Flask, monkeypatch: pytest.MonkeyPatch +) -> None: + variable = _node_variable(value="old") + variable.value_type = SegmentType.FILE + raw_mapping = { + "transfer_method": "local_file", + "upload_file_id": "file-1", + "filename": "kept-for-file-factory", + } + built_file = object() + captured: dict[str, Any] = {} + + def build_from_mapping(**kwargs: Any) -> object: + captured.update(kwargs) + return built_file + + def build_segment_with_type(segment_type: SegmentType, value: object): + assert segment_type == SegmentType.FILE + assert value is built_file + return build_segment("updated") + + class DraftVariableService: + def __init__(self, session: object) -> None: + pass + + def get_variable(self, *, variable_id: str) -> WorkflowDraftVariable: + assert variable_id == variable.id + return variable + + def update_variable(self, target: WorkflowDraftVariable, *, name: str | None, value: Any) -> None: + assert target is variable + assert name is None + target.set_value(value) + + monkeypatch.setattr(draft_variable_module, "WorkflowDraftVariableService", DraftVariableService) + monkeypatch.setattr(draft_variable_module, "build_from_mapping", build_from_mapping) + monkeypatch.setattr(draft_variable_module, "build_segment_with_type", build_segment_with_type) + session = Mock(return_value=object()) + session.commit = Mock() + monkeypatch.setattr(draft_variable_module, "db", SimpleNamespace(session=session)) + + api = VariableApi() + handler = unwrap(api.patch) + + with app.test_request_context( + f"/apps/app-1/workflows/draft/variables/{variable.id}", + method="PATCH", + json={"value": raw_mapping}, + ): + payload = handler(api, _current_user(), _app_model(), uuid.UUID(variable.id)) + + assert captured["tenant_id"] == "tenant-1" + assert captured["mapping"] == raw_mapping + expected_payload = { + "id": variable.id, + "type": "node", + "name": "node_var", + "description": "", + "selector": ["node-1", "node_var"], + "value_type": "string", + "edited": False, + "visible": True, + "is_truncated": False, + "value": "updated", + "full_content": None, + } + _assert_raw_payload_matches_model(payload, WorkflowDraftVariableResponse, expected_payload) + + +def test_environment_variable_collection_get_returns_response_model_contract( + app: Flask, monkeypatch: pytest.MonkeyPatch +) -> None: + env_var = SimpleNamespace( + id="env-1", + name="API_KEY", + description="secret token", + selector=["env", "API_KEY"], + value_type=SegmentType.SECRET, + value="token", + ) + + class WorkflowService: + def get_draft_workflow(self, *, app_model: Any) -> SimpleNamespace: + assert app_model.id == _TEST_APP_ID + return SimpleNamespace(environment_variables=[env_var]) + + monkeypatch.setattr(draft_variable_module, "WorkflowService", WorkflowService) + + api = EnvironmentVariableCollectionApi() + handler = unwrap(api.get) + + with app.test_request_context("/apps/app-1/workflows/draft/environment-variables", method="GET"): + payload = handler(api, _current_user(), _app_model()) + + expected_payload = { + "items": [ + { + "id": "env-1", + "type": "env", + "name": "API_KEY", + "description": "secret token", + "selector": ["env", "API_KEY"], + "value_type": "secret", + "value": "token", + "edited": False, + "visible": True, + "editable": True, + } + ] + } + _assert_raw_payload_matches_model( + payload, + draft_variable_module.WorkflowDraftEnvironmentVariableListResponse, + expected_payload, + ) + + +class TestWorkflowDraftVariableFields: + def test_full_content_response_constructor(self): + """Test that full_content serialization uses pre-loaded relationships.""" + # Create mock objects with relationships pre-loaded + mock_variable = WorkflowDraftVariable( + file_id="test-file-id", + variable_file=WorkflowDraftVariableFile( + size=100000, + length=50, + value_type=SegmentType.OBJECT, + upload_file_id="test-upload-file-id", + tenant_id=str(uuid.uuid4()), + app_id=str(uuid.uuid4()), + user_id=str(uuid.uuid4()), + ), + ) + + # Mock the file helpers + with patch("controllers.console.app.workflow_draft_variable.file_helpers", autospec=True) as mock_file_helpers: + mock_file_helpers.get_signed_file_url.return_value = "http://example.com/signed-url" + + # Call the function + result = WorkflowDraftVariableFullContentResponse.from_workflow_draft_variable(mock_variable) + + # Verify it returns the expected structure + assert result is not None + assert result.size_bytes == 100000 + assert result.length == 50 + assert result.value_type == "object" + assert result.download_url == "http://example.com/signed-url" + + # Verify it used the pre-loaded relationships (no database queries) + mock_file_helpers.get_signed_file_url.assert_called_once_with("test-upload-file-id", as_attachment=True) + + def test_full_content_response_constructor_handles_none_cases(self): + """Test that full_content serialization handles None cases properly.""" + + # Test with no file_id + draft_var = WorkflowDraftVariable() + draft_var.file_id = None + result = WorkflowDraftVariableFullContentResponse.from_workflow_draft_variable(draft_var) + assert result is None + + def test_full_content_response_constructor_preserves_none_size(self): + draft_var = WorkflowDraftVariable( + file_id="test-file-id", + variable_file=WorkflowDraftVariableFile( + size=None, + length=50, + value_type=SegmentType.OBJECT, + upload_file_id="test-upload-file-id", + tenant_id=str(uuid.uuid4()), + app_id=str(uuid.uuid4()), + user_id=str(uuid.uuid4()), + ), + ) + + with patch("controllers.console.app.workflow_draft_variable.file_helpers", autospec=True) as mock_file_helpers: + mock_file_helpers.get_signed_file_url.return_value = "http://example.com/signed-url" + + result = WorkflowDraftVariableFullContentResponse.from_workflow_draft_variable(draft_var) + + assert result is not None + assert result.size_bytes is None + + def test_full_content_response_constructor_should_raises_when_file_id_exists_but_file_is_none(self): + # Test with no file_id + draft_var = WorkflowDraftVariable() + draft_var.file_id = str(uuid.uuid4()) + draft_var.variable_file = None + with pytest.raises(AssertionError): + result = WorkflowDraftVariableFullContentResponse.from_workflow_draft_variable(draft_var) + + def test_conversation_variable(self): + conv_var = WorkflowDraftVariable.new_conversation_variable( + app_id=_TEST_APP_ID, name="conv_var", value=build_segment(1) + ) + + conv_var.id = str(uuid.uuid4()) + conv_var.visible = True + + expected_without_value: dict[str, Any] = { + "id": conv_var.id, + "type": conv_var.get_variable_type().value, + "name": "conv_var", + "description": "", + "selector": [CONVERSATION_VARIABLE_NODE_ID, "conv_var"], + "value_type": "number", + "edited": False, + "visible": True, + "is_truncated": False, + } + + assert ( + WorkflowDraftVariableWithoutValueResponse.from_workflow_draft_variable(conv_var).model_dump(mode="json") + == expected_without_value + ) + expected_with_value = expected_without_value.copy() + expected_with_value["value"] = 1 + expected_with_value["full_content"] = None + assert ( + WorkflowDraftVariableResponse.from_workflow_draft_variable(conv_var).model_dump(mode="json") + == expected_with_value + ) + + def test_create_sys_variable(self): + sys_var = WorkflowDraftVariable.new_sys_variable( + app_id=_TEST_APP_ID, + name="sys_var", + value=build_segment("a"), + editable=True, + node_execution_id=_TEST_NODE_EXEC_ID, + ) + + sys_var.id = str(uuid.uuid4()) + sys_var.last_edited_at = naive_utc_now() + sys_var.visible = True + + expected_without_value: dict[str, Any] = { + "id": sys_var.id, + "type": sys_var.get_variable_type().value, + "name": "sys_var", + "description": "", + "selector": [SYSTEM_VARIABLE_NODE_ID, "sys_var"], + "value_type": "string", + "edited": True, + "visible": True, + "is_truncated": False, + } + assert ( + WorkflowDraftVariableWithoutValueResponse.from_workflow_draft_variable(sys_var).model_dump(mode="json") + == expected_without_value + ) + expected_with_value = expected_without_value.copy() + expected_with_value["value"] = "a" + expected_with_value["full_content"] = None + assert ( + WorkflowDraftVariableResponse.from_workflow_draft_variable(sys_var).model_dump(mode="json") + == expected_with_value + ) + + def test_node_variable(self): + node_var = WorkflowDraftVariable.new_node_variable( + app_id=_TEST_APP_ID, + node_id="test_node", + name="node_var", + value=build_segment([1, "a"]), + visible=False, + node_execution_id=_TEST_NODE_EXEC_ID, + ) + + node_var.id = str(uuid.uuid4()) + node_var.last_edited_at = naive_utc_now() + + expected_without_value: dict[str, Any] = { + "id": node_var.id, + "type": node_var.get_variable_type().value, + "name": "node_var", + "description": "", + "selector": ["test_node", "node_var"], + "value_type": "array[any]", + "edited": True, + "visible": False, + "is_truncated": False, + } + + assert ( + WorkflowDraftVariableWithoutValueResponse.from_workflow_draft_variable(node_var).model_dump(mode="json") + == expected_without_value + ) + expected_with_value = expected_without_value.copy() + expected_with_value["value"] = [1, "a"] + expected_with_value["full_content"] = None + assert ( + WorkflowDraftVariableResponse.from_workflow_draft_variable(node_var).model_dump(mode="json") + == expected_with_value + ) + + def test_node_variable_with_file(self): + node_var = WorkflowDraftVariable.new_node_variable( + app_id=_TEST_APP_ID, + node_id="test_node", + name="node_var", + value=build_segment([1, "a"]), + visible=False, + node_execution_id=_TEST_NODE_EXEC_ID, + ) + + node_var.id = str(uuid.uuid4()) + node_var.last_edited_at = naive_utc_now() + variable_file = WorkflowDraftVariableFile( + upload_file_id=str(uuid.uuid4()), + size=1024, + length=10, + value_type=SegmentType.ARRAY_STRING, + tenant_id=str(uuidv7()), + app_id=str(uuidv7()), + user_id=str(uuidv7()), + ) + variable_file.id = str(uuidv7()) + node_var.variable_file = variable_file + node_var.file_id = variable_file.id + + expected_without_value: dict[str, Any] = { + "id": node_var.id, + "type": node_var.get_variable_type().value, + "name": "node_var", + "description": "", + "selector": ["test_node", "node_var"], + "value_type": "array[any]", + "edited": True, + "visible": False, + "is_truncated": True, + } + + with patch("controllers.console.app.workflow_draft_variable.file_helpers", autospec=True) as mock_file_helpers: + mock_file_helpers.get_signed_file_url.return_value = "http://example.com/signed-url" + assert ( + WorkflowDraftVariableWithoutValueResponse.from_workflow_draft_variable(node_var).model_dump(mode="json") + == expected_without_value + ) + expected_with_value = expected_without_value.copy() + expected_with_value["value"] = [1, "a"] + expected_with_value["full_content"] = { + "size_bytes": 1024, + "value_type": "array[string]", + "length": 10, + "download_url": "http://example.com/signed-url", + } + assert ( + WorkflowDraftVariableResponse.from_workflow_draft_variable(node_var).model_dump(mode="json") + == expected_with_value + ) + + +class TestWorkflowDraftVariableList: + def test_workflow_draft_variable_list(self): + class TestCase(NamedTuple): + name: str + var_list: WorkflowDraftVariableList + expected: dict + + node_var = WorkflowDraftVariable.new_node_variable( + app_id=_TEST_APP_ID, + node_id="test_node", + name="test_var", + value=build_segment("a"), + visible=True, + node_execution_id=_TEST_NODE_EXEC_ID, + ) + node_var.id = str(uuid.uuid4()) + node_var_dict = { + "id": node_var.id, + "type": node_var.get_variable_type().value, + "name": "test_var", + "description": "", + "selector": ["test_node", "test_var"], + "value_type": "string", + "edited": False, + "visible": True, + "is_truncated": False, + } + + cases = [ + TestCase( + name="empty variable list", + var_list=WorkflowDraftVariableList(variables=[]), + expected={ + "items": [], + "total": None, + }, + ), + TestCase( + name="empty variable list with total", + var_list=WorkflowDraftVariableList(variables=[], total=10), + expected={ + "items": [], + "total": 10, + }, + ), + TestCase( + name="non-empty variable list", + var_list=WorkflowDraftVariableList(variables=[node_var], total=None), + expected={ + "items": [node_var_dict], + "total": None, + }, + ), + TestCase( + name="non-empty variable list with total", + var_list=WorkflowDraftVariableList(variables=[node_var], total=10), + expected={ + "items": [node_var_dict], + "total": 10, + }, + ), + ] + + for idx, case in enumerate(cases, 1): + assert ( + WorkflowDraftVariableListWithoutValueResponse.from_workflow_draft_variable_list( + case.var_list + ).model_dump(mode="json") + == case.expected + ), f"Test case {idx} failed, {case.name=}" + + +def test_workflow_node_variables_fields(): + conv_var = WorkflowDraftVariable.new_conversation_variable( + app_id=_TEST_APP_ID, name="conv_var", value=build_segment(1) + ) + conv_var.visible = True + resp = WorkflowDraftVariableListResponse.from_workflow_draft_variable_list( + WorkflowDraftVariableList(variables=[conv_var]) + ).model_dump(mode="json") + assert isinstance(resp, dict) + assert len(resp["items"]) == 1 + item_dict = resp["items"][0] + assert item_dict["name"] == "conv_var" + assert item_dict["value"] == 1 + + +def test_workflow_file_variable_with_signed_url(): + """Test that File type variables include signed URLs in API responses.""" + from graphon.file import File, FileTransferMethod, FileType + + # Create a File object with LOCAL_FILE transfer method (which generates signed URLs) + test_file = File( + file_id="test_file_id", + file_type=FileType.IMAGE, + transfer_method=FileTransferMethod.LOCAL_FILE, + related_id="test_upload_file_id", + filename="test.jpg", + extension=".jpg", + mime_type="image/jpeg", + size=12345, + ) + + # Create a WorkflowDraftVariable with the File + file_var = WorkflowDraftVariable.new_node_variable( + app_id=_TEST_APP_ID, + node_id="test_node", + name="file_var", + value=build_segment(test_file), + node_execution_id=_TEST_NODE_EXEC_ID, + ) + + resp = WorkflowDraftVariableListResponse.from_workflow_draft_variable_list( + WorkflowDraftVariableList(variables=[file_var]) + ).model_dump(mode="json") + + # Verify the response structure + assert isinstance(resp, dict) + assert len(resp["items"]) == 1 + item_dict = resp["items"][0] + assert item_dict["name"] == "file_var" + + # Verify the value is a dict (File.to_dict() result) and contains expected fields + value = item_dict["value"] + assert isinstance(value, dict) + + # Verify the File fields are preserved + assert value["id"] == test_file.id + assert value["filename"] == test_file.filename + assert value["type"] == test_file.type.value + assert value["transfer_method"] == test_file.transfer_method.value + assert value["size"] == test_file.size + + # Verify the URL is present (it should be a signed URL for LOCAL_FILE transfer method) + remote_url = value["remote_url"] + assert remote_url is not None + + assert isinstance(remote_url, str) + # For LOCAL_FILE, the URL should contain signature parameters + assert "timestamp=" in remote_url + assert "nonce=" in remote_url + assert "sign=" in remote_url + + +def test_workflow_file_variable_remote_url(): + """Test that File type variables with REMOTE_URL transfer method return the remote URL.""" + from graphon.file import File, FileTransferMethod, FileType + + # Create a File object with REMOTE_URL transfer method + test_file = File( + file_id="test_file_id", + file_type=FileType.IMAGE, + transfer_method=FileTransferMethod.REMOTE_URL, + remote_url="https://example.com/test.jpg", + filename="test.jpg", + extension=".jpg", + mime_type="image/jpeg", + size=12345, + ) + + # Create a WorkflowDraftVariable with the File + file_var = WorkflowDraftVariable.new_node_variable( + app_id=_TEST_APP_ID, + node_id="test_node", + name="file_var", + value=build_segment(test_file), + node_execution_id=_TEST_NODE_EXEC_ID, + ) + + resp = WorkflowDraftVariableListResponse.from_workflow_draft_variable_list( + WorkflowDraftVariableList(variables=[file_var]) + ).model_dump(mode="json") + + # Verify the response structure + assert isinstance(resp, dict) + assert len(resp["items"]) == 1 + item_dict = resp["items"][0] + assert item_dict["name"] == "file_var" + + # Verify the value is a dict (File.to_dict() result) and contains expected fields + value = item_dict["value"] + assert isinstance(value, dict) + remote_url = value["remote_url"] + + # For REMOTE_URL, the URL should be the original remote URL + assert remote_url == test_file.remote_url diff --git a/api/tests/unit_tests/controllers/console/app/test_workflow_human_input_debug_api.py b/api/tests/unit_tests/controllers/console/app/test_workflow_human_input_debug_api.py index f04ab6d6e7c..9c47f8e5a31 100644 --- a/api/tests/unit_tests/controllers/console/app/test_workflow_human_input_debug_api.py +++ b/api/tests/unit_tests/controllers/console/app/test_workflow_human_input_debug_api.py @@ -77,9 +77,11 @@ def test_human_input_preview_delegates_to_service( preview_payload = { "form_id": "node-42", + "node_id": "node-42", + "node_title": "Human Input", "form_content": "
example
", "inputs": [{"name": "topic"}], - "actions": [{"id": "continue"}], + "actions": [{"id": "continue", "title": "Continue"}], } service_instance = MagicMock() service_instance.get_human_input_form_preview.return_value = preview_payload @@ -88,7 +90,15 @@ def test_human_input_preview_delegates_to_service( with app.test_request_context(case.path, method="POST", json={"inputs": {"topic": "tech"}}): response = case.resource_cls().post(app_id=app_model.id, node_id="node-42") - assert response == preview_payload + assert response == { + **preview_payload, + "TYPE": "human_input_required", + "actions": [{"id": "continue", "title": "Continue", "button_style": "default"}], + "resolved_default_values": {}, + "display_in_ui": False, + "form_token": None, + "expiration_time": None, + } service_instance.get_human_input_form_preview.assert_called_once_with( app_model=app_model, account=account, diff --git a/api/tests/unit_tests/controllers/console/app/test_workflow_run_api.py b/api/tests/unit_tests/controllers/console/app/test_workflow_run_api.py index 71034ebd405..f029c4ff822 100644 --- a/api/tests/unit_tests/controllers/console/app/test_workflow_run_api.py +++ b/api/tests/unit_tests/controllers/console/app/test_workflow_run_api.py @@ -3,27 +3,14 @@ from __future__ import annotations from datetime import UTC, datetime from inspect import unwrap from types import SimpleNamespace -from typing import Any import pytest from flask import Flask -from flask_restx import marshal from controllers.console.app import workflow_run as workflow_run_module from models import Account -def _serialize_200_response(handler, payload: Any) -> Any: - response_doc = getattr(handler, "__apidoc__", {}).get("responses", {}).get("200") - if response_doc is None: - return payload - - response_model = response_doc[1] - if isinstance(response_model, dict): - return marshal(payload, response_model) - return payload - - def _account() -> SimpleNamespace: return SimpleNamespace(id="account-1", name="Alice", email="alice@example.com") @@ -100,11 +87,9 @@ def test_workflow_run_list_returns_frontend_history_contract(app: Flask, monkeyp with app.test_request_context("/apps/app-1/workflow-runs?limit=10", method="GET"): payload = handler(api, app_model=SimpleNamespace(id="app-1", tenant_id="tenant-1")) - response = _serialize_200_response(api.get, payload) - - assert response["limit"] == 10 - assert response["has_more"] is False - assert response["data"][0] == { + assert payload["limit"] == 10 + assert payload["has_more"] is False + assert payload["data"][0] == { "id": "run-1", "version": "v1", "status": "succeeded", @@ -141,10 +126,8 @@ def test_advanced_chat_workflow_run_list_keeps_message_fields(app: Flask, monkey with app.test_request_context("/apps/app-1/advanced-chat/workflow-runs?limit=1", method="GET"): payload = handler(api, app_model=SimpleNamespace(id="app-1", tenant_id="tenant-1")) - response = _serialize_200_response(api.get, payload) - - assert response["data"][0]["conversation_id"] == "conversation-1" - assert response["data"][0]["message_id"] == "message-1" + assert payload["data"][0]["conversation_id"] == "conversation-1" + assert payload["data"][0]["message_id"] == "message-1" def test_workflow_run_detail_returns_frontend_detail_contract(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: @@ -180,9 +163,7 @@ def test_workflow_run_detail_returns_frontend_detail_contract(app: Flask, monkey with app.test_request_context("/apps/app-1/workflow-runs/run-1", method="GET"): payload = handler(api, app_model=SimpleNamespace(id="app-1", tenant_id="tenant-1"), run_id="run-1") - response = _serialize_200_response(api.get, payload) - - assert response == { + assert payload == { "id": "run-1", "version": "v1", "graph": {"nodes": []}, @@ -219,9 +200,7 @@ def test_workflow_run_node_executions_return_frontend_trace_contract( api, _current_account(), app_model=SimpleNamespace(id="app-1", tenant_id="tenant-1"), run_id="run-1" ) - response = _serialize_200_response(api.get, payload) - - assert response == { + assert payload == { "data": [ { "id": "node-exec-1", diff --git a/api/tests/unit_tests/controllers/console/app/workflow_draft_variables_test.py b/api/tests/unit_tests/controllers/console/app/workflow_draft_variables_test.py deleted file mode 100644 index 62fa82e3397..00000000000 --- a/api/tests/unit_tests/controllers/console/app/workflow_draft_variables_test.py +++ /dev/null @@ -1,413 +0,0 @@ -import uuid -from collections import OrderedDict -from typing import Any, NamedTuple -from unittest.mock import patch - -import pytest -from flask_restx import marshal - -from controllers.console.app.workflow_draft_variable import ( - _WORKFLOW_DRAFT_VARIABLE_FIELDS, - _WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS, - _WORKFLOW_DRAFT_VARIABLE_LIST_WITHOUT_VALUE_FIELDS, - _WORKFLOW_DRAFT_VARIABLE_WITHOUT_VALUE_FIELDS, - _serialize_full_content, -) -from core.workflow.variable_prefixes import CONVERSATION_VARIABLE_NODE_ID, SYSTEM_VARIABLE_NODE_ID -from factories.variable_factory import build_segment -from graphon.variables.types import SegmentType -from libs.datetime_utils import naive_utc_now -from libs.uuid_utils import uuidv7 -from models.workflow import WorkflowDraftVariable, WorkflowDraftVariableFile -from services.workflow_draft_variable_service import WorkflowDraftVariableList - -_TEST_APP_ID = "test_app_id" -_TEST_NODE_EXEC_ID = str(uuid.uuid4()) - - -class TestWorkflowDraftVariableFields: - def test_serialize_full_content(self): - """Test that _serialize_full_content uses pre-loaded relationships.""" - # Create mock objects with relationships pre-loaded - mock_variable = WorkflowDraftVariable( - file_id="test-file-id", - variable_file=WorkflowDraftVariableFile( - size=100000, - length=50, - value_type=SegmentType.OBJECT, - upload_file_id="test-upload-file-id", - tenant_id=str(uuid.uuid4()), - app_id=str(uuid.uuid4()), - user_id=str(uuid.uuid4()), - ), - ) - - # Mock the file helpers - with patch("controllers.console.app.workflow_draft_variable.file_helpers", autospec=True) as mock_file_helpers: - mock_file_helpers.get_signed_file_url.return_value = "http://example.com/signed-url" - - # Call the function - result = _serialize_full_content(mock_variable) - - # Verify it returns the expected structure - assert result is not None - assert result["size_bytes"] == 100000 - assert result["length"] == 50 - assert result["value_type"] == "object" - assert "download_url" in result - assert result["download_url"] == "http://example.com/signed-url" - - # Verify it used the pre-loaded relationships (no database queries) - mock_file_helpers.get_signed_file_url.assert_called_once_with("test-upload-file-id", as_attachment=True) - - def test_serialize_full_content_handles_none_cases(self): - """Test that _serialize_full_content handles None cases properly.""" - - # Test with no file_id - draft_var = WorkflowDraftVariable() - draft_var.file_id = None - result = _serialize_full_content(draft_var) - assert result is None - - def test_serialize_full_content_should_raises_when_file_id_exists_but_file_is_none(self): - # Test with no file_id - draft_var = WorkflowDraftVariable() - draft_var.file_id = str(uuid.uuid4()) - draft_var.variable_file = None - with pytest.raises(AssertionError): - result = _serialize_full_content(draft_var) - - def test_conversation_variable(self): - conv_var = WorkflowDraftVariable.new_conversation_variable( - app_id=_TEST_APP_ID, name="conv_var", value=build_segment(1) - ) - - conv_var.id = str(uuid.uuid4()) - conv_var.visible = True - - expected_without_value: OrderedDict[str, Any] = OrderedDict( - { - "id": conv_var.id, - "type": conv_var.get_variable_type().value, - "name": "conv_var", - "description": "", - "selector": [CONVERSATION_VARIABLE_NODE_ID, "conv_var"], - "value_type": "number", - "edited": False, - "visible": True, - "is_truncated": False, - } - ) - - assert marshal(conv_var, _WORKFLOW_DRAFT_VARIABLE_WITHOUT_VALUE_FIELDS) == expected_without_value - expected_with_value = expected_without_value.copy() - expected_with_value["value"] = 1 - expected_with_value["full_content"] = None - assert marshal(conv_var, _WORKFLOW_DRAFT_VARIABLE_FIELDS) == expected_with_value - - def test_create_sys_variable(self): - sys_var = WorkflowDraftVariable.new_sys_variable( - app_id=_TEST_APP_ID, - name="sys_var", - value=build_segment("a"), - editable=True, - node_execution_id=_TEST_NODE_EXEC_ID, - ) - - sys_var.id = str(uuid.uuid4()) - sys_var.last_edited_at = naive_utc_now() - sys_var.visible = True - - expected_without_value = OrderedDict( - { - "id": sys_var.id, - "type": sys_var.get_variable_type().value, - "name": "sys_var", - "description": "", - "selector": [SYSTEM_VARIABLE_NODE_ID, "sys_var"], - "value_type": "string", - "edited": True, - "visible": True, - "is_truncated": False, - } - ) - assert marshal(sys_var, _WORKFLOW_DRAFT_VARIABLE_WITHOUT_VALUE_FIELDS) == expected_without_value - expected_with_value = expected_without_value.copy() - expected_with_value["value"] = "a" - expected_with_value["full_content"] = None - assert marshal(sys_var, _WORKFLOW_DRAFT_VARIABLE_FIELDS) == expected_with_value - - def test_node_variable(self): - node_var = WorkflowDraftVariable.new_node_variable( - app_id=_TEST_APP_ID, - node_id="test_node", - name="node_var", - value=build_segment([1, "a"]), - visible=False, - node_execution_id=_TEST_NODE_EXEC_ID, - ) - - node_var.id = str(uuid.uuid4()) - node_var.last_edited_at = naive_utc_now() - - expected_without_value: OrderedDict[str, Any] = OrderedDict( - { - "id": node_var.id, - "type": node_var.get_variable_type().value, - "name": "node_var", - "description": "", - "selector": ["test_node", "node_var"], - "value_type": "array[any]", - "edited": True, - "visible": False, - "is_truncated": False, - } - ) - - assert marshal(node_var, _WORKFLOW_DRAFT_VARIABLE_WITHOUT_VALUE_FIELDS) == expected_without_value - expected_with_value = expected_without_value.copy() - expected_with_value["value"] = [1, "a"] - expected_with_value["full_content"] = None - assert marshal(node_var, _WORKFLOW_DRAFT_VARIABLE_FIELDS) == expected_with_value - - def test_node_variable_with_file(self): - node_var = WorkflowDraftVariable.new_node_variable( - app_id=_TEST_APP_ID, - node_id="test_node", - name="node_var", - value=build_segment([1, "a"]), - visible=False, - node_execution_id=_TEST_NODE_EXEC_ID, - ) - - node_var.id = str(uuid.uuid4()) - node_var.last_edited_at = naive_utc_now() - variable_file = WorkflowDraftVariableFile( - upload_file_id=str(uuid.uuid4()), - size=1024, - length=10, - value_type=SegmentType.ARRAY_STRING, - tenant_id=str(uuidv7()), - app_id=str(uuidv7()), - user_id=str(uuidv7()), - ) - variable_file.id = str(uuidv7()) - node_var.variable_file = variable_file - node_var.file_id = variable_file.id - - expected_without_value: OrderedDict[str, Any] = OrderedDict( - { - "id": node_var.id, - "type": node_var.get_variable_type(), - "name": "node_var", - "description": "", - "selector": ["test_node", "node_var"], - "value_type": "array[any]", - "edited": True, - "visible": False, - "is_truncated": True, - } - ) - - with patch("controllers.console.app.workflow_draft_variable.file_helpers", autospec=True) as mock_file_helpers: - mock_file_helpers.get_signed_file_url.return_value = "http://example.com/signed-url" - assert marshal(node_var, _WORKFLOW_DRAFT_VARIABLE_WITHOUT_VALUE_FIELDS) == expected_without_value - expected_with_value = expected_without_value.copy() - expected_with_value["value"] = [1, "a"] - expected_with_value["full_content"] = { - "size_bytes": 1024, - "value_type": "array[string]", - "length": 10, - "download_url": "http://example.com/signed-url", - } - assert marshal(node_var, _WORKFLOW_DRAFT_VARIABLE_FIELDS) == expected_with_value - - -class TestWorkflowDraftVariableList: - def test_workflow_draft_variable_list(self): - class TestCase(NamedTuple): - name: str - var_list: WorkflowDraftVariableList - expected: dict - - node_var = WorkflowDraftVariable.new_node_variable( - app_id=_TEST_APP_ID, - node_id="test_node", - name="test_var", - value=build_segment("a"), - visible=True, - node_execution_id=_TEST_NODE_EXEC_ID, - ) - node_var.id = str(uuid.uuid4()) - node_var_dict = OrderedDict( - { - "id": node_var.id, - "type": node_var.get_variable_type().value, - "name": "test_var", - "description": "", - "selector": ["test_node", "test_var"], - "value_type": "string", - "edited": False, - "visible": True, - "is_truncated": False, - } - ) - - cases = [ - TestCase( - name="empty variable list", - var_list=WorkflowDraftVariableList(variables=[]), - expected=OrderedDict( - { - "items": [], - "total": None, - } - ), - ), - TestCase( - name="empty variable list with total", - var_list=WorkflowDraftVariableList(variables=[], total=10), - expected=OrderedDict( - { - "items": [], - "total": 10, - } - ), - ), - TestCase( - name="non-empty variable list", - var_list=WorkflowDraftVariableList(variables=[node_var], total=None), - expected=OrderedDict( - { - "items": [node_var_dict], - "total": None, - } - ), - ), - TestCase( - name="non-empty variable list with total", - var_list=WorkflowDraftVariableList(variables=[node_var], total=10), - expected=OrderedDict( - { - "items": [node_var_dict], - "total": 10, - } - ), - ), - ] - - for idx, case in enumerate(cases, 1): - assert marshal(case.var_list, _WORKFLOW_DRAFT_VARIABLE_LIST_WITHOUT_VALUE_FIELDS) == case.expected, ( - f"Test case {idx} failed, {case.name=}" - ) - - -def test_workflow_node_variables_fields(): - conv_var = WorkflowDraftVariable.new_conversation_variable( - app_id=_TEST_APP_ID, name="conv_var", value=build_segment(1) - ) - resp = marshal(WorkflowDraftVariableList(variables=[conv_var]), _WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS) - assert isinstance(resp, dict) - assert len(resp["items"]) == 1 - item_dict = resp["items"][0] - assert item_dict["name"] == "conv_var" - assert item_dict["value"] == 1 - - -def test_workflow_file_variable_with_signed_url(): - """Test that File type variables include signed URLs in API responses.""" - from graphon.file import File, FileTransferMethod, FileType - - # Create a File object with LOCAL_FILE transfer method (which generates signed URLs) - test_file = File( - file_id="test_file_id", - file_type=FileType.IMAGE, - transfer_method=FileTransferMethod.LOCAL_FILE, - related_id="test_upload_file_id", - filename="test.jpg", - extension=".jpg", - mime_type="image/jpeg", - size=12345, - ) - - # Create a WorkflowDraftVariable with the File - file_var = WorkflowDraftVariable.new_node_variable( - app_id=_TEST_APP_ID, - node_id="test_node", - name="file_var", - value=build_segment(test_file), - node_execution_id=_TEST_NODE_EXEC_ID, - ) - - # Marshal the variable using the API fields - resp = marshal(WorkflowDraftVariableList(variables=[file_var]), _WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS) - - # Verify the response structure - assert isinstance(resp, dict) - assert len(resp["items"]) == 1 - item_dict = resp["items"][0] - assert item_dict["name"] == "file_var" - - # Verify the value is a dict (File.to_dict() result) and contains expected fields - value = item_dict["value"] - assert isinstance(value, dict) - - # Verify the File fields are preserved - assert value["id"] == test_file.id - assert value["filename"] == test_file.filename - assert value["type"] == test_file.type.value - assert value["transfer_method"] == test_file.transfer_method.value - assert value["size"] == test_file.size - - # Verify the URL is present (it should be a signed URL for LOCAL_FILE transfer method) - remote_url = value["remote_url"] - assert remote_url is not None - - assert isinstance(remote_url, str) - # For LOCAL_FILE, the URL should contain signature parameters - assert "timestamp=" in remote_url - assert "nonce=" in remote_url - assert "sign=" in remote_url - - -def test_workflow_file_variable_remote_url(): - """Test that File type variables with REMOTE_URL transfer method return the remote URL.""" - from graphon.file import File, FileTransferMethod, FileType - - # Create a File object with REMOTE_URL transfer method - test_file = File( - file_id="test_file_id", - file_type=FileType.IMAGE, - transfer_method=FileTransferMethod.REMOTE_URL, - remote_url="https://example.com/test.jpg", - filename="test.jpg", - extension=".jpg", - mime_type="image/jpeg", - size=12345, - ) - - # Create a WorkflowDraftVariable with the File - file_var = WorkflowDraftVariable.new_node_variable( - app_id=_TEST_APP_ID, - node_id="test_node", - name="file_var", - value=build_segment(test_file), - node_execution_id=_TEST_NODE_EXEC_ID, - ) - - # Marshal the variable using the API fields - resp = marshal(WorkflowDraftVariableList(variables=[file_var]), _WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS) - - # Verify the response structure - assert isinstance(resp, dict) - assert len(resp["items"]) == 1 - item_dict = resp["items"][0] - assert item_dict["name"] == "file_var" - - # Verify the value is a dict (File.to_dict() result) and contains expected fields - value = item_dict["value"] - assert isinstance(value, dict) - remote_url = value["remote_url"] - - # For REMOTE_URL, the URL should be the original remote URL - assert remote_url == test_file.remote_url diff --git a/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_rag_pipeline_draft_variable.py b/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_rag_pipeline_draft_variable.py index 0d18120b715..3ba1374d80c 100644 --- a/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_rag_pipeline_draft_variable.py +++ b/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_rag_pipeline_draft_variable.py @@ -1,8 +1,11 @@ +import uuid from inspect import unwrap +from types import SimpleNamespace +from typing import Any from unittest.mock import MagicMock, patch import pytest -from flask import Flask, Response +from flask import Flask from controllers.common.errors import InvalidArgumentError, NotFoundError from controllers.console import console_ns @@ -16,8 +19,26 @@ from controllers.console.datasets.rag_pipeline.rag_pipeline_draft_variable impor RagPipelineVariableResetApi, ) from core.workflow.variable_prefixes import SYSTEM_VARIABLE_NODE_ID +from factories.variable_factory import build_segment from graphon.variables.types import SegmentType from models.account import Account, TenantAccountRole +from models.workflow import WorkflowDraftVariable +from services.workflow_draft_variable_service import WorkflowDraftVariableList + +_TEST_NODE_EXEC_ID = str(uuid.uuid4()) + + +def _node_variable(*, app_id: str = "p1", value: Any = "hello") -> WorkflowDraftVariable: + variable = WorkflowDraftVariable.new_node_variable( + app_id=app_id, + user_id="account-1", + node_id="node1", + name="node_var", + value=build_segment(value), + node_execution_id=_TEST_NODE_EXEC_ID, + ) + variable.id = str(uuid.uuid4()) + return variable @pytest.fixture @@ -47,13 +68,12 @@ class TestRagPipelineVariableCollectionApi: method = unwrap(api.get) pipeline = MagicMock(id="p1") + variable = _node_variable(value="hello") rag_srv = MagicMock() rag_srv.is_workflow_exist.return_value = True - # IMPORTANT: RESTX expects .variables - var_list = MagicMock() - var_list.variables = [] + var_list = WorkflowDraftVariableList(variables=[variable], total=1) draft_srv = MagicMock() draft_srv.list_variables_without_values.return_value = var_list @@ -73,7 +93,22 @@ class TestRagPipelineVariableCollectionApi: ): result = method(api, editor_user, pipeline) - assert result is var_list + assert result == { + "items": [ + { + "id": variable.id, + "type": "node", + "name": "node_var", + "description": "", + "selector": ["node1", "node_var"], + "value_type": "string", + "edited": False, + "visible": True, + "is_truncated": False, + } + ], + "total": 1, + } draft_srv.list_variables_without_values.assert_called_once_with( app_id="p1", page=1, @@ -114,8 +149,7 @@ class TestRagPipelineVariableCollectionApi: ): result = method(api, editor_user, pipeline) - assert isinstance(result, Response) - assert result.status_code == 204 + assert result == ("", 204) class TestRagPipelineNodeVariableCollectionApi: @@ -124,9 +158,8 @@ class TestRagPipelineNodeVariableCollectionApi: method = unwrap(api.get) pipeline = MagicMock(id="p1") - - var_list = MagicMock() - var_list.variables = [] + variable = _node_variable(value=None) + var_list = WorkflowDraftVariableList(variables=[variable]) srv = MagicMock() srv.list_node_variables.return_value = var_list @@ -142,7 +175,23 @@ class TestRagPipelineNodeVariableCollectionApi: ): result = method(api, editor_user, pipeline, "node1") - assert result is var_list + assert result == { + "items": [ + { + "id": variable.id, + "type": "node", + "name": "node_var", + "description": "", + "selector": ["node1", "node_var"], + "value_type": "none", + "edited": False, + "visible": True, + "is_truncated": False, + "value": None, + "full_content": None, + } + ] + } srv.list_node_variables.assert_called_once_with("p1", "node1", user_id="account-1") def test_get_node_variables_invalid_node(self, app: Flask, editor_user): @@ -157,6 +206,40 @@ class TestRagPipelineNodeVariableCollectionApi: class TestRagPipelineVariableApi: + def test_get_variable_success_returns_concrete_shape(self, app: Flask, fake_db, editor_user): + api = RagPipelineVariableApi() + method = unwrap(api.get) + + pipeline = MagicMock(id="p1") + variable = _node_variable(value={"answer": 42}) + + srv = MagicMock() + srv.get_variable.return_value = variable + + with ( + app.test_request_context("/"), + patch("controllers.console.datasets.rag_pipeline.rag_pipeline_draft_variable.db", fake_db), + patch( + "controllers.console.datasets.rag_pipeline.rag_pipeline_draft_variable.WorkflowDraftVariableService", + return_value=srv, + ), + ): + result = method(api, editor_user, pipeline, variable.id) + + assert result == { + "id": variable.id, + "type": "node", + "name": "node_var", + "description": "", + "selector": ["node1", "node_var"], + "value_type": "object", + "edited": False, + "visible": True, + "is_truncated": False, + "value": {"answer": 42}, + "full_content": None, + } + def test_get_variable_not_found(self, app: Flask, fake_db, editor_user): api = RagPipelineVariableApi() method = unwrap(api.get) @@ -199,6 +282,42 @@ class TestRagPipelineVariableApi: with pytest.raises(InvalidArgumentError): method(api, editor_user, pipeline, "v1") + def test_patch_variable_noop_returns_current_concrete_shape(self, app: Flask, fake_db, editor_user): + api = RagPipelineVariableApi() + method = unwrap(api.patch) + + pipeline = MagicMock(id="p1", tenant_id="t1") + variable = _node_variable(value=42) + + srv = MagicMock() + srv.get_variable.return_value = variable + + with ( + app.test_request_context("/", json={}), + patch.object(type(console_ns), "payload", {}), + patch("controllers.console.datasets.rag_pipeline.rag_pipeline_draft_variable.db", fake_db), + patch( + "controllers.console.datasets.rag_pipeline.rag_pipeline_draft_variable.WorkflowDraftVariableService", + return_value=srv, + ), + ): + result = method(api, editor_user, pipeline, variable.id) + + assert result == { + "id": variable.id, + "type": "node", + "name": "node_var", + "description": "", + "selector": ["node1", "node_var"], + "value_type": "number", + "edited": False, + "visible": True, + "is_truncated": False, + "value": 42, + "full_content": None, + } + srv.update_variable.assert_not_called() + def test_delete_variable_success(self, app: Flask, fake_db, editor_user): api = RagPipelineVariableApi() method = unwrap(api.delete) @@ -219,7 +338,7 @@ class TestRagPipelineVariableApi: ): result = method(api, editor_user, pipeline, "v1") - assert result.status_code == 204 + assert result == ("", 204) class TestRagPipelineVariableResetApi: @@ -229,7 +348,7 @@ class TestRagPipelineVariableResetApi: pipeline = MagicMock(id="p1") workflow = MagicMock() - variable = MagicMock(app_id="p1") + variable = _node_variable(value="reset") srv = MagicMock() srv.get_variable.return_value = variable @@ -249,14 +368,22 @@ class TestRagPipelineVariableResetApi: "controllers.console.datasets.rag_pipeline.rag_pipeline_draft_variable.WorkflowDraftVariableService", return_value=srv, ), - patch( - "controllers.console.datasets.rag_pipeline.rag_pipeline_draft_variable.marshal", - return_value={"id": "v1"}, - ), ): - result = method(api, editor_user, pipeline, "v1") + result = method(api, editor_user, pipeline, variable.id) - assert result == {"id": "v1"} + assert result == { + "id": variable.id, + "type": "node", + "name": "node_var", + "description": "", + "selector": ["node1", "node_var"], + "value_type": "string", + "edited": False, + "visible": True, + "is_truncated": False, + "value": "reset", + "full_content": None, + } class TestSystemAndEnvironmentVariablesApi: @@ -265,9 +392,16 @@ class TestSystemAndEnvironmentVariablesApi: method = unwrap(api.get) pipeline = MagicMock(id="p1") - - var_list = MagicMock() - var_list.variables = [] + variable = WorkflowDraftVariable.new_sys_variable( + app_id="p1", + user_id="account-1", + name="query", + value=build_segment("system query"), + editable=True, + node_execution_id=_TEST_NODE_EXEC_ID, + ) + variable.id = str(uuid.uuid4()) + var_list = WorkflowDraftVariableList(variables=[variable]) srv = MagicMock() srv.list_system_variables.return_value = var_list @@ -283,19 +417,35 @@ class TestSystemAndEnvironmentVariablesApi: ): result = method(api, editor_user, pipeline) - assert result is var_list + assert result == { + "items": [ + { + "id": variable.id, + "type": "sys", + "name": "query", + "description": "", + "selector": ["sys", "query"], + "value_type": "string", + "edited": False, + "visible": True, + "is_truncated": False, + "value": "system query", + "full_content": None, + } + ] + } srv.list_system_variables.assert_called_once_with("p1", user_id="account-1") def test_environment_variables_success(self, app: Flask, editor_user): api = RagPipelineEnvironmentVariableCollectionApi() method = unwrap(api.get) - env_var = MagicMock( + env_var = SimpleNamespace( id="e1", name="ENV", description="d", - selector="s", - value_type=MagicMock(value="string"), + selector=["env", "ENV"], + value_type=SimpleNamespace(value="string"), value="x", ) @@ -314,4 +464,19 @@ class TestSystemAndEnvironmentVariablesApi: ): result = method(api, editor_user, pipeline) - assert len(result["items"]) == 1 + assert result == { + "items": [ + { + "id": "e1", + "type": "env", + "name": "ENV", + "description": "d", + "selector": ["env", "ENV"], + "value_type": "string", + "value": "x", + "edited": False, + "visible": True, + "editable": True, + } + ] + } diff --git a/api/tests/unit_tests/controllers/console/snippets/test_snippet_workflow.py b/api/tests/unit_tests/controllers/console/snippets/test_snippet_workflow.py index b20dd3e30a7..122df375650 100644 --- a/api/tests/unit_tests/controllers/console/snippets/test_snippet_workflow.py +++ b/api/tests/unit_tests/controllers/console/snippets/test_snippet_workflow.py @@ -3,7 +3,6 @@ from __future__ import annotations import json from datetime import datetime from inspect import unwrap -from types import SimpleNamespace from unittest.mock import Mock import pytest @@ -13,6 +12,7 @@ from werkzeug.exceptions import HTTPException, NotFound from controllers.console.snippets import snippet_workflow as snippet_workflow_module from models.account import Account, TenantAccountRole from models.snippet import CustomizedSnippet +from models.workflow import Workflow, WorkflowKind, WorkflowType def _account(account_id: str = "account-1") -> Account: @@ -35,6 +35,38 @@ def _snippet(**overrides) -> CustomizedSnippet: return CustomizedSnippet(**data) +def _workflow(**overrides) -> Workflow: + workflow = Workflow.new( + tenant_id="tenant-1", + app_id="snippet-1", + type=WorkflowType.WORKFLOW.value, + version="2024-01-01 00:00:00", + graph=json.dumps({"nodes": [], "edges": []}), + features=json.dumps({}), + created_by="account-1", + environment_variables=[], + conversation_variables=[], + rag_pipeline_variables=[], + kind=WorkflowKind.SNIPPET.value, + marked_name="", + marked_comment="", + ) + workflow.id = "workflow-1" + for key, value in overrides.items(): + setattr(workflow, key, value) + return workflow + + +class _DbStub: + engine = object() + + +class _SessionStub: + def __init__(self, merged_snippet: CustomizedSnippet) -> None: + self.merge = Mock(return_value=merged_snippet) + self.commit = Mock() + + @pytest.fixture(autouse=True) def _patch_snippet_service_factory(monkeypatch: pytest.MonkeyPatch) -> None: def factory(): @@ -93,10 +125,15 @@ def test_get_snippet_raises_not_found_when_snippet_missing(app: Flask, monkeypat def test_draft_workflow_get_raises_when_missing(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: snippet = _snippet() + + class SnippetServiceStub: + def get_draft_workflow(self, *, snippet: CustomizedSnippet) -> None: + return None + monkeypatch.setattr( snippet_workflow_module, "SnippetService", - lambda: SimpleNamespace(get_draft_workflow=Mock(return_value=None)), + SnippetServiceStub, ) api = snippet_workflow_module.SnippetDraftWorkflowApi() @@ -111,10 +148,15 @@ def test_draft_workflow_post_returns_400_for_invalid_graph(app: Flask, monkeypat user = _account("account-1") snippet = _snippet() sync_draft_workflow = Mock(side_effect=ValueError("invalid graph")) + + class SnippetServiceStub: + def sync_draft_workflow(self, **kwargs): + return sync_draft_workflow(**kwargs) + monkeypatch.setattr( snippet_workflow_module, "SnippetService", - lambda: SimpleNamespace(sync_draft_workflow=sync_draft_workflow), + SnippetServiceStub, ) api = snippet_workflow_module.SnippetDraftWorkflowApi() @@ -131,27 +173,77 @@ def test_draft_workflow_post_returns_400_for_invalid_graph(app: Flask, monkeypat assert response == {"message": "invalid graph"} +def _response_model_name(entry: object) -> str: + assert isinstance(entry, tuple) + assert len(entry) >= 2 + model = entry[1] + name = getattr(model, "name", None) + assert isinstance(name, str) + return name + + +def test_snippet_workflow_endpoints_keep_response_docs() -> None: + assert snippet_workflow_module.SnippetDefaultBlockConfigsApi.get.__apidoc__["responses"]["200"] == ( + "Default block configs retrieved successfully", + None, + {}, + ) + + cases = [ + ( + snippet_workflow_module.SnippetDraftConfigApi.get, + snippet_workflow_module.SnippetDraftConfigResponse.__name__, + ), + ( + snippet_workflow_module.SnippetDraftRunIterationNodeApi.post, + snippet_workflow_module.EventStreamResponse.__name__, + ), + ( + snippet_workflow_module.SnippetDraftRunLoopNodeApi.post, + snippet_workflow_module.EventStreamResponse.__name__, + ), + ( + snippet_workflow_module.SnippetDraftWorkflowRunApi.post, + snippet_workflow_module.EventStreamResponse.__name__, + ), + ( + snippet_workflow_module.SnippetWorkflowTaskStopApi.post, + snippet_workflow_module.SimpleResultResponse.__name__, + ), + ] + + for view, model_name in cases: + responses = getattr(view, "__apidoc__", {}).get("responses", {}) + assert _response_model_name(responses["200"]) == model_name + + def test_draft_config_returns_parallel_depth_limit(app) -> None: api = snippet_workflow_module.SnippetDraftConfigApi() handler = unwrap(api.get) + snippet = _snippet() with app.test_request_context("/snippets/snippet-1/workflows/draft/config"): - assert handler(api, snippet=SimpleNamespace(id="snippet-1")) == {"parallel_depth_limit": 3} + assert handler(api, snippet=snippet) == {"parallel_depth_limit": 3} def test_published_workflow_get_returns_none_when_not_published(app) -> None: api = snippet_workflow_module.SnippetPublishedWorkflowApi() handler = unwrap(api.get) + snippet = _snippet(is_published=False) with app.test_request_context("/snippets/snippet-1/workflows/publish"): - assert handler(api, snippet=SimpleNamespace(id="snippet-1", is_published=False)) is None + assert handler(api, snippet=snippet) is None def test_published_workflow_post_returns_400_when_publish_fails(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: user = _account("account-1") snippet = _snippet() merged_snippet = _snippet() - session = SimpleNamespace(merge=Mock(return_value=merged_snippet), commit=Mock()) + session = _SessionStub(merged_snippet) + + class SnippetServiceStub: + def publish_workflow(self, **kwargs): + raise ValueError("No valid workflow found.") class SessionContext: def __init__(self, engine): @@ -164,11 +256,11 @@ def test_published_workflow_post_returns_400_when_publish_fails(app: Flask, monk return False monkeypatch.setattr(snippet_workflow_module, "Session", SessionContext) - monkeypatch.setattr(snippet_workflow_module, "db", SimpleNamespace(engine=object())) + monkeypatch.setattr(snippet_workflow_module, "db", _DbStub()) monkeypatch.setattr( snippet_workflow_module, "SnippetService", - lambda: SimpleNamespace(publish_workflow=Mock(side_effect=ValueError("No valid workflow found."))), + SnippetServiceStub, ) api = snippet_workflow_module.SnippetPublishedWorkflowApi() @@ -182,44 +274,97 @@ def test_published_workflow_post_returns_400_when_publish_fails(app: Flask, monk session.commit.assert_not_called() -def test_default_block_configs_delegates_to_service(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: - get_default_block_configs = Mock(return_value=[{"type": "llm"}]) +def test_published_workflow_post_returns_publish_result(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: + user = _account("account-1") + snippet = _snippet() + merged_snippet = _snippet() + workflow = _workflow(marked_name="Release 1", marked_comment="Initial release") + session = _SessionStub(merged_snippet) + publish_workflow = Mock(return_value=workflow) + + class SnippetServiceStub: + def publish_workflow(self, **kwargs): + return publish_workflow(**kwargs) + + class SessionContext: + def __init__(self, engine): + self.engine = engine + + def __enter__(self): + return session + + def __exit__(self, exc_type, exc, tb): + return False + + monkeypatch.setattr(snippet_workflow_module, "Session", SessionContext) + monkeypatch.setattr(snippet_workflow_module, "db", _DbStub()) monkeypatch.setattr( snippet_workflow_module, "SnippetService", - lambda: SimpleNamespace(get_default_block_configs=get_default_block_configs), + SnippetServiceStub, + ) + + api = snippet_workflow_module.SnippetPublishedWorkflowApi() + handler = unwrap(api.post) + + with app.test_request_context( + "/snippets/snippet-1/workflows/publish", + method="POST", + json={"marked_name": "Release 1", "marked_comment": "Initial release"}, + ): + response = handler(api, user, snippet) + + assert response == {"result": "success", "created_at": int(workflow.created_at.timestamp())} + publish_workflow.assert_called_once_with( + session=session, + snippet=merged_snippet, + account=user, + ) + session.commit.assert_called_once() + + +def test_default_block_configs_delegates_to_service(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: + get_default_block_configs = Mock(return_value=[{"type": "llm"}]) + + class SnippetServiceStub: + def get_default_block_configs(self): + return get_default_block_configs() + + monkeypatch.setattr( + snippet_workflow_module, + "SnippetService", + SnippetServiceStub, ) api = snippet_workflow_module.SnippetDefaultBlockConfigsApi() handler = unwrap(api.get) + snippet = _snippet() with app.test_request_context("/snippets/snippet-1/workflows/default-workflow-block-configs"): - result = handler(api, snippet=SimpleNamespace(id="snippet-1")) + result = handler(api, snippet=snippet) assert result == [{"type": "llm"}] get_default_block_configs.assert_called_once() def test_list_published_snippet_workflows_includes_input_fields(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: - workflow = SimpleNamespace( - id="workflow-1", - graph_dict={"nodes": [], "edges": []}, - features_dict={}, - unique_hash="hash-1", + workflow = _workflow( + graph=json.dumps({"nodes": [], "edges": []}), + features=json.dumps({}), version="2024-01-01 00:00:00", marked_name="", marked_comment="", - created_by_account=None, created_at=datetime(2024, 1, 1), - updated_by_account=None, updated_at=datetime(2024, 1, 1), - tool_published=False, environment_variables=[], conversation_variables=[], rag_pipeline_variables=[], ) input_fields = [{"variable": "query", "type": "text"}] snippet = _snippet(input_fields=json.dumps(input_fields)) + monkeypatch.setattr(Workflow, "created_by_account", property(lambda _workflow: None)) + monkeypatch.setattr(Workflow, "updated_by_account", property(lambda _workflow: None)) + monkeypatch.setattr(Workflow, "tool_published", property(lambda _workflow: False)) class SessionContext: def __init__(self, engine): @@ -232,11 +377,16 @@ def test_list_published_snippet_workflows_includes_input_fields(app: Flask, monk return False monkeypatch.setattr(snippet_workflow_module, "Session", SessionContext) - monkeypatch.setattr(snippet_workflow_module, "db", SimpleNamespace(engine=object())) + monkeypatch.setattr(snippet_workflow_module, "db", _DbStub()) + + class SnippetServiceStub: + def get_all_published_workflows(self, **kwargs): + return [workflow], False + monkeypatch.setattr( snippet_workflow_module, "SnippetService", - lambda: SimpleNamespace(get_all_published_workflows=Mock(return_value=([workflow], False))), + SnippetServiceStub, ) api = snippet_workflow_module.SnippetPublishedAllWorkflowApi() @@ -249,18 +399,21 @@ def test_list_published_snippet_workflows_includes_input_fields(app: Flask, monk def test_restore_published_snippet_workflow_to_draft_success(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: - workflow = SimpleNamespace( - unique_hash="restored-hash", + workflow = _workflow( updated_at=None, created_at=datetime(2024, 1, 1), ) user = _account("account-1") snippet = _snippet() + class SnippetServiceStub: + def restore_published_workflow_to_draft(self, **kwargs): + return workflow + monkeypatch.setattr( snippet_workflow_module, "SnippetService", - lambda: SimpleNamespace(restore_published_workflow_to_draft=lambda **_kwargs: workflow), + SnippetServiceStub, ) api = snippet_workflow_module.SnippetDraftWorkflowRestoreApi() @@ -273,21 +426,21 @@ def test_restore_published_snippet_workflow_to_draft_success(app: Flask, monkeyp response = handler(api, user, snippet, workflow_id="published-workflow") assert response["result"] == "success" - assert response["hash"] == "restored-hash" + assert response["hash"] == workflow.unique_hash def test_restore_published_snippet_workflow_to_draft_not_found(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: user = _account("account-1") snippet = _snippet() + class SnippetServiceStub: + def restore_published_workflow_to_draft(self, **kwargs): + raise snippet_workflow_module.WorkflowNotFoundError("Workflow not found") + monkeypatch.setattr( snippet_workflow_module, "SnippetService", - lambda: SimpleNamespace( - restore_published_workflow_to_draft=lambda **_kwargs: (_ for _ in ()).throw( - snippet_workflow_module.WorkflowNotFoundError("Workflow not found") - ) - ), + SnippetServiceStub, ) api = snippet_workflow_module.SnippetDraftWorkflowRestoreApi() @@ -307,14 +460,14 @@ def test_restore_published_snippet_workflow_to_draft_returns_400_for_draft_sourc user = _account("account-1") snippet = _snippet() + class SnippetServiceStub: + def restore_published_workflow_to_draft(self, **kwargs): + raise snippet_workflow_module.IsDraftWorkflowError("source workflow must be published") + monkeypatch.setattr( snippet_workflow_module, "SnippetService", - lambda: SimpleNamespace( - restore_published_workflow_to_draft=lambda **_kwargs: (_ for _ in ()).throw( - snippet_workflow_module.IsDraftWorkflowError("source workflow must be published") - ) - ), + SnippetServiceStub, ) api = snippet_workflow_module.SnippetDraftWorkflowRestoreApi() @@ -337,14 +490,14 @@ def test_restore_published_snippet_workflow_to_draft_returns_400_for_invalid_gra user = _account("account-1") snippet = _snippet() + class SnippetServiceStub: + def restore_published_workflow_to_draft(self, **kwargs): + raise ValueError("invalid snippet workflow graph") + monkeypatch.setattr( snippet_workflow_module, "SnippetService", - lambda: SimpleNamespace( - restore_published_workflow_to_draft=lambda **_kwargs: (_ for _ in ()).throw( - ValueError("invalid snippet workflow graph") - ) - ), + SnippetServiceStub, ) api = snippet_workflow_module.SnippetDraftWorkflowRestoreApi() @@ -363,10 +516,15 @@ def test_restore_published_snippet_workflow_to_draft_returns_400_for_invalid_gra def test_workflow_run_detail_raises_not_found_when_run_missing(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: snippet = _snippet() + + class SnippetServiceStub: + def get_snippet_workflow_run(self, **kwargs) -> None: + return None + monkeypatch.setattr( snippet_workflow_module, "SnippetService", - lambda: SimpleNamespace(get_snippet_workflow_run=Mock(return_value=None)), + SnippetServiceStub, ) api = snippet_workflow_module.SnippetWorkflowRunDetailApi() @@ -381,14 +539,19 @@ def test_draft_node_last_run_raises_not_found_when_execution_missing( app: Flask, monkeypatch: pytest.MonkeyPatch ) -> None: snippet = _snippet() - draft_workflow = SimpleNamespace(id="workflow-1") + draft_workflow = _workflow(id="workflow-1", version=Workflow.VERSION_DRAFT) + + class SnippetServiceStub: + def get_draft_workflow(self, *, snippet: CustomizedSnippet) -> Workflow: + return draft_workflow + + def get_snippet_node_last_run(self, **kwargs) -> None: + return None + monkeypatch.setattr( snippet_workflow_module, "SnippetService", - lambda: SimpleNamespace( - get_draft_workflow=Mock(return_value=draft_workflow), - get_snippet_node_last_run=Mock(return_value=None), - ), + SnippetServiceStub, ) api = snippet_workflow_module.SnippetDraftNodeLastRunApi() @@ -402,6 +565,12 @@ def test_draft_node_last_run_raises_not_found_when_execution_missing( def test_workflow_task_stop_uses_queue_flag_and_graph_command(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: set_stop_flag = Mock() send_stop_command = Mock() + + class GraphEngineManagerStub: + def __init__(self, redis_client): + self.redis_client = redis_client + self.send_stop_command = send_stop_command + monkeypatch.setattr( snippet_workflow_module.AppQueueManager, "set_stop_flag_no_user_check", @@ -410,14 +579,15 @@ def test_workflow_task_stop_uses_queue_flag_and_graph_command(app: Flask, monkey monkeypatch.setattr( snippet_workflow_module, "GraphEngineManager", - Mock(return_value=SimpleNamespace(send_stop_command=send_stop_command)), + GraphEngineManagerStub, ) api = snippet_workflow_module.SnippetWorkflowTaskStopApi() handler = unwrap(api.post) + snippet = _snippet() with app.test_request_context("/snippets/snippet-1/workflow-runs/tasks/task-1/stop", method="POST"): - result = handler(api, snippet=SimpleNamespace(id="snippet-1"), task_id="task-1") + result = handler(api, snippet=snippet, task_id="task-1") assert result == {"result": "success"} set_stop_flag.assert_called_once_with("task-1") diff --git a/api/tests/unit_tests/controllers/console/snippets/test_snippet_workflow_draft_variable.py b/api/tests/unit_tests/controllers/console/snippets/test_snippet_workflow_draft_variable.py index 03a6fdb0d60..054965dc2f5 100644 --- a/api/tests/unit_tests/controllers/console/snippets/test_snippet_workflow_draft_variable.py +++ b/api/tests/unit_tests/controllers/console/snippets/test_snippet_workflow_draft_variable.py @@ -1,15 +1,28 @@ +import json +import uuid +from contextlib import nullcontext from inspect import unwrap from types import SimpleNamespace +from typing import Any, cast from unittest.mock import Mock import pytest from flask import Flask from controllers.console.snippets import snippet_workflow_draft_variable as module -from core.workflow.variable_prefixes import CONVERSATION_VARIABLE_NODE_ID, SYSTEM_VARIABLE_NODE_ID +from factories.variable_factory import build_environment_variable_from_mapping, build_segment +from graphon.variables.types import SegmentType +from models import workflow as workflow_model_module from models.account import Account, AccountStatus +from models.snippet import CustomizedSnippet +from models.workflow import Workflow, WorkflowDraftVariable, WorkflowType from services.workflow_draft_variable_service import WorkflowDraftVariableList +_TEST_SNIPPET_ID = str(uuid.uuid4()) +_TEST_TENANT_ID = str(uuid.uuid4()) +_TEST_USER_ID = str(uuid.uuid4()) +_TEST_NODE_EXECUTION_ID = str(uuid.uuid4()) + def _make_account() -> Account: account = Account( @@ -17,16 +30,97 @@ def _make_account() -> Account: email="tester@example.com", status=AccountStatus.ACTIVE, ) - account.id = "user-1" # type: ignore[assignment] + account.id = _TEST_USER_ID return account +def _make_snippet() -> CustomizedSnippet: + snippet = CustomizedSnippet() + snippet.id = _TEST_SNIPPET_ID + snippet.tenant_id = _TEST_TENANT_ID + snippet.name = "Reusable snippet" + snippet.description = "Snippet under test" + snippet.type = "node" + return snippet + + +def _make_draft_workflow(*, environment_variables: list[Any] | None = None) -> Workflow: + workflow = Workflow() + workflow.id = str(uuid.uuid4()) + workflow.tenant_id = _TEST_TENANT_ID + workflow.app_id = _TEST_SNIPPET_ID + workflow.type = WorkflowType.SNIPPET + workflow.version = Workflow.VERSION_DRAFT + workflow.graph = "{}" + workflow.features = "{}" + workflow.created_by = _TEST_USER_ID + workflow._environment_variables = json.dumps( + {variable.id: variable.model_dump(mode="json") for variable in environment_variables or []} + ) + return workflow + + +def _make_node_variable( + *, value: Any = "value", node_id: str = "llm-1", name: str = "node_var" +) -> WorkflowDraftVariable: + return WorkflowDraftVariable.new_node_variable( + app_id=_TEST_SNIPPET_ID, + user_id=_TEST_USER_ID, + node_id=node_id, + name=name, + value=build_segment(value), + node_execution_id=_TEST_NODE_EXECUTION_ID, + ) + + +def _make_system_variable() -> WorkflowDraftVariable: + return WorkflowDraftVariable.new_sys_variable( + app_id=_TEST_SNIPPET_ID, + user_id=_TEST_USER_ID, + name="query", + value=build_segment("hello"), + node_execution_id=_TEST_NODE_EXECUTION_ID, + ) + + +def _make_conversation_variable() -> WorkflowDraftVariable: + return WorkflowDraftVariable.new_conversation_variable( + app_id=_TEST_SNIPPET_ID, + user_id=_TEST_USER_ID, + name="topic", + value=build_segment("support"), + ) + + +def _expected_variable_payload(variable: WorkflowDraftVariable, *, value: Any) -> dict[str, Any]: + expected_without_value = _expected_variable_without_value_payload(variable) + return { + **expected_without_value, + "value": value, + "full_content": None, + } + + +def _expected_variable_without_value_payload(variable: WorkflowDraftVariable) -> dict[str, Any]: + return { + "id": variable.id, + "type": "node", + "name": variable.name, + "description": "", + "selector": [variable.node_id, variable.name], + "value_type": str(variable.value_type.exposed_type()), + "edited": False, + "visible": True, + "is_truncated": False, + } + + @pytest.fixture(autouse=True) def _patch_snippet_service_factory(monkeypatch: pytest.MonkeyPatch): def factory(): service_factory = module.SnippetService if isinstance(service_factory, type): - return service_factory.__new__(service_factory) + return cast(Any, service_factory).__new__(service_factory) return service_factory() monkeypatch.setattr(module, "_snippet_service", factory) @@ -40,23 +134,17 @@ def app(): def test_ensure_snippet_draft_variable_row_allowed_rejects_system_variable(): - variable = SimpleNamespace(node_id=SYSTEM_VARIABLE_NODE_ID) - with pytest.raises(module.NotFoundError, match="variable not found"): - module._ensure_snippet_draft_variable_row_allowed(variable=variable, variable_id="var-1") + module._ensure_snippet_draft_variable_row_allowed(variable=_make_system_variable(), variable_id="var-1") def test_ensure_snippet_draft_variable_row_allowed_rejects_conversation_variable(): - variable = SimpleNamespace(node_id=CONVERSATION_VARIABLE_NODE_ID) - with pytest.raises(module.NotFoundError, match="variable not found"): - module._ensure_snippet_draft_variable_row_allowed(variable=variable, variable_id="var-1") + module._ensure_snippet_draft_variable_row_allowed(variable=_make_conversation_variable(), variable_id="var-1") def test_ensure_snippet_draft_variable_row_allowed_accepts_canvas_node_variable(): - variable = SimpleNamespace(node_id="llm-1") - - module._ensure_snippet_draft_variable_row_allowed(variable=variable, variable_id="var-1") + module._ensure_snippet_draft_variable_row_allowed(variable=_make_node_variable(), variable_id="var-1") def test_conversation_variables_returns_empty_list(app: Flask): @@ -64,9 +152,9 @@ def test_conversation_variables_returns_empty_list(app: Flask): handler = unwrap(api.get) with app.test_request_context("/"): - result = handler(api, _make_account(), snippet=SimpleNamespace(id="snippet-1")) + result = handler(api, _make_account(), snippet=_make_snippet()) - assert result == WorkflowDraftVariableList(variables=[]) + assert result == {"items": []} def test_system_variables_returns_empty_list(app: Flask): @@ -74,9 +162,9 @@ def test_system_variables_returns_empty_list(app: Flask): handler = unwrap(api.get) with app.test_request_context("/"): - result = handler(api, _make_account(), snippet=SimpleNamespace(id="snippet-1")) + result = handler(api, _make_account(), snippet=_make_snippet()) - assert result == WorkflowDraftVariableList(variables=[]) + assert result == {"items": []} def test_delete_variable_collection_deletes_current_user_variables(app: Flask, monkeypatch: pytest.MonkeyPatch): @@ -89,10 +177,10 @@ def test_delete_variable_collection_deletes_current_user_variables(app: Flask, m handler = unwrap(api.delete) with app.test_request_context("/", method="DELETE"): - response = handler(api, _make_account(), snippet=SimpleNamespace(id="snippet-1")) + response = handler(api, _make_account(), snippet=_make_snippet()) - assert response.status_code == 204 - draft_var_service.delete_user_workflow_variables.assert_called_once_with("snippet-1", user_id="user-1") + assert response == ("", 204) + draft_var_service.delete_user_workflow_variables.assert_called_once_with(_TEST_SNIPPET_ID, user_id=_TEST_USER_ID) db_session.commit.assert_called_once() @@ -108,11 +196,58 @@ def test_variable_collection_get_raises_when_draft_workflow_missing(app: Flask, with app.test_request_context("/?page=1&limit=20"): with pytest.raises(module.DraftWorkflowNotExist): - handler(api, _make_account(), snippet=SimpleNamespace(id="snippet-1")) + handler(api, _make_account(), snippet=_make_snippet()) + + +def test_variable_collection_get_returns_without_value_contract(app, monkeypatch): + variable = _make_node_variable(value="hidden-value") + draft_workflow = _make_draft_workflow() + captured_args: dict[str, Any] = {} + + class DraftVariableService: + def __init__(self, *, session: object) -> None: + captured_args["session"] = session + + def list_variables_without_values(self, **kwargs: Any) -> WorkflowDraftVariableList: + captured_args.update(kwargs) + return WorkflowDraftVariableList(variables=[variable], total=9) + + session = object() + monkeypatch.setattr( + module, + "SnippetService", + Mock(return_value=SimpleNamespace(get_draft_workflow=Mock(return_value=draft_workflow))), + ) + monkeypatch.setattr(module, "WorkflowDraftVariableService", DraftVariableService) + monkeypatch.setattr(module, "db", SimpleNamespace(engine=object())) + monkeypatch.setattr( + module, + "Session", + lambda *args, **kwargs: nullcontext(session), + ) + + api = module.SnippetWorkflowVariableCollectionApi() + handler = unwrap(api.get) + + with app.test_request_context("/?page=2&limit=3", method="GET"): + result = handler(api, _make_account(), snippet=_make_snippet()) + + expected = {"items": [_expected_variable_without_value_payload(variable)], "total": 9} + assert captured_args == { + "session": session, + "app_id": _TEST_SNIPPET_ID, + "page": 2, + "limit": 3, + "user_id": _TEST_USER_ID, + "exclude_node_ids": module._SNIPPET_EXCLUDED_DRAFT_VARIABLE_NODE_IDS, + } + assert result == expected + module.WorkflowDraftVariableListWithoutValueResponse.model_validate(result) def test_node_variable_collection_get_lists_node_variables(app: Flask, monkeypatch: pytest.MonkeyPatch): - variables = WorkflowDraftVariableList(variables=[SimpleNamespace(id="var-1")]) + variable = _make_node_variable(value=None) + variables = WorkflowDraftVariableList(variables=[variable]) list_node_variables = Mock(return_value=variables) class SessionContext: @@ -138,10 +273,12 @@ def test_node_variable_collection_get_lists_node_variables(app: Flask, monkeypat handler = unwrap(api.get) with app.test_request_context("/"): - result = handler(api, _make_account(), snippet=SimpleNamespace(id="snippet-1"), node_id="llm-1") + result = handler(api, _make_account(), snippet=_make_snippet(), node_id="llm-1") - assert result is variables - list_node_variables.assert_called_once_with("snippet-1", "llm-1", user_id="user-1") + expected = {"items": [_expected_variable_payload(variable, value=None)]} + assert result == expected + module.WorkflowDraftVariableListResponse.model_validate(result) + list_node_variables.assert_called_once_with(_TEST_SNIPPET_ID, "llm-1", user_id=_TEST_USER_ID) def test_node_variable_collection_delete_deletes_node_variables(app: Flask, monkeypatch: pytest.MonkeyPatch): @@ -156,15 +293,15 @@ def test_node_variable_collection_delete_deletes_node_variables(app: Flask, monk handler = unwrap(api.delete) with app.test_request_context("/", method="DELETE"): - response = handler(api, _make_account(), snippet=SimpleNamespace(id="snippet-1"), node_id="llm-1") + response = handler(api, _make_account(), snippet=_make_snippet(), node_id="llm-1") - assert response.status_code == 204 - delete_node_variables.assert_called_once_with("snippet-1", "llm-1", user_id="user-1") + assert response == ("", 204) + delete_node_variables.assert_called_once_with(_TEST_SNIPPET_ID, "llm-1", user_id=_TEST_USER_ID) db_session.commit.assert_called_once() def test_variable_patch_returns_variable_when_no_changes(app: Flask, monkeypatch: pytest.MonkeyPatch): - variable = SimpleNamespace(id="var-1", app_id="snippet-1", user_id="user-1", node_id="llm-1") + variable = _make_node_variable(value=42) draft_var_service = SimpleNamespace(get_variable=Mock(return_value=variable), update_variable=Mock()) db_session = Mock() db_session.return_value = SimpleNamespace() @@ -178,17 +315,76 @@ def test_variable_patch_returns_variable_when_no_changes(app: Flask, monkeypatch result = handler( api, _make_account(), - snippet=SimpleNamespace(id="snippet-1", tenant_id="tenant-1"), - variable_id="var-1", + snippet=_make_snippet(), + variable_id=variable.id, ) - assert result is variable + expected = _expected_variable_payload(variable, value=42) + assert result == expected + module.WorkflowDraftVariableResponse.model_validate(result) draft_var_service.update_variable.assert_not_called() db_session.commit.assert_not_called() +def test_variable_get_returns_raw_response_contract(app: Flask, monkeypatch: pytest.MonkeyPatch): + variable = _make_node_variable(value={"count": 2, "enabled": True}) + draft_var_service = SimpleNamespace(get_variable=Mock(return_value=variable)) + db_session = Mock() + db_session.return_value = SimpleNamespace() + monkeypatch.setattr(module.db, "session", db_session) + monkeypatch.setattr(module, "WorkflowDraftVariableService", Mock(return_value=draft_var_service)) + + api = module.SnippetVariableApi() + handler = unwrap(api.get) + + with app.test_request_context("/", method="GET"): + result = handler(api, _make_account(), snippet=_make_snippet(), variable_id=variable.id) + + expected = _expected_variable_payload(variable, value={"count": 2, "enabled": True}) + assert result == expected + module.WorkflowDraftVariableResponse.model_validate(result) + draft_var_service.get_variable.assert_called_once_with(variable_id=variable.id) + + +def test_variable_patch_updates_from_pydantic_payload_and_returns_raw_contract( + app: Flask, monkeypatch: pytest.MonkeyPatch +): + variable = _make_node_variable(value=1, name="old_name") + request_model = module.WorkflowDraftVariableUpdatePayload.model_validate({"name": "renamed", "value": 13}) + + class DraftVariableService: + def __init__(self, session: object) -> None: + pass + + def get_variable(self, *, variable_id: str) -> WorkflowDraftVariable: + assert variable_id == variable.id + return variable + + def update_variable(self, target: WorkflowDraftVariable, *, name: str | None, value: Any) -> None: + assert target is variable + assert name == "renamed" + target.set_name(name) + target.set_value(value) + + session = Mock(return_value=object()) + session.commit = Mock() + monkeypatch.setattr(module, "WorkflowDraftVariableService", DraftVariableService) + monkeypatch.setattr(module.db, "session", session) + + api = module.SnippetVariableApi() + handler = unwrap(api.patch) + + with app.test_request_context("/", method="PATCH", json=request_model.model_dump(mode="json")): + result = handler(api, _make_account(), snippet=_make_snippet(), variable_id=variable.id) + + expected = _expected_variable_payload(variable, value=13) + assert result == expected + module.WorkflowDraftVariableResponse.model_validate(result) + session.commit.assert_called_once() + + def test_variable_delete_deletes_variable(app: Flask, monkeypatch: pytest.MonkeyPatch): - variable = SimpleNamespace(id="var-1", app_id="snippet-1", user_id="user-1", node_id="llm-1") + variable = _make_node_variable() delete_variable = Mock() draft_var_service = SimpleNamespace(get_variable=Mock(return_value=variable), delete_variable=delete_variable) db_session = Mock() @@ -200,16 +396,16 @@ def test_variable_delete_deletes_variable(app: Flask, monkeypatch: pytest.Monkey handler = unwrap(api.delete) with app.test_request_context("/", method="DELETE"): - response = handler(api, _make_account(), snippet=SimpleNamespace(id="snippet-1"), variable_id="var-1") + response = handler(api, _make_account(), snippet=_make_snippet(), variable_id=variable.id) - assert response.status_code == 204 + assert response == ("", 204) delete_variable.assert_called_once_with(variable) db_session.commit.assert_called_once() def test_variable_reset_returns_no_content_when_reset_result_is_none(app: Flask, monkeypatch: pytest.MonkeyPatch): - variable = SimpleNamespace(id="var-1", app_id="snippet-1", user_id="user-1", node_id="llm-1") - draft_workflow = SimpleNamespace(id="workflow-1") + variable = _make_node_variable() + draft_workflow = _make_draft_workflow() draft_var_service = SimpleNamespace( get_variable=Mock(return_value=variable), reset_variable=Mock(return_value=None), @@ -228,42 +424,75 @@ def test_variable_reset_returns_no_content_when_reset_result_is_none(app: Flask, handler = unwrap(api.put) with app.test_request_context("/", method="PUT"): - response = handler(api, _make_account(), snippet=SimpleNamespace(id="snippet-1"), variable_id="var-1") + response = handler(api, _make_account(), snippet=_make_snippet(), variable_id=variable.id) assert response.status_code == 204 draft_var_service.reset_variable.assert_called_once_with(draft_workflow, variable) db_session.commit.assert_called_once() -def test_environment_variables_returns_workflow_environment_variables(app: Flask, monkeypatch: pytest.MonkeyPatch): - env_var = SimpleNamespace( - id="env-1", - name="API_KEY", - description="secret", - selector=["env", "API_KEY"], - value_type=SimpleNamespace(exposed_type=Mock(return_value=SimpleNamespace(value="secret"))), - value="sk-test", +def test_variable_reset_returns_raw_response_contract_when_variable_is_reset( + app: Flask, monkeypatch: pytest.MonkeyPatch +): + variable = _make_node_variable(value="edited") + reset_variable = _make_node_variable(value="default") + reset_variable.id = variable.id + draft_workflow = _make_draft_workflow() + draft_var_service = SimpleNamespace( + get_variable=Mock(return_value=variable), + reset_variable=Mock(return_value=reset_variable), ) + db_session = Mock() + db_session.return_value = SimpleNamespace() + monkeypatch.setattr(module.db, "session", db_session) + monkeypatch.setattr(module, "WorkflowDraftVariableService", Mock(return_value=draft_var_service)) monkeypatch.setattr( module, "SnippetService", - Mock( - return_value=SimpleNamespace( - get_draft_workflow=Mock(return_value=SimpleNamespace(environment_variables=[env_var])) - ) - ), + Mock(return_value=SimpleNamespace(get_draft_workflow=Mock(return_value=draft_workflow))), + ) + + api = module.SnippetVariableResetApi() + handler = unwrap(api.put) + + with app.test_request_context("/", method="PUT"): + result = handler(api, _make_account(), snippet=_make_snippet(), variable_id=variable.id) + + expected = _expected_variable_payload(reset_variable, value="default") + assert result == expected + module.WorkflowDraftVariableResponse.model_validate(result) + draft_var_service.reset_variable.assert_called_once_with(draft_workflow, variable) + db_session.commit.assert_called_once() + + +def test_environment_variables_returns_workflow_environment_variables(app, monkeypatch): + env_var = build_environment_variable_from_mapping( + { + "id": str(uuid.uuid4()), + "name": "API_KEY", + "description": "secret", + "value_type": SegmentType.SECRET, + "value": "sk-test", + } + ) + draft_workflow = _make_draft_workflow(environment_variables=[env_var]) + monkeypatch.setattr(workflow_model_module.encrypter, "decrypt_token", lambda tenant_id, token: token) + monkeypatch.setattr( + module, + "SnippetService", + Mock(return_value=SimpleNamespace(get_draft_workflow=Mock(return_value=draft_workflow))), ) api = module.SnippetEnvironmentVariableCollectionApi() handler = unwrap(api.get) with app.test_request_context("/"): - result = handler(api, _make_account(), snippet=SimpleNamespace(id="snippet-1")) + result = handler(api, _make_account(), snippet=_make_snippet()) - assert result == { + expected = { "items": [ { - "id": "env-1", + "id": env_var.id, "type": "env", "name": "API_KEY", "description": "secret", @@ -276,3 +505,5 @@ def test_environment_variables_returns_workflow_environment_variables(app: Flask } ] } + assert result == expected + module.WorkflowDraftEnvironmentVariableListResponse.model_validate(result) diff --git a/packages/contracts/generated/api/console/account/types.gen.ts b/packages/contracts/generated/api/console/account/types.gen.ts index cdd45925fb2..1a7d1644f8f 100644 --- a/packages/contracts/generated/api/console/account/types.gen.ts +++ b/packages/contracts/generated/api/console/account/types.gen.ts @@ -12,7 +12,7 @@ export type AccountAvatarPayload = { avatar: string } -export type Account = { +export type AccountResponse = { avatar?: string | null readonly avatar_url: string | null created_at?: number | null @@ -140,7 +140,7 @@ export type AccountIntegrateResponse = { provider: string } -export type AccountWritable = { +export type AccountResponseWritable = { avatar?: string | null created_at?: number | null email: string @@ -177,7 +177,7 @@ export type PostAccountAvatarData = { } export type PostAccountAvatarResponses = { - 200: Account + 200: AccountResponse } export type PostAccountAvatarResponse = PostAccountAvatarResponses[keyof PostAccountAvatarResponses] @@ -218,7 +218,7 @@ export type PostAccountChangeEmailResetData = { } export type PostAccountChangeEmailResetResponses = { - 200: Account + 200: AccountResponse } export type PostAccountChangeEmailResetResponse @@ -374,7 +374,7 @@ export type PostAccountInterfaceLanguageData = { } export type PostAccountInterfaceLanguageResponses = { - 200: Account + 200: AccountResponse } export type PostAccountInterfaceLanguageResponse @@ -388,7 +388,7 @@ export type PostAccountInterfaceThemeData = { } export type PostAccountInterfaceThemeResponses = { - 200: Account + 200: AccountResponse } export type PostAccountInterfaceThemeResponse @@ -402,7 +402,7 @@ export type PostAccountNameData = { } export type PostAccountNameResponses = { - 200: Account + 200: AccountResponse } export type PostAccountNameResponse = PostAccountNameResponses[keyof PostAccountNameResponses] @@ -415,7 +415,7 @@ export type PostAccountPasswordData = { } export type PostAccountPasswordResponses = { - 200: Account + 200: AccountResponse } export type PostAccountPasswordResponse @@ -429,7 +429,7 @@ export type GetAccountProfileData = { } export type GetAccountProfileResponses = { - 200: Account + 200: AccountResponse } export type GetAccountProfileResponse = GetAccountProfileResponses[keyof GetAccountProfileResponses] @@ -442,7 +442,7 @@ export type PostAccountTimezoneData = { } export type PostAccountTimezoneResponses = { - 200: Account + 200: AccountResponse } export type PostAccountTimezoneResponse diff --git a/packages/contracts/generated/api/console/account/zod.gen.ts b/packages/contracts/generated/api/console/account/zod.gen.ts index 9951efc8d9f..cba539b0316 100644 --- a/packages/contracts/generated/api/console/account/zod.gen.ts +++ b/packages/contracts/generated/api/console/account/zod.gen.ts @@ -17,9 +17,9 @@ export const zAccountAvatarPayload = z.object({ }) /** - * Account + * AccountResponse */ -export const zAccount = z.object({ +export const zAccountResponse = z.object({ avatar: z.string().nullish(), avatar_url: z.string().nullable(), created_at: z.int().nullish(), @@ -212,9 +212,9 @@ export const zAccountIntegrateListResponse = z.object({ }) /** - * Account + * AccountResponse */ -export const zAccountWritable = z.object({ +export const zAccountResponseWritable = z.object({ avatar: z.string().nullish(), created_at: z.int().nullish(), email: z.string(), @@ -242,7 +242,7 @@ export const zPostAccountAvatarBody = zAccountAvatarPayload /** * Success */ -export const zPostAccountAvatarResponse = zAccount +export const zPostAccountAvatarResponse = zAccountResponse export const zPostAccountChangeEmailBody = zChangeEmailSendPayload @@ -263,7 +263,7 @@ export const zPostAccountChangeEmailResetBody = zChangeEmailResetPayload /** * Success */ -export const zPostAccountChangeEmailResetResponse = zAccount +export const zPostAccountChangeEmailResetResponse = zAccountResponse export const zPostAccountChangeEmailValidityBody = zChangeEmailValidityPayload @@ -336,37 +336,37 @@ export const zPostAccountInterfaceLanguageBody = zAccountInterfaceLanguagePayloa /** * Success */ -export const zPostAccountInterfaceLanguageResponse = zAccount +export const zPostAccountInterfaceLanguageResponse = zAccountResponse export const zPostAccountInterfaceThemeBody = zAccountInterfaceThemePayload /** * Success */ -export const zPostAccountInterfaceThemeResponse = zAccount +export const zPostAccountInterfaceThemeResponse = zAccountResponse export const zPostAccountNameBody = zAccountNamePayload /** * Success */ -export const zPostAccountNameResponse = zAccount +export const zPostAccountNameResponse = zAccountResponse export const zPostAccountPasswordBody = zAccountPasswordPayload /** * Success */ -export const zPostAccountPasswordResponse = zAccount +export const zPostAccountPasswordResponse = zAccountResponse /** * Success */ -export const zGetAccountProfileResponse = zAccount +export const zGetAccountProfileResponse = zAccountResponse export const zPostAccountTimezoneBody = zAccountTimezonePayload /** * Success */ -export const zPostAccountTimezoneResponse = zAccount +export const zPostAccountTimezoneResponse = zAccountResponse diff --git a/packages/contracts/generated/api/console/apps/orpc.gen.ts b/packages/contracts/generated/api/console/apps/orpc.gen.ts index ea72df28458..1409ac2ea2e 100644 --- a/packages/contracts/generated/api/console/apps/orpc.gen.ts +++ b/packages/contracts/generated/api/console/apps/orpc.gen.ts @@ -173,11 +173,6 @@ import { zGetAppsByAppIdWorkflowRunsPath, zGetAppsByAppIdWorkflowRunsQuery, zGetAppsByAppIdWorkflowRunsResponse, - zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypePath, - zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeQuery, - zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeResponse, - zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsPath, - zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsResponse, zGetAppsByAppIdWorkflowsDraftConversationVariablesPath, zGetAppsByAppIdWorkflowsDraftConversationVariablesResponse, zGetAppsByAppIdWorkflowsDraftEnvironmentVariablesPath, @@ -258,18 +253,6 @@ import { zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormPreviewBody, zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormPreviewPath, zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormPreviewResponse, - zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunBody, - zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunPath, - zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunResponse, - zPostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunBody, - zPostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunPath, - zPostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunResponse, - zPostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunBody, - zPostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunPath, - zPostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunResponse, - zPostAppsByAppIdAdvancedChatWorkflowsDraftRunBody, - zPostAppsByAppIdAdvancedChatWorkflowsDraftRunPath, - zPostAppsByAppIdAdvancedChatWorkflowsDraftRunResponse, zPostAppsByAppIdAgentFilesBody, zPostAppsByAppIdAgentFilesPath, zPostAppsByAppIdAgentFilesQuery, @@ -383,15 +366,6 @@ import { zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormPreviewBody, zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormPreviewPath, zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormPreviewResponse, - zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunBody, - zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunPath, - zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunResponse, - zPostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunBody, - zPostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunPath, - zPostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunResponse, - zPostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunBody, - zPostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunPath, - zPostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunResponse, zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCopyFromRosterBody, zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCopyFromRosterPath, zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCopyFromRosterResponse, @@ -411,15 +385,6 @@ import { zPostAppsByAppIdWorkflowsDraftNodesByNodeIdTriggerRunResponse, zPostAppsByAppIdWorkflowsDraftPath, zPostAppsByAppIdWorkflowsDraftResponse, - zPostAppsByAppIdWorkflowsDraftRunBody, - zPostAppsByAppIdWorkflowsDraftRunPath, - zPostAppsByAppIdWorkflowsDraftRunResponse, - zPostAppsByAppIdWorkflowsDraftTriggerRunAllBody, - zPostAppsByAppIdWorkflowsDraftTriggerRunAllPath, - zPostAppsByAppIdWorkflowsDraftTriggerRunAllResponse, - zPostAppsByAppIdWorkflowsDraftTriggerRunBody, - zPostAppsByAppIdWorkflowsDraftTriggerRunPath, - zPostAppsByAppIdWorkflowsDraftTriggerRunResponse, zPostAppsByAppIdWorkflowsPublishBody, zPostAppsByAppIdWorkflowsPublishPath, zPostAppsByAppIdWorkflowsPublishResponse, @@ -630,36 +595,8 @@ export const preview = { post: post4, } -/** - * Submit human input form preview - * - * Submit human input form preview for advanced chat workflow - */ -export const post5 = oc - .route({ - description: 'Submit human input form preview for advanced chat workflow', - inputStructure: 'detailed', - method: 'POST', - operationId: 'postAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRun', - path: '/apps/{app_id}/advanced-chat/workflows/draft/human-input/nodes/{node_id}/form/run', - summary: 'Submit human input form preview', - tags: ['console'], - }) - .input( - z.object({ - body: zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunBody, - params: zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunPath, - }), - ) - .output(zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunResponse) - -export const run = { - post: post5, -} - export const form = { preview, - run, } export const byNodeId = { @@ -674,116 +611,8 @@ export const humanInput = { nodes, } -/** - * Run draft workflow iteration node - * - * Run draft workflow iteration node for advanced chat - */ -export const post6 = oc - .route({ - description: 'Run draft workflow iteration node for advanced chat', - inputStructure: 'detailed', - method: 'POST', - operationId: 'postAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRun', - path: '/apps/{app_id}/advanced-chat/workflows/draft/iteration/nodes/{node_id}/run', - summary: 'Run draft workflow iteration node', - tags: ['console'], - }) - .input( - z.object({ - body: zPostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunBody, - params: zPostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunPath, - }), - ) - .output(zPostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunResponse) - -export const run2 = { - post: post6, -} - -export const byNodeId2 = { - run: run2, -} - -export const nodes2 = { - byNodeId: byNodeId2, -} - -export const iteration = { - nodes: nodes2, -} - -/** - * Run draft workflow loop node - * - * Run draft workflow loop node for advanced chat - */ -export const post7 = oc - .route({ - description: 'Run draft workflow loop node for advanced chat', - inputStructure: 'detailed', - method: 'POST', - operationId: 'postAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRun', - path: '/apps/{app_id}/advanced-chat/workflows/draft/loop/nodes/{node_id}/run', - summary: 'Run draft workflow loop node', - tags: ['console'], - }) - .input( - z.object({ - body: zPostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunBody, - params: zPostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunPath, - }), - ) - .output(zPostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunResponse) - -export const run3 = { - post: post7, -} - -export const byNodeId3 = { - run: run3, -} - -export const nodes3 = { - byNodeId: byNodeId3, -} - -export const loop = { - nodes: nodes3, -} - -/** - * Run draft workflow - * - * Run draft workflow for advanced chat application - */ -export const post8 = oc - .route({ - description: 'Run draft workflow for advanced chat application', - inputStructure: 'detailed', - method: 'POST', - operationId: 'postAppsByAppIdAdvancedChatWorkflowsDraftRun', - path: '/apps/{app_id}/advanced-chat/workflows/draft/run', - summary: 'Run draft workflow', - tags: ['console'], - }) - .input( - z.object({ - body: zPostAppsByAppIdAdvancedChatWorkflowsDraftRunBody, - params: zPostAppsByAppIdAdvancedChatWorkflowsDraftRunPath, - }), - ) - .output(zPostAppsByAppIdAdvancedChatWorkflowsDraftRunResponse) - -export const run4 = { - post: post8, -} - export const draft = { humanInput, - iteration, - loop, - run: run4, } export const workflows2 = { @@ -953,7 +782,7 @@ export const delete_ = oc * * Commit an uploaded file into the agent drive under files/ (ENG-625 D3) */ -export const post9 = oc +export const post5 = oc .route({ description: 'Commit an uploaded file into the agent drive under files/ (ENG-625 D3)', inputStructure: 'detailed', @@ -975,7 +804,7 @@ export const post9 = oc export const files2 = { delete: delete_, - post: post9, + post: post5, } /** @@ -1005,7 +834,7 @@ export const logs = { * * Upload + standardize a Skill into the agent drive */ -export const post10 = oc +export const post6 = oc .route({ description: 'Upload + standardize a Skill into the agent drive', inputStructure: 'detailed', @@ -1026,7 +855,7 @@ export const post10 = oc .output(zPostAppsByAppIdAgentSkillsUploadResponse) export const upload = { - post: post10, + post: post6, } /** @@ -1035,7 +864,7 @@ export const upload = { * Infer CLI tool + ENV suggestions from a standardized skill's SKILL.md (draft only, ENG-371) * Saving still goes through composer validation. */ -export const post11 = oc +export const post7 = oc .route({ description: 'Infer CLI tool + ENV suggestions from a standardized skill\'s SKILL.md (draft only, ENG-371)\nSaving still goes through composer validation.', @@ -1055,7 +884,7 @@ export const post11 = oc .output(zPostAppsByAppIdAgentSkillsBySlugInferToolsResponse) export const inferTools = { - post: post11, + post: post7, } /** @@ -1121,7 +950,7 @@ export const status = { /** * Enable or disable annotation reply for an app */ -export const post12 = oc +export const post8 = oc .route({ description: 'Enable or disable annotation reply for an app', inputStructure: 'detailed', @@ -1139,7 +968,7 @@ export const post12 = oc .output(zPostAppsByAppIdAnnotationReplyByActionResponse) export const byAction = { - post: post12, + post: post8, status, } @@ -1169,7 +998,7 @@ export const annotationSetting = { /** * Update annotation settings for an app */ -export const post13 = oc +export const post9 = oc .route({ description: 'Update annotation settings for an app', inputStructure: 'detailed', @@ -1187,7 +1016,7 @@ export const post13 = oc .output(zPostAppsByAppIdAnnotationSettingsByAnnotationSettingIdResponse) export const byAnnotationSettingId = { - post: post13, + post: post9, } export const annotationSettings = { @@ -1197,7 +1026,7 @@ export const annotationSettings = { /** * Batch import annotations from CSV file with rate limiting and security checks */ -export const post14 = oc +export const post10 = oc .route({ description: 'Batch import annotations from CSV file with rate limiting and security checks', inputStructure: 'detailed', @@ -1210,7 +1039,7 @@ export const post14 = oc .output(zPostAppsByAppIdAnnotationsBatchImportResponse) export const batchImport = { - post: post14, + post: post10, } /** @@ -1313,7 +1142,7 @@ export const delete3 = oc /** * Update or delete an annotation */ -export const post15 = oc +export const post11 = oc .route({ description: 'Update or delete an annotation', inputStructure: 'detailed', @@ -1332,7 +1161,7 @@ export const post15 = oc export const byAnnotationId = { delete: delete3, - post: post15, + post: post11, hitHistories, } @@ -1371,7 +1200,7 @@ export const get17 = oc /** * Create a new annotation for an app */ -export const post16 = oc +export const post12 = oc .route({ description: 'Create a new annotation for an app', inputStructure: 'detailed', @@ -1389,7 +1218,7 @@ export const post16 = oc export const annotations = { delete: delete4, get: get17, - post: post16, + post: post12, batchImport, batchImportStatus, count: count2, @@ -1400,7 +1229,7 @@ export const annotations = { /** * Enable or disable app API */ -export const post17 = oc +export const post13 = oc .route({ description: 'Enable or disable app API', inputStructure: 'detailed', @@ -1413,13 +1242,13 @@ export const post17 = oc .output(zPostAppsByAppIdApiEnableResponse) export const apiEnable = { - post: post17, + post: post13, } /** * Transcript audio to text for chat messages */ -export const post18 = oc +export const post14 = oc .route({ description: 'Transcript audio to text for chat messages', inputStructure: 'detailed', @@ -1432,7 +1261,7 @@ export const post18 = oc .output(zPostAppsByAppIdAudioToTextResponse) export const audioToText = { - post: post18, + post: post14, } /** @@ -1522,7 +1351,7 @@ export const byMessageId = { /** * Stop a running chat message generation */ -export const post19 = oc +export const post15 = oc .route({ description: 'Stop a running chat message generation', inputStructure: 'detailed', @@ -1535,7 +1364,7 @@ export const post19 = oc .output(zPostAppsByAppIdChatMessagesByTaskIdStopResponse) export const stop = { - post: post19, + post: post15, } export const byTaskId = { @@ -1629,7 +1458,7 @@ export const completionConversations = { /** * Stop a running completion message generation */ -export const post20 = oc +export const post16 = oc .route({ description: 'Stop a running completion message generation', inputStructure: 'detailed', @@ -1642,7 +1471,7 @@ export const post20 = oc .output(zPostAppsByAppIdCompletionMessagesByTaskIdStopResponse) export const stop2 = { - post: post20, + post: post16, } export const byTaskId2 = { @@ -1652,7 +1481,7 @@ export const byTaskId2 = { /** * Generate completion message for debugging */ -export const post21 = oc +export const post17 = oc .route({ description: 'Generate completion message for debugging', inputStructure: 'detailed', @@ -1670,7 +1499,7 @@ export const post21 = oc .output(zPostAppsByAppIdCompletionMessagesResponse) export const completionMessages = { - post: post21, + post: post17, byTaskId: byTaskId2, } @@ -1705,7 +1534,7 @@ export const conversationVariables = { * Convert expert mode of chatbot app to workflow mode * Convert Completion App to Workflow App */ -export const post22 = oc +export const post18 = oc .route({ description: 'Convert application to workflow mode\nConvert expert mode of chatbot app to workflow mode\nConvert Completion App to Workflow App', @@ -1725,7 +1554,7 @@ export const post22 = oc .output(zPostAppsByAppIdConvertToWorkflowResponse) export const convertToWorkflow = { - post: post22, + post: post18, } /** @@ -1733,7 +1562,7 @@ export const convertToWorkflow = { * * Create a copy of an existing application */ -export const post23 = oc +export const post19 = oc .route({ description: 'Create a copy of an existing application', inputStructure: 'detailed', @@ -1748,7 +1577,7 @@ export const post23 = oc .output(zPostAppsByAppIdCopyResponse) export const copy = { - post: post23, + post: post19, } /** @@ -1802,7 +1631,7 @@ export const export3 = { /** * Create or update message feedback (like/dislike) */ -export const post24 = oc +export const post20 = oc .route({ description: 'Create or update message feedback (like/dislike)', inputStructure: 'detailed', @@ -1815,14 +1644,14 @@ export const post24 = oc .output(zPostAppsByAppIdFeedbacksResponse) export const feedbacks = { - post: post24, + post: post20, export: export3, } /** * Update application icon */ -export const post25 = oc +export const post21 = oc .route({ description: 'Update application icon', inputStructure: 'detailed', @@ -1835,7 +1664,7 @@ export const post25 = oc .output(zPostAppsByAppIdIconResponse) export const icon = { - post: post25, + post: post21, } /** @@ -1866,7 +1695,7 @@ export const messages = { * * Update application model configuration */ -export const post26 = oc +export const post22 = oc .route({ description: 'Update application model configuration', inputStructure: 'detailed', @@ -1882,13 +1711,13 @@ export const post26 = oc .output(zPostAppsByAppIdModelConfigResponse) export const modelConfig = { - post: post26, + post: post22, } /** * Check if app name is available */ -export const post27 = oc +export const post23 = oc .route({ description: 'Check if app name is available', inputStructure: 'detailed', @@ -1901,13 +1730,13 @@ export const post27 = oc .output(zPostAppsByAppIdNameResponse) export const name = { - post: post27, + post: post23, } /** * Publish app to Creators Platform */ -export const post28 = oc +export const post24 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -1920,7 +1749,7 @@ export const post28 = oc .output(zPostAppsByAppIdPublishToCreatorsPlatformResponse) export const publishToCreatorsPlatform = { - post: post28, + post: post24, } /** @@ -1941,7 +1770,7 @@ export const get28 = oc /** * Create MCP server configuration for an application */ -export const post29 = oc +export const post25 = oc .route({ description: 'Create MCP server configuration for an application', inputStructure: 'detailed', @@ -1971,14 +1800,14 @@ export const put = oc export const server = { get: get28, - post: post29, + post: post25, put, } /** * Reset access token for application site */ -export const post30 = oc +export const post26 = oc .route({ description: 'Reset access token for application site', inputStructure: 'detailed', @@ -1991,13 +1820,13 @@ export const post30 = oc .output(zPostAppsByAppIdSiteAccessTokenResetResponse) export const accessTokenReset = { - post: post30, + post: post26, } /** * Update application site configuration */ -export const post31 = oc +export const post27 = oc .route({ description: 'Update application site configuration', inputStructure: 'detailed', @@ -2010,14 +1839,14 @@ export const post31 = oc .output(zPostAppsByAppIdSiteResponse) export const site = { - post: post31, + post: post27, accessTokenReset, } /** * Enable or disable app site */ -export const post32 = oc +export const post28 = oc .route({ description: 'Enable or disable app site', inputStructure: 'detailed', @@ -2030,7 +1859,7 @@ export const post32 = oc .output(zPostAppsByAppIdSiteEnableResponse) export const siteEnable = { - post: post32, + post: post28, } /** @@ -2051,7 +1880,7 @@ export const delete7 = oc /** * Star an application for the current account */ -export const post33 = oc +export const post29 = oc .route({ description: 'Star an application for the current account', inputStructure: 'detailed', @@ -2065,7 +1894,7 @@ export const post33 = oc export const star = { delete: delete7, - post: post33, + post: post29, } /** @@ -2298,7 +2127,7 @@ export const voices = { /** * Convert text to speech for chat messages */ -export const post34 = oc +export const post30 = oc .route({ description: 'Convert text to speech for chat messages', inputStructure: 'detailed', @@ -2313,7 +2142,7 @@ export const post34 = oc .output(zPostAppsByAppIdTextToAudioResponse) export const textToAudio = { - post: post34, + post: post30, voices, } @@ -2338,7 +2167,7 @@ export const get38 = oc /** * Update app tracing configuration */ -export const post35 = oc +export const post31 = oc .route({ description: 'Update app tracing configuration', inputStructure: 'detailed', @@ -2352,7 +2181,7 @@ export const post35 = oc export const trace = { get: get38, - post: post35, + post: post31, } /** @@ -2421,7 +2250,7 @@ export const patch = oc * * Create a new tracing configuration for an application */ -export const post36 = oc +export const post32 = oc .route({ description: 'Create a new tracing configuration for an application', inputStructure: 'detailed', @@ -2441,13 +2270,13 @@ export const traceConfig = { delete: delete8, get: get39, patch, - post: post36, + post: post32, } /** * Update app trigger (enable/disable) */ -export const post37 = oc +export const post33 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -2465,7 +2294,7 @@ export const post37 = oc .output(zPostAppsByAppIdTriggerEnableResponse) export const triggerEnable = { - post: post37, + post: post33, } /** @@ -2573,7 +2402,7 @@ export const count3 = { * * Stop running workflow task */ -export const post38 = oc +export const post34 = oc .route({ description: 'Stop running workflow task', inputStructure: 'detailed', @@ -2587,7 +2416,7 @@ export const post38 = oc .output(zPostAppsByAppIdWorkflowRunsTasksByTaskIdStopResponse) export const stop3 = { - post: post38, + post: post34, } export const byTaskId3 = { @@ -2690,7 +2519,7 @@ export const read = { /** * Upload one workflow Agent sandbox file as a Dify ToolFile mapping */ -export const post39 = oc +export const post35 = oc .route({ description: 'Upload one workflow Agent sandbox file as a Dify ToolFile mapping', inputStructure: 'detailed', @@ -2708,7 +2537,7 @@ export const post39 = oc .output(zPostAppsByAppIdWorkflowRunsByWorkflowRunIdAgentNodesByNodeIdSandboxFilesUploadResponse) export const upload2 = { - post: post39, + post: post35, } /** @@ -2742,12 +2571,12 @@ export const sandbox = { files: files3, } -export const byNodeId4 = { +export const byNodeId2 = { sandbox, } export const agentNodes = { - byNodeId: byNodeId4, + byNodeId: byNodeId2, } export const byWorkflowRunId = { @@ -2859,7 +2688,7 @@ export const byReplyId = { * * Add a reply to a workflow comment */ -export const post40 = oc +export const post36 = oc .route({ description: 'Add a reply to a workflow comment', inputStructure: 'detailed', @@ -2879,7 +2708,7 @@ export const post40 = oc .output(zPostAppsByAppIdWorkflowCommentsByCommentIdRepliesResponse) export const replies = { - post: post40, + post: post36, byReplyId, } @@ -2888,7 +2717,7 @@ export const replies = { * * Resolve a workflow comment */ -export const post41 = oc +export const post37 = oc .route({ description: 'Resolve a workflow comment', inputStructure: 'detailed', @@ -2902,7 +2731,7 @@ export const post41 = oc .output(zPostAppsByAppIdWorkflowCommentsByCommentIdResolveResponse) export const resolve = { - post: post41, + post: post37, } /** @@ -2996,7 +2825,7 @@ export const get52 = oc * * Create a new workflow comment */ -export const post42 = oc +export const post38 = oc .route({ description: 'Create a new workflow comment', inputStructure: 'detailed', @@ -3017,7 +2846,7 @@ export const post42 = oc export const comments = { get: get52, - post: post42, + post: post38, mentionUsers, byCommentId, } @@ -3130,60 +2959,10 @@ export const workflow = { statistics: statistics2, } -/** - * Get default block config - * - * Get default block configuration by type - */ -export const get57 = oc - .route({ - description: 'Get default block configuration by type', - inputStructure: 'detailed', - method: 'GET', - operationId: 'getAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockType', - path: '/apps/{app_id}/workflows/default-workflow-block-configs/{block_type}', - summary: 'Get default block config', - tags: ['console'], - }) - .input( - z.object({ - params: zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypePath, - query: zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeQuery.optional(), - }), - ) - .output(zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeResponse) - -export const byBlockType = { - get: get57, -} - -/** - * Get default block config - * - * Get default block configurations for workflow - */ -export const get58 = oc - .route({ - description: 'Get default block configurations for workflow', - inputStructure: 'detailed', - method: 'GET', - operationId: 'getAppsByAppIdWorkflowsDefaultWorkflowBlockConfigs', - path: '/apps/{app_id}/workflows/default-workflow-block-configs', - summary: 'Get default block config', - tags: ['console'], - }) - .input(z.object({ params: zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsPath })) - .output(zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsResponse) - -export const defaultWorkflowBlockConfigs = { - get: get58, - byBlockType, -} - /** * Get conversation variables for workflow */ -export const get59 = oc +export const get57 = oc .route({ description: 'Get conversation variables for workflow', inputStructure: 'detailed', @@ -3198,7 +2977,7 @@ export const get59 = oc /** * Update conversation variables for workflow draft */ -export const post43 = oc +export const post39 = oc .route({ description: 'Update conversation variables for workflow draft', inputStructure: 'detailed', @@ -3216,8 +2995,8 @@ export const post43 = oc .output(zPostAppsByAppIdWorkflowsDraftConversationVariablesResponse) export const conversationVariables2 = { - get: get59, - post: post43, + get: get57, + post: post39, } /** @@ -3225,7 +3004,7 @@ export const conversationVariables2 = { * * Get environment variables for workflow */ -export const get60 = oc +export const get58 = oc .route({ description: 'Get environment variables for workflow', inputStructure: 'detailed', @@ -3241,7 +3020,7 @@ export const get60 = oc /** * Update environment variables for workflow draft */ -export const post44 = oc +export const post40 = oc .route({ description: 'Update environment variables for workflow draft', inputStructure: 'detailed', @@ -3259,14 +3038,14 @@ export const post44 = oc .output(zPostAppsByAppIdWorkflowsDraftEnvironmentVariablesResponse) export const environmentVariables = { - get: get60, - post: post44, + get: get58, + post: post40, } /** * Update draft workflow features */ -export const post45 = oc +export const post41 = oc .route({ description: 'Update draft workflow features', inputStructure: 'detailed', @@ -3284,7 +3063,7 @@ export const post45 = oc .output(zPostAppsByAppIdWorkflowsDraftFeaturesResponse) export const features = { - post: post45, + post: post41, } /** @@ -3292,7 +3071,7 @@ export const features = { * * Test human input delivery for workflow */ -export const post46 = oc +export const post42 = oc .route({ description: 'Test human input delivery for workflow', inputStructure: 'detailed', @@ -3311,7 +3090,7 @@ export const post46 = oc .output(zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdDeliveryTestResponse) export const deliveryTest = { - post: post46, + post: post42, } /** @@ -3319,7 +3098,7 @@ export const deliveryTest = { * * Get human input form preview for workflow */ -export const post47 = oc +export const post43 = oc .route({ description: 'Get human input form preview for workflow', inputStructure: 'detailed', @@ -3338,133 +3117,27 @@ export const post47 = oc .output(zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormPreviewResponse) export const preview3 = { - post: post47, -} - -/** - * Submit human input form preview - * - * Submit human input form preview for workflow - */ -export const post48 = oc - .route({ - description: 'Submit human input form preview for workflow', - inputStructure: 'detailed', - method: 'POST', - operationId: 'postAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRun', - path: '/apps/{app_id}/workflows/draft/human-input/nodes/{node_id}/form/run', - summary: 'Submit human input form preview', - tags: ['console'], - }) - .input( - z.object({ - body: zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunBody, - params: zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunPath, - }), - ) - .output(zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunResponse) - -export const run5 = { - post: post48, + post: post43, } export const form2 = { preview: preview3, - run: run5, } -export const byNodeId5 = { +export const byNodeId3 = { deliveryTest, form: form2, } -export const nodes4 = { - byNodeId: byNodeId5, +export const nodes2 = { + byNodeId: byNodeId3, } export const humanInput2 = { - nodes: nodes4, + nodes: nodes2, } -/** - * Run draft workflow iteration node - * - * Run draft workflow iteration node - */ -export const post49 = oc - .route({ - description: 'Run draft workflow iteration node', - inputStructure: 'detailed', - method: 'POST', - operationId: 'postAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRun', - path: '/apps/{app_id}/workflows/draft/iteration/nodes/{node_id}/run', - summary: 'Run draft workflow iteration node', - tags: ['console'], - }) - .input( - z.object({ - body: zPostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunBody, - params: zPostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunPath, - }), - ) - .output(zPostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunResponse) - -export const run6 = { - post: post49, -} - -export const byNodeId6 = { - run: run6, -} - -export const nodes5 = { - byNodeId: byNodeId6, -} - -export const iteration2 = { - nodes: nodes5, -} - -/** - * Run draft workflow loop node - * - * Run draft workflow loop node - */ -export const post50 = oc - .route({ - description: 'Run draft workflow loop node', - inputStructure: 'detailed', - method: 'POST', - operationId: 'postAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRun', - path: '/apps/{app_id}/workflows/draft/loop/nodes/{node_id}/run', - summary: 'Run draft workflow loop node', - tags: ['console'], - }) - .input( - z.object({ - body: zPostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunBody, - params: zPostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunPath, - }), - ) - .output(zPostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunResponse) - -export const run7 = { - post: post50, -} - -export const byNodeId7 = { - run: run7, -} - -export const nodes6 = { - byNodeId: byNodeId7, -} - -export const loop2 = { - nodes: nodes6, -} - -export const get61 = oc +export const get59 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -3478,10 +3151,10 @@ export const get61 = oc .output(zGetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCandidatesResponse) export const candidates = { - get: get61, + get: get59, } -export const post51 = oc +export const post44 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -3498,10 +3171,10 @@ export const post51 = oc .output(zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCopyFromRosterResponse) export const copyFromRoster = { - post: post51, + post: post44, } -export const post52 = oc +export const post45 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -3518,10 +3191,10 @@ export const post52 = oc .output(zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactResponse) export const impact = { - post: post52, + post: post45, } -export const post53 = oc +export const post46 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -3538,10 +3211,10 @@ export const post53 = oc .output(zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerSaveToRosterResponse) export const saveToRoster = { - post: post53, + post: post46, } -export const post54 = oc +export const post47 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -3558,10 +3231,10 @@ export const post54 = oc .output(zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerValidateResponse) export const validate = { - post: post54, + post: post47, } -export const get62 = oc +export const get60 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -3589,7 +3262,7 @@ export const put4 = oc .output(zPutAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerResponse) export const agentComposer = { - get: get62, + get: get60, put: put4, candidates, copyFromRoster, @@ -3601,7 +3274,7 @@ export const agentComposer = { /** * Get last run result for draft workflow node */ -export const get63 = oc +export const get61 = oc .route({ description: 'Get last run result for draft workflow node', inputStructure: 'detailed', @@ -3614,7 +3287,7 @@ export const get63 = oc .output(zGetAppsByAppIdWorkflowsDraftNodesByNodeIdLastRunResponse) export const lastRun = { - get: get63, + get: get61, } /** @@ -3622,7 +3295,7 @@ export const lastRun = { * * Run draft workflow node */ -export const post55 = oc +export const post48 = oc .route({ description: 'Run draft workflow node', inputStructure: 'detailed', @@ -3640,8 +3313,8 @@ export const post55 = oc ) .output(zPostAppsByAppIdWorkflowsDraftNodesByNodeIdRunResponse) -export const run8 = { - post: post55, +export const run = { + post: post48, } /** @@ -3649,7 +3322,7 @@ export const run8 = { * * Poll for trigger events and execute single node when event arrives */ -export const post56 = oc +export const post49 = oc .route({ description: 'Poll for trigger events and execute single node when event arrives', inputStructure: 'detailed', @@ -3662,12 +3335,12 @@ export const post56 = oc .input(z.object({ params: zPostAppsByAppIdWorkflowsDraftNodesByNodeIdTriggerRunPath })) .output(zPostAppsByAppIdWorkflowsDraftNodesByNodeIdTriggerRunResponse) -export const run9 = { - post: post56, +export const run2 = { + post: post49, } export const trigger = { - run: run9, + run: run2, } /** @@ -3689,7 +3362,7 @@ export const delete11 = oc /** * Get variables for a specific node */ -export const get64 = oc +export const get62 = oc .route({ description: 'Get variables for a specific node', inputStructure: 'detailed', @@ -3703,52 +3376,25 @@ export const get64 = oc export const variables = { delete: delete11, - get: get64, + get: get62, } -export const byNodeId8 = { +export const byNodeId4 = { agentComposer, lastRun, - run: run8, + run, trigger, variables, } -export const nodes7 = { - byNodeId: byNodeId8, -} - -/** - * Run draft workflow - * - * Run draft workflow - */ -export const post57 = oc - .route({ - description: 'Run draft workflow', - inputStructure: 'detailed', - method: 'POST', - operationId: 'postAppsByAppIdWorkflowsDraftRun', - path: '/apps/{app_id}/workflows/draft/run', - summary: 'Run draft workflow', - tags: ['console'], - }) - .input( - z.object({ - body: zPostAppsByAppIdWorkflowsDraftRunBody, - params: zPostAppsByAppIdWorkflowsDraftRunPath, - }), - ) - .output(zPostAppsByAppIdWorkflowsDraftRunResponse) - -export const run10 = { - post: post57, +export const nodes3 = { + byNodeId: byNodeId4, } /** * Server-Sent Events stream of inspector deltas for a draft workflow run. */ -export const get65 = oc +export const get63 = oc .route({ description: 'Server-Sent Events stream of inspector deltas for a draft workflow run.', inputStructure: 'detailed', @@ -3761,13 +3407,13 @@ export const get65 = oc .output(zGetAppsByAppIdWorkflowsDraftRunsByRunIdNodeOutputsEventsResponse) export const events = { - get: get65, + get: get63, } /** * Full value for one declared output, including signed download URL for files. */ -export const get66 = oc +export const get64 = oc .route({ description: 'Full value for one declared output, including signed download URL for files.', inputStructure: 'detailed', @@ -3784,7 +3430,7 @@ export const get66 = oc .output(zGetAppsByAppIdWorkflowsDraftRunsByRunIdNodeOutputsByNodeIdByOutputNamePreviewResponse) export const preview4 = { - get: get66, + get: get64, } export const byOutputName = { @@ -3794,7 +3440,7 @@ export const byOutputName = { /** * One node's declared outputs for a draft workflow run. */ -export const get67 = oc +export const get65 = oc .route({ description: 'One node\'s declared outputs for a draft workflow run.', inputStructure: 'detailed', @@ -3806,15 +3452,15 @@ export const get67 = oc .input(z.object({ params: zGetAppsByAppIdWorkflowsDraftRunsByRunIdNodeOutputsByNodeIdPath })) .output(zGetAppsByAppIdWorkflowsDraftRunsByRunIdNodeOutputsByNodeIdResponse) -export const byNodeId9 = { - get: get67, +export const byNodeId5 = { + get: get65, byOutputName, } /** * Snapshot of every node's declared outputs for a draft workflow run. */ -export const get68 = oc +export const get66 = oc .route({ description: 'Snapshot of every node\'s declared outputs for a draft workflow run.', inputStructure: 'detailed', @@ -3827,9 +3473,9 @@ export const get68 = oc .output(zGetAppsByAppIdWorkflowsDraftRunsByRunIdNodeOutputsResponse) export const nodeOutputs = { - get: get68, + get: get66, events, - byNodeId: byNodeId9, + byNodeId: byNodeId5, } export const byRunId2 = { @@ -3843,7 +3489,7 @@ export const runs = { /** * Get system variables for workflow */ -export const get69 = oc +export const get67 = oc .route({ description: 'Get system variables for workflow', inputStructure: 'detailed', @@ -3856,66 +3502,7 @@ export const get69 = oc .output(zGetAppsByAppIdWorkflowsDraftSystemVariablesResponse) export const systemVariables = { - get: get69, -} - -/** - * Poll for trigger events and execute full workflow when event arrives - * - * Poll for trigger events and execute full workflow when event arrives - */ -export const post58 = oc - .route({ - description: 'Poll for trigger events and execute full workflow when event arrives', - inputStructure: 'detailed', - method: 'POST', - operationId: 'postAppsByAppIdWorkflowsDraftTriggerRun', - path: '/apps/{app_id}/workflows/draft/trigger/run', - summary: 'Poll for trigger events and execute full workflow when event arrives', - tags: ['console'], - }) - .input( - z.object({ - body: zPostAppsByAppIdWorkflowsDraftTriggerRunBody, - params: zPostAppsByAppIdWorkflowsDraftTriggerRunPath, - }), - ) - .output(zPostAppsByAppIdWorkflowsDraftTriggerRunResponse) - -export const run11 = { - post: post58, -} - -/** - * Full workflow debug when the start node is a trigger - * - * Full workflow debug when the start node is a trigger - */ -export const post59 = oc - .route({ - description: 'Full workflow debug when the start node is a trigger', - inputStructure: 'detailed', - method: 'POST', - operationId: 'postAppsByAppIdWorkflowsDraftTriggerRunAll', - path: '/apps/{app_id}/workflows/draft/trigger/run-all', - summary: 'Full workflow debug when the start node is a trigger', - tags: ['console'], - }) - .input( - z.object({ - body: zPostAppsByAppIdWorkflowsDraftTriggerRunAllBody, - params: zPostAppsByAppIdWorkflowsDraftTriggerRunAllPath, - }), - ) - .output(zPostAppsByAppIdWorkflowsDraftTriggerRunAllResponse) - -export const runAll = { - post: post59, -} - -export const trigger2 = { - run: run11, - runAll, + get: get67, } /** @@ -3956,7 +3543,7 @@ export const delete12 = oc /** * Get a specific workflow variable */ -export const get70 = oc +export const get68 = oc .route({ description: 'Get a specific workflow variable', inputStructure: 'detailed', @@ -3990,7 +3577,7 @@ export const patch2 = oc export const byVariableId = { delete: delete12, - get: get70, + get: get68, patch: patch2, reset, } @@ -4016,7 +3603,7 @@ export const delete13 = oc * * Get draft workflow variables */ -export const get71 = oc +export const get69 = oc .route({ description: 'Get draft workflow variables', inputStructure: 'detailed', @@ -4036,7 +3623,7 @@ export const get71 = oc export const variables2 = { delete: delete13, - get: get71, + get: get69, byVariableId, } @@ -4045,7 +3632,7 @@ export const variables2 = { * * Get draft workflow for an application */ -export const get72 = oc +export const get70 = oc .route({ description: 'Get draft workflow for an application', inputStructure: 'detailed', @@ -4063,7 +3650,7 @@ export const get72 = oc * * Sync draft workflow configuration */ -export const post60 = oc +export const post50 = oc .route({ description: 'Sync draft workflow configuration', inputStructure: 'detailed', @@ -4082,19 +3669,15 @@ export const post60 = oc .output(zPostAppsByAppIdWorkflowsDraftResponse) export const draft2 = { - get: get72, - post: post60, + get: get70, + post: post50, conversationVariables: conversationVariables2, environmentVariables, features, humanInput: humanInput2, - iteration: iteration2, - loop: loop2, - nodes: nodes7, - run: run10, + nodes: nodes3, runs, systemVariables, - trigger: trigger2, variables: variables2, } @@ -4103,7 +3686,7 @@ export const draft2 = { * * Get published workflow for an application */ -export const get73 = oc +export const get71 = oc .route({ description: 'Get published workflow for an application', inputStructure: 'detailed', @@ -4119,7 +3702,7 @@ export const get73 = oc /** * Publish workflow */ -export const post61 = oc +export const post51 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -4137,14 +3720,14 @@ export const post61 = oc .output(zPostAppsByAppIdWorkflowsPublishResponse) export const publish = { - get: get73, - post: post61, + get: get71, + post: post51, } /** * Server-Sent Events stream of inspector deltas for a published workflow run. */ -export const get74 = oc +export const get72 = oc .route({ description: 'Server-Sent Events stream of inspector deltas for a published workflow run.', inputStructure: 'detailed', @@ -4157,13 +3740,13 @@ export const get74 = oc .output(zGetAppsByAppIdWorkflowsPublishedRunsByRunIdNodeOutputsEventsResponse) export const events2 = { - get: get74, + get: get72, } /** * Full value for one declared output of a published run. */ -export const get75 = oc +export const get73 = oc .route({ description: 'Full value for one declared output of a published run.', inputStructure: 'detailed', @@ -4184,7 +3767,7 @@ export const get75 = oc ) export const preview5 = { - get: get75, + get: get73, } export const byOutputName2 = { @@ -4194,7 +3777,7 @@ export const byOutputName2 = { /** * One node's declared outputs for a published workflow run. */ -export const get76 = oc +export const get74 = oc .route({ description: 'One node\'s declared outputs for a published workflow run.', inputStructure: 'detailed', @@ -4206,15 +3789,15 @@ export const get76 = oc .input(z.object({ params: zGetAppsByAppIdWorkflowsPublishedRunsByRunIdNodeOutputsByNodeIdPath })) .output(zGetAppsByAppIdWorkflowsPublishedRunsByRunIdNodeOutputsByNodeIdResponse) -export const byNodeId10 = { - get: get76, +export const byNodeId6 = { + get: get74, byOutputName: byOutputName2, } /** * Snapshot of every node's declared outputs for a published workflow run. */ -export const get77 = oc +export const get75 = oc .route({ description: 'Snapshot of every node\'s declared outputs for a published workflow run.', inputStructure: 'detailed', @@ -4227,9 +3810,9 @@ export const get77 = oc .output(zGetAppsByAppIdWorkflowsPublishedRunsByRunIdNodeOutputsResponse) export const nodeOutputs2 = { - get: get77, + get: get75, events: events2, - byNodeId: byNodeId10, + byNodeId: byNodeId6, } export const byRunId3 = { @@ -4247,7 +3830,7 @@ export const published = { /** * Get webhook trigger for a node */ -export const get78 = oc +export const get76 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -4265,7 +3848,7 @@ export const get78 = oc .output(zGetAppsByAppIdWorkflowsTriggersWebhookResponse) export const webhook = { - get: get78, + get: get76, } export const triggers2 = { @@ -4275,7 +3858,7 @@ export const triggers2 = { /** * Restore a published workflow version into the draft workflow */ -export const post62 = oc +export const post52 = oc .route({ description: 'Restore a published workflow version into the draft workflow', inputStructure: 'detailed', @@ -4288,7 +3871,7 @@ export const post62 = oc .output(zPostAppsByAppIdWorkflowsByWorkflowIdRestoreResponse) export const restore = { - post: post62, + post: post52, } /** @@ -4341,7 +3924,7 @@ export const byWorkflowId = { * * Get all published workflows for an application */ -export const get79 = oc +export const get77 = oc .route({ description: 'Get all published workflows for an application', inputStructure: 'detailed', @@ -4360,8 +3943,7 @@ export const get79 = oc .output(zGetAppsByAppIdWorkflowsResponse) export const workflows3 = { - get: get79, - defaultWorkflowBlockConfigs, + get: get77, draft: draft2, publish, published, @@ -4393,7 +3975,7 @@ export const delete15 = oc * * Get application details */ -export const get80 = oc +export const get78 = oc .route({ description: 'Get application details', inputStructure: 'detailed', @@ -4426,7 +4008,7 @@ export const put6 = oc export const byAppId2 = { delete: delete15, - get: get80, + get: get78, put: put6, advancedChat, agent, @@ -4495,7 +4077,7 @@ export const byApiKeyId = { * * Get all API keys for an app */ -export const get81 = oc +export const get79 = oc .route({ description: 'Get all API keys for an app', inputStructure: 'detailed', @@ -4513,7 +4095,7 @@ export const get81 = oc * * Create a new API key for an app */ -export const post63 = oc +export const post53 = oc .route({ description: 'Create a new API key for an app', inputStructure: 'detailed', @@ -4528,8 +4110,8 @@ export const post63 = oc .output(zPostAppsByResourceIdApiKeysResponse) export const apiKeys = { - get: get81, - post: post63, + get: get79, + post: post53, byApiKeyId, } @@ -4540,7 +4122,7 @@ export const byResourceId = { /** * Refresh MCP server configuration and regenerate server code */ -export const get82 = oc +export const get80 = oc .route({ description: 'Refresh MCP server configuration and regenerate server code', inputStructure: 'detailed', @@ -4553,7 +4135,7 @@ export const get82 = oc .output(zGetAppsByServerIdServerRefreshResponse) export const refresh = { - get: get82, + get: get80, } export const server2 = { @@ -4569,7 +4151,7 @@ export const byServerId = { * * Get list of applications with pagination and filtering */ -export const get83 = oc +export const get81 = oc .route({ description: 'Get list of applications with pagination and filtering', inputStructure: 'detailed', @@ -4587,7 +4169,7 @@ export const get83 = oc * * Create a new application */ -export const post64 = oc +export const post54 = oc .route({ description: 'Create a new application', inputStructure: 'detailed', @@ -4602,8 +4184,8 @@ export const post64 = oc .output(zPostAppsResponse) export const apps = { - get: get83, - post: post64, + get: get81, + post: post54, imports, starred, workflows, diff --git a/packages/contracts/generated/api/console/apps/types.gen.ts b/packages/contracts/generated/api/console/apps/types.gen.ts index 9e79518f3cd..9c4e02e0fb0 100644 --- a/packages/contracts/generated/api/console/apps/types.gen.ts +++ b/packages/contracts/generated/api/console/apps/types.gen.ts @@ -119,10 +119,9 @@ export type HumanInputFormPreviewPayload = { } export type HumanInputFormPreviewResponse = { - actions?: Array<{ - [key: string]: unknown - }> - display_in_ui?: boolean | null + TYPE?: 'human_input_required' + actions?: Array + display_in_ui?: boolean expiration_time?: number | null form_content: string form_id: string @@ -137,46 +136,6 @@ export type HumanInputFormPreviewResponse = { } } -export type HumanInputFormSubmitPayload = { - action: string - form_inputs: { - [key: string]: unknown - } - inputs: { - [key: string]: unknown - } -} - -export type HumanInputFormSubmitResponse = { - [key: string]: unknown -} - -export type IterationNodeRunPayload = { - inputs?: { - [key: string]: unknown - } | null -} - -export type GeneratedAppResponse = JsonValue - -export type LoopNodeRunPayload = { - inputs?: { - [key: string]: unknown - } | null -} - -export type AdvancedChatWorkflowRunPayload = { - conversation_id?: string | null - files?: Array<{ - [key: string]: unknown - }> | null - inputs?: { - [key: string]: unknown - } | null - parent_message_id?: string | null - query?: string -} - export type AgentDriveListResponse = { items?: Array } @@ -422,6 +381,8 @@ export type CompletionMessagePayload = { retriever_from?: string } +export type GeneratedAppResponse = JsonValue + export type PaginatedConversationVariableResponse = { data: Array has_more: boolean @@ -731,7 +692,7 @@ export type WorkflowRunPaginationResponse = { export type WorkflowRunDetailResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null created_by_role?: string | null elapsed_time?: number | null @@ -799,7 +760,7 @@ export type WorkflowCommentCreate = { } export type WorkflowCommentMentionUsersPayload = { - users: Array + users: Array } export type WorkflowCommentDetail = { @@ -876,18 +837,10 @@ export type WorkflowPaginationResponse = { page: number } -export type DefaultBlockConfigsResponse = Array<{ - [key: string]: unknown -}> - -export type DefaultBlockConfigResponse = { - [key: string]: unknown -} - export type WorkflowResponse = { conversation_variables: Array created_at: number - created_by?: SimpleAccount | null + created_by?: SimpleAccountResponse | null environment_variables: Array features: { [key: string]: unknown @@ -902,7 +855,7 @@ export type WorkflowResponse = { rag_pipeline_variables: Array tool_published: boolean updated_at: number - updated_by?: SimpleAccount | null + updated_by?: SimpleAccountResponse | null version: string } @@ -923,13 +876,13 @@ export type SyncDraftWorkflowPayload = { } export type SyncDraftWorkflowResponse = { - hash?: string - result?: string - updated_at?: string + hash: string + result: string + updated_at: number } -export type WorkflowDraftVariableList = { - items?: Array +export type WorkflowDraftVariableListResponse = { + items: Array } export type ConversationVariableUpdatePayload = { @@ -938,8 +891,8 @@ export type ConversationVariableUpdatePayload = { }> } -export type EnvironmentVariableListResponse = { - items: Array +export type WorkflowDraftEnvironmentVariableListResponse = { + items: Array } export type EnvironmentVariableUpdatePayload = { @@ -961,7 +914,7 @@ export type HumanInputDeliveryTestPayload = { } } -export type EmptyObjectResponse = { +export type HumanInputDeliveryTestResponse = { [key: string]: unknown } @@ -1029,7 +982,7 @@ export type AgentComposerValidateResponse = { export type WorkflowRunNodeExecutionResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null created_by_role?: string | null elapsed_time?: number | null @@ -1062,17 +1015,6 @@ export type DraftWorkflowNodeRunPayload = { query?: string } -export type DraftWorkflowRunPayload = { - datasource_info_list: Array<{ - [key: string]: unknown - }> - datasource_type: string - inputs: { - [key: string]: unknown - } - start_node_id: string -} - export type WorkflowRunSnapshotView = { node_outputs?: Array workflow_run_id: string @@ -1099,56 +1041,36 @@ export type OutputPreviewView = { value?: unknown } -export type DraftWorkflowTriggerRunRequest = { - node_id: string +export type WorkflowDraftVariableListWithoutValueResponse = { + items: Array + total: number | null } -export type DraftWorkflowTriggerRunAllPayload = { - node_ids: Array -} - -export type WorkflowDraftVariableListWithoutValue = { - items?: Array - total?: number -} - -export type WorkflowDraftVariable = { - description?: string - edited?: boolean - full_content?: { - [key: string]: unknown - } - id?: string - is_truncated?: boolean - name?: string - selector?: Array - type?: string - value?: - | string - | number - | number - | boolean - | { - [key: string]: unknown - } - | Array - | null - value_type?: string - visible?: boolean +export type WorkflowDraftVariableResponse = { + description: string + edited: boolean + full_content: WorkflowDraftVariableFullContentResponse | null + id: string + is_truncated: boolean + name: string + selector: Array + type: string + value: unknown + value_type: string + visible: boolean } export type WorkflowDraftVariableUpdatePayload = { name?: string | null - value?: unknown | null + value?: unknown } export type PublishWorkflowPayload = { - knowledge_base_setting?: { - [key: string]: unknown - } | null + marked_comment?: string | null + marked_name?: string | null } -export type WorkflowPublishResponse = { +export type PublishWorkflowResponse = { created_at: number result: string } @@ -1167,12 +1089,6 @@ export type WorkflowUpdatePayload = { marked_name?: string | null } -export type WorkflowRestoreResponse = { - hash: string - result: string - updated_at: number -} - export type ApiKeyList = { data: Array } @@ -1289,7 +1205,7 @@ export type WorkflowOnlineUsersByApp = { export type AdvancedChatWorkflowRunForListResponse = { conversation_id?: string | null created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null elapsed_time?: number | null exceptions_count?: number | null finished_at?: number | null @@ -1302,6 +1218,12 @@ export type AdvancedChatWorkflowRunForListResponse = { version?: string | null } +export type HumanInputUserActionResponse = { + button_style?: string + id: string + title: string +} + export type AgentDriveItemResponse = { created_at?: number | null file_kind: string @@ -1596,7 +1518,7 @@ export type UserSatisfactionRateStatisticItem = { export type WorkflowAppLogPartialResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null created_by_role?: string | null created_from?: string | null @@ -1607,7 +1529,7 @@ export type WorkflowAppLogPartialResponse = { export type WorkflowArchivedLogPartialResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null id: string trigger_metadata?: unknown @@ -1616,7 +1538,7 @@ export type WorkflowArchivedLogPartialResponse = { export type WorkflowRunForListResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null elapsed_time?: number | null exceptions_count?: number | null finished_at?: number | null @@ -1628,7 +1550,7 @@ export type WorkflowRunForListResponse = { version?: string | null } -export type SimpleAccount = { +export type SimpleAccountResponse = { email: string id: string name: string @@ -1671,7 +1593,7 @@ export type WorkflowCommentBasic = { updated_at?: number | null } -export type AccountWithRole = { +export type AccountWithRoleResponse = { avatar?: string | null created_at?: number | null email: string @@ -1760,7 +1682,7 @@ export type PipelineVariableResponse = { variable: string } -export type EnvironmentVariableItemResponse = { +export type WorkflowDraftEnvironmentVariableResponse = { description?: string | null editable: boolean edited: boolean @@ -1961,16 +1883,23 @@ export type NodeOutputStatus export type DeclaredOutputType = 'array' | 'boolean' | 'file' | 'number' | 'object' | 'string' -export type WorkflowDraftVariableWithoutValue = { - description?: string - edited?: boolean - id?: string - is_truncated?: boolean - name?: string - selector?: Array - type?: string - value_type?: string - visible?: boolean +export type WorkflowDraftVariableWithoutValueResponse = { + description: string + edited: boolean + id: string + is_truncated: boolean + name: string + selector: Array + type: string + value_type: string + visible: boolean +} + +export type WorkflowDraftVariableFullContentResponse = { + download_url: string + length: number | null + size_bytes: number | null + value_type: string } export type ModelConfigPartial = { @@ -2054,6 +1983,12 @@ export type SimpleMessageDetail = { query: string } +export type SimpleAccount = { + email: string + id: string + name: string +} + export type HumanInputFormDefinition = { actions?: Array display_in_ui?: boolean @@ -2654,6 +2589,10 @@ export type WorkflowCommentDetailWritable = { updated_at?: number | null } +export type HumanInputDeliveryTestResponseWritable = { + [key: string]: unknown +} + export type AppPartialWritable = { access_mode?: string | null app_id?: string | null @@ -2999,88 +2938,6 @@ export type PostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdForm export type PostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormPreviewResponse = PostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormPreviewResponses[keyof PostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormPreviewResponses] -export type PostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunData = { - body: HumanInputFormSubmitPayload - path: { - app_id: string - node_id: string - } - query?: never - url: '/apps/{app_id}/advanced-chat/workflows/draft/human-input/nodes/{node_id}/form/run' -} - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunResponses = { - 200: HumanInputFormSubmitResponse -} - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunResponse - = PostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunResponses[keyof PostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunResponses] - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunData = { - body: IterationNodeRunPayload - path: { - app_id: string - node_id: string - } - query?: never - url: '/apps/{app_id}/advanced-chat/workflows/draft/iteration/nodes/{node_id}/run' -} - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunErrors = { - 403: unknown - 404: unknown -} - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunResponses = { - 200: GeneratedAppResponse -} - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunResponse - = PostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunResponses[keyof PostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunResponses] - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunData = { - body: LoopNodeRunPayload - path: { - app_id: string - node_id: string - } - query?: never - url: '/apps/{app_id}/advanced-chat/workflows/draft/loop/nodes/{node_id}/run' -} - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunErrors = { - 403: unknown - 404: unknown -} - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunResponses = { - 200: GeneratedAppResponse -} - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunResponse - = PostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunResponses[keyof PostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunResponses] - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftRunData = { - body: AdvancedChatWorkflowRunPayload - path: { - app_id: string - } - query?: never - url: '/apps/{app_id}/advanced-chat/workflows/draft/run' -} - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftRunErrors = { - 400: unknown - 403: unknown -} - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftRunResponses = { - 200: GeneratedAppResponse -} - -export type PostAppsByAppIdAdvancedChatWorkflowsDraftRunResponse - = PostAppsByAppIdAdvancedChatWorkflowsDraftRunResponses[keyof PostAppsByAppIdAdvancedChatWorkflowsDraftRunResponses] - export type GetAppsByAppIdAgentDriveFilesData = { body?: never path: { @@ -5114,45 +4971,6 @@ export type GetAppsByAppIdWorkflowsResponses = { export type GetAppsByAppIdWorkflowsResponse = GetAppsByAppIdWorkflowsResponses[keyof GetAppsByAppIdWorkflowsResponses] -export type GetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsData = { - body?: never - path: { - app_id: string - } - query?: never - url: '/apps/{app_id}/workflows/default-workflow-block-configs' -} - -export type GetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsResponses = { - 200: DefaultBlockConfigsResponse -} - -export type GetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsResponse - = GetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsResponses[keyof GetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsResponses] - -export type GetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeData = { - body?: never - path: { - app_id: string - block_type: string - } - query?: { - q?: string - } - url: '/apps/{app_id}/workflows/default-workflow-block-configs/{block_type}' -} - -export type GetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeErrors = { - 404: unknown -} - -export type GetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeResponses = { - 200: DefaultBlockConfigResponse -} - -export type GetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeResponse - = GetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeResponses[keyof GetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeResponses] - export type GetAppsByAppIdWorkflowsDraftData = { body?: never path: { @@ -5208,7 +5026,7 @@ export type GetAppsByAppIdWorkflowsDraftConversationVariablesErrors = { } export type GetAppsByAppIdWorkflowsDraftConversationVariablesResponses = { - 200: WorkflowDraftVariableList + 200: WorkflowDraftVariableListResponse } export type GetAppsByAppIdWorkflowsDraftConversationVariablesResponse @@ -5244,7 +5062,7 @@ export type GetAppsByAppIdWorkflowsDraftEnvironmentVariablesErrors = { } export type GetAppsByAppIdWorkflowsDraftEnvironmentVariablesResponses = { - 200: EnvironmentVariableListResponse + 200: WorkflowDraftEnvironmentVariableListResponse } export type GetAppsByAppIdWorkflowsDraftEnvironmentVariablesResponse @@ -5293,7 +5111,7 @@ export type PostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdDeliveryTestData } export type PostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdDeliveryTestResponses = { - 200: EmptyObjectResponse + 200: HumanInputDeliveryTestResponse } export type PostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdDeliveryTestResponse @@ -5316,67 +5134,6 @@ export type PostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormPreviewRespo export type PostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormPreviewResponse = PostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormPreviewResponses[keyof PostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormPreviewResponses] -export type PostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunData = { - body: HumanInputFormSubmitPayload - path: { - app_id: string - node_id: string - } - query?: never - url: '/apps/{app_id}/workflows/draft/human-input/nodes/{node_id}/form/run' -} - -export type PostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunResponses = { - 200: HumanInputFormSubmitResponse -} - -export type PostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunResponse - = PostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunResponses[keyof PostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunResponses] - -export type PostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunData = { - body: IterationNodeRunPayload - path: { - app_id: string - node_id: string - } - query?: never - url: '/apps/{app_id}/workflows/draft/iteration/nodes/{node_id}/run' -} - -export type PostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunErrors = { - 403: unknown - 404: unknown -} - -export type PostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunResponses = { - 200: GeneratedAppResponse -} - -export type PostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunResponse - = PostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunResponses[keyof PostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunResponses] - -export type PostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunData = { - body: LoopNodeRunPayload - path: { - app_id: string - node_id: string - } - query?: never - url: '/apps/{app_id}/workflows/draft/loop/nodes/{node_id}/run' -} - -export type PostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunErrors = { - 403: unknown - 404: unknown -} - -export type PostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunResponses = { - 200: GeneratedAppResponse -} - -export type PostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunResponse - = PostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunResponses[keyof PostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunResponses] - export type GetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerData = { body?: never path: { @@ -5556,7 +5313,7 @@ export type PostAppsByAppIdWorkflowsDraftNodesByNodeIdTriggerRunErrors = { } export type PostAppsByAppIdWorkflowsDraftNodesByNodeIdTriggerRunResponses = { - 200: GeneratedAppResponse + 200: WorkflowRunNodeExecutionResponse } export type PostAppsByAppIdWorkflowsDraftNodesByNodeIdTriggerRunResponse @@ -5590,32 +5347,12 @@ export type GetAppsByAppIdWorkflowsDraftNodesByNodeIdVariablesData = { } export type GetAppsByAppIdWorkflowsDraftNodesByNodeIdVariablesResponses = { - 200: WorkflowDraftVariableList + 200: WorkflowDraftVariableListResponse } export type GetAppsByAppIdWorkflowsDraftNodesByNodeIdVariablesResponse = GetAppsByAppIdWorkflowsDraftNodesByNodeIdVariablesResponses[keyof GetAppsByAppIdWorkflowsDraftNodesByNodeIdVariablesResponses] -export type PostAppsByAppIdWorkflowsDraftRunData = { - body: DraftWorkflowRunPayload - path: { - app_id: string - } - query?: never - url: '/apps/{app_id}/workflows/draft/run' -} - -export type PostAppsByAppIdWorkflowsDraftRunErrors = { - 403: unknown -} - -export type PostAppsByAppIdWorkflowsDraftRunResponses = { - 200: GeneratedAppResponse -} - -export type PostAppsByAppIdWorkflowsDraftRunResponse - = PostAppsByAppIdWorkflowsDraftRunResponses[keyof PostAppsByAppIdWorkflowsDraftRunResponses] - export type GetAppsByAppIdWorkflowsDraftRunsByRunIdNodeOutputsData = { body?: never path: { @@ -5714,54 +5451,12 @@ export type GetAppsByAppIdWorkflowsDraftSystemVariablesData = { } export type GetAppsByAppIdWorkflowsDraftSystemVariablesResponses = { - 200: WorkflowDraftVariableList + 200: WorkflowDraftVariableListResponse } export type GetAppsByAppIdWorkflowsDraftSystemVariablesResponse = GetAppsByAppIdWorkflowsDraftSystemVariablesResponses[keyof GetAppsByAppIdWorkflowsDraftSystemVariablesResponses] -export type PostAppsByAppIdWorkflowsDraftTriggerRunData = { - body: DraftWorkflowTriggerRunRequest - path: { - app_id: string - } - query?: never - url: '/apps/{app_id}/workflows/draft/trigger/run' -} - -export type PostAppsByAppIdWorkflowsDraftTriggerRunErrors = { - 403: unknown - 500: unknown -} - -export type PostAppsByAppIdWorkflowsDraftTriggerRunResponses = { - 200: GeneratedAppResponse -} - -export type PostAppsByAppIdWorkflowsDraftTriggerRunResponse - = PostAppsByAppIdWorkflowsDraftTriggerRunResponses[keyof PostAppsByAppIdWorkflowsDraftTriggerRunResponses] - -export type PostAppsByAppIdWorkflowsDraftTriggerRunAllData = { - body: DraftWorkflowTriggerRunAllPayload - path: { - app_id: string - } - query?: never - url: '/apps/{app_id}/workflows/draft/trigger/run-all' -} - -export type PostAppsByAppIdWorkflowsDraftTriggerRunAllErrors = { - 403: unknown - 500: unknown -} - -export type PostAppsByAppIdWorkflowsDraftTriggerRunAllResponses = { - 200: GeneratedAppResponse -} - -export type PostAppsByAppIdWorkflowsDraftTriggerRunAllResponse - = PostAppsByAppIdWorkflowsDraftTriggerRunAllResponses[keyof PostAppsByAppIdWorkflowsDraftTriggerRunAllResponses] - export type DeleteAppsByAppIdWorkflowsDraftVariablesData = { body?: never path: { @@ -5791,7 +5486,7 @@ export type GetAppsByAppIdWorkflowsDraftVariablesData = { } export type GetAppsByAppIdWorkflowsDraftVariablesResponses = { - 200: WorkflowDraftVariableListWithoutValue + 200: WorkflowDraftVariableListWithoutValueResponse } export type GetAppsByAppIdWorkflowsDraftVariablesResponse @@ -5833,7 +5528,7 @@ export type GetAppsByAppIdWorkflowsDraftVariablesByVariableIdErrors = { } export type GetAppsByAppIdWorkflowsDraftVariablesByVariableIdResponses = { - 200: WorkflowDraftVariable + 200: WorkflowDraftVariableResponse } export type GetAppsByAppIdWorkflowsDraftVariablesByVariableIdResponse @@ -5854,7 +5549,7 @@ export type PatchAppsByAppIdWorkflowsDraftVariablesByVariableIdErrors = { } export type PatchAppsByAppIdWorkflowsDraftVariablesByVariableIdResponses = { - 200: WorkflowDraftVariable + 200: WorkflowDraftVariableResponse } export type PatchAppsByAppIdWorkflowsDraftVariablesByVariableIdResponse @@ -5875,7 +5570,7 @@ export type PutAppsByAppIdWorkflowsDraftVariablesByVariableIdResetErrors = { } export type PutAppsByAppIdWorkflowsDraftVariablesByVariableIdResetResponses = { - 200: WorkflowDraftVariable + 200: WorkflowDraftVariableResponse 204: void } @@ -5908,7 +5603,7 @@ export type PostAppsByAppIdWorkflowsPublishData = { } export type PostAppsByAppIdWorkflowsPublishResponses = { - 200: WorkflowPublishResponse + 200: PublishWorkflowResponse } export type PostAppsByAppIdWorkflowsPublishResponse @@ -6077,7 +5772,7 @@ export type PostAppsByAppIdWorkflowsByWorkflowIdRestoreErrors = { } export type PostAppsByAppIdWorkflowsByWorkflowIdRestoreResponses = { - 200: WorkflowRestoreResponse + 200: SyncDraftWorkflowResponse } export type PostAppsByAppIdWorkflowsByWorkflowIdRestoreResponse diff --git a/packages/contracts/generated/api/console/apps/zod.gen.ts b/packages/contracts/generated/api/console/apps/zod.gen.ts index 9b86fda0a62..c38cae58a03 100644 --- a/packages/contracts/generated/api/console/apps/zod.gen.ts +++ b/packages/contracts/generated/api/console/apps/zod.gen.ts @@ -43,61 +43,6 @@ export const zHumanInputFormPreviewPayload = z.object({ inputs: z.record(z.string(), z.unknown()).optional(), }) -/** - * HumanInputFormPreviewResponse - */ -export const zHumanInputFormPreviewResponse = z.object({ - actions: z.array(z.record(z.string(), z.unknown())).optional(), - display_in_ui: z.boolean().nullish(), - expiration_time: z.int().nullish(), - form_content: z.string(), - form_id: z.string(), - form_token: z.string().nullish(), - inputs: z.array(z.record(z.string(), z.unknown())).optional(), - node_id: z.string(), - node_title: z.string(), - resolved_default_values: z.record(z.string(), z.unknown()).optional(), -}) - -/** - * HumanInputFormSubmitPayload - */ -export const zHumanInputFormSubmitPayload = z.object({ - action: z.string(), - form_inputs: z.record(z.string(), z.unknown()), - inputs: z.record(z.string(), z.unknown()), -}) - -/** - * HumanInputFormSubmitResponse - */ -export const zHumanInputFormSubmitResponse = z.record(z.string(), z.unknown()) - -/** - * IterationNodeRunPayload - */ -export const zIterationNodeRunPayload = z.object({ - inputs: z.record(z.string(), z.unknown()).nullish(), -}) - -/** - * LoopNodeRunPayload - */ -export const zLoopNodeRunPayload = z.object({ - inputs: z.record(z.string(), z.unknown()).nullish(), -}) - -/** - * AdvancedChatWorkflowRunPayload - */ -export const zAdvancedChatWorkflowRunPayload = z.object({ - conversation_id: z.string().nullish(), - files: z.array(z.record(z.string(), z.unknown())).nullish(), - inputs: z.record(z.string(), z.unknown()).nullish(), - parent_message_id: z.string().nullish(), - query: z.string().optional().default(''), -}) - /** * AgentDriveDownloadResponse */ @@ -581,16 +526,6 @@ export const zWorkflowCommentResolve = z.object({ resolved_by: z.string().nullish(), }) -/** - * DefaultBlockConfigsResponse - */ -export const zDefaultBlockConfigsResponse = z.array(z.record(z.string(), z.unknown())) - -/** - * DefaultBlockConfigResponse - */ -export const zDefaultBlockConfigResponse = z.record(z.string(), z.unknown()) - /** * SyncDraftWorkflowPayload */ @@ -602,10 +537,13 @@ export const zSyncDraftWorkflowPayload = z.object({ hash: z.string().nullish(), }) +/** + * SyncDraftWorkflowResponse + */ export const zSyncDraftWorkflowResponse = z.object({ - hash: z.string().optional(), - result: z.string().optional(), - updated_at: z.string().optional(), + hash: z.string(), + result: z.string(), + updated_at: z.int(), }) /** @@ -638,9 +576,9 @@ export const zHumanInputDeliveryTestPayload = z.object({ }) /** - * EmptyObjectResponse + * HumanInputDeliveryTestResponse */ -export const zEmptyObjectResponse = z.record(z.string(), z.unknown()) +export const zHumanInputDeliveryTestResponse = z.record(z.string(), z.unknown()) /** * WorkflowComposerCopyFromRosterPayload @@ -660,80 +598,31 @@ export const zDraftWorkflowNodeRunPayload = z.object({ query: z.string().optional().default(''), }) -/** - * DraftWorkflowRunPayload - */ -export const zDraftWorkflowRunPayload = z.object({ - datasource_info_list: z.array(z.record(z.string(), z.unknown())), - datasource_type: z.string(), - inputs: z.record(z.string(), z.unknown()), - start_node_id: z.string(), -}) - /** * EventStreamResponse */ export const zEventStreamResponse = z.string() -export const zDraftWorkflowTriggerRunRequest = z.object({ - node_id: z.string(), -}) - -/** - * DraftWorkflowTriggerRunAllPayload - */ -export const zDraftWorkflowTriggerRunAllPayload = z.object({ - node_ids: z.array(z.string()), -}) - -export const zWorkflowDraftVariable = z.object({ - description: z.string().optional(), - edited: z.boolean().optional(), - full_content: z.record(z.string(), z.unknown()).optional(), - id: z.string().optional(), - is_truncated: z.boolean().optional(), - name: z.string().optional(), - selector: z.array(z.string()).optional(), - type: z.string().optional(), - value: z - .union([ - z.string(), - z.int(), - z.number(), - z.boolean(), - z.record(z.string(), z.unknown()), - z.array(z.unknown()), - ]) - .nullish(), - value_type: z.string().optional(), - visible: z.boolean().optional(), -}) - -export const zWorkflowDraftVariableList = z.object({ - items: z.array(zWorkflowDraftVariable).optional(), -}) - /** * WorkflowDraftVariableUpdatePayload */ export const zWorkflowDraftVariableUpdatePayload = z.object({ name: z.string().nullish(), - value: z.unknown().nullish(), + value: z.unknown().optional(), }) /** * PublishWorkflowPayload - * - * Payload for publishing snippet workflow. */ export const zPublishWorkflowPayload = z.object({ - knowledge_base_setting: z.record(z.string(), z.unknown()).nullish(), + marked_comment: z.string().max(100).nullish(), + marked_name: z.string().max(20).nullish(), }) /** - * WorkflowPublishResponse + * PublishWorkflowResponse */ -export const zWorkflowPublishResponse = z.object({ +export const zPublishWorkflowResponse = z.object({ created_at: z.int(), result: z.string(), }) @@ -758,15 +647,6 @@ export const zWorkflowUpdatePayload = z.object({ marked_name: z.string().max(20).nullish(), }) -/** - * WorkflowRestoreResponse - */ -export const zWorkflowRestoreResponse = z.object({ - hash: z.string(), - result: z.string(), - updated_at: z.int(), -}) - /** * ApiKeyItem */ @@ -919,6 +799,32 @@ export const zImport = z.object({ status: zImportStatus, }) +/** + * HumanInputUserActionResponse + */ +export const zHumanInputUserActionResponse = z.object({ + button_style: z.string().optional().default('default'), + id: z.string(), + title: z.string(), +}) + +/** + * HumanInputFormPreviewResponse + */ +export const zHumanInputFormPreviewResponse = z.object({ + TYPE: z.literal('human_input_required').optional().default('human_input_required'), + actions: z.array(zHumanInputUserActionResponse).optional(), + display_in_ui: z.boolean().optional().default(false), + expiration_time: z.int().nullish(), + form_content: z.string(), + form_id: z.string(), + form_token: z.string().nullish(), + inputs: z.array(z.record(z.string(), z.unknown())).optional(), + node_id: z.string(), + node_title: z.string(), + resolved_default_values: z.record(z.string(), z.unknown()).optional(), +}) + /** * AgentDriveItemResponse */ @@ -1319,9 +1225,9 @@ export const zUserSatisfactionRateStatisticResponse = z.object({ }) /** - * SimpleAccount + * SimpleAccountResponse */ -export const zSimpleAccount = z.object({ +export const zSimpleAccountResponse = z.object({ email: z.string(), id: z.string(), name: z.string(), @@ -1333,7 +1239,7 @@ export const zSimpleAccount = z.object({ export const zAdvancedChatWorkflowRunForListResponse = z.object({ conversation_id: z.string().nullish(), created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), elapsed_time: z.number().nullish(), exceptions_count: z.int().nullish(), finished_at: z.int().nullish(), @@ -1355,72 +1261,12 @@ export const zAdvancedChatWorkflowRunPaginationResponse = z.object({ limit: z.int(), }) -/** - * ConversationAnnotation - */ -export const zConversationAnnotation = z.object({ - account: zSimpleAccount.nullish(), - content: z.string(), - created_at: z.int().nullish(), - id: z.string(), - question: z.string().nullish(), -}) - -/** - * ConversationAnnotationHitHistory - */ -export const zConversationAnnotationHitHistory = z.object({ - annotation_create_account: zSimpleAccount.nullish(), - created_at: z.int().nullish(), - id: z.string(), -}) - -/** - * Feedback - */ -export const zFeedback = z.object({ - content: z.string().nullish(), - from_account: zSimpleAccount.nullish(), - from_end_user_id: z.string().nullish(), - from_source: z.string(), - rating: z.string(), -}) - -/** - * MessageDetail - */ -export const zMessageDetail = z.object({ - agent_thoughts: z.array(zAgentThought), - annotation: zConversationAnnotation.nullish(), - annotation_hit_history: zConversationAnnotationHitHistory.nullish(), - answer_tokens: z.int(), - conversation_id: z.string(), - created_at: z.int().nullish(), - error: z.string().nullish(), - feedbacks: z.array(zFeedback), - from_account_id: z.string().nullish(), - from_end_user_id: z.string().nullish(), - from_source: z.string(), - id: z.string(), - inputs: z.record(z.string(), zJsonValue), - message: zJsonValue, - message_files: z.array(zMessageFile), - message_metadata_dict: zJsonValue, - message_tokens: z.int(), - parent_message_id: z.string().nullish(), - provider_response_latency: z.number(), - query: z.string(), - re_sign_file_url_answer: z.string(), - status: z.string(), - workflow_run_id: z.string().nullish(), -}) - /** * WorkflowRunForListResponse */ export const zWorkflowRunForListResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), elapsed_time: z.number().nullish(), exceptions_count: z.int().nullish(), finished_at: z.int().nullish(), @@ -1456,7 +1302,7 @@ export const zSimpleEndUser = z.object({ */ export const zWorkflowRunDetailResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), created_by_end_user: zSimpleEndUser.nullish(), created_by_role: z.string().nullish(), elapsed_time: z.number().nullish(), @@ -1478,7 +1324,7 @@ export const zWorkflowRunDetailResponse = z.object({ */ export const zWorkflowRunNodeExecutionResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), created_by_end_user: zSimpleEndUser.nullish(), created_by_role: z.string().nullish(), elapsed_time: z.number().nullish(), @@ -1544,9 +1390,9 @@ export const zSandboxUploadResponse = z.object({ }) /** - * AccountWithRole + * AccountWithRoleResponse */ -export const zAccountWithRole = z.object({ +export const zAccountWithRoleResponse = z.object({ avatar: z.string().nullish(), created_at: z.int().nullish(), email: z.string(), @@ -1563,7 +1409,7 @@ export const zAccountWithRole = z.object({ * WorkflowCommentMentionUsersPayload */ export const zWorkflowCommentMentionUsersPayload = z.object({ - users: z.array(zAccountWithRole), + users: z.array(zAccountWithRoleResponse), }) /** @@ -1752,7 +1598,7 @@ export const zPipelineVariableResponse = z.object({ export const zWorkflowResponse = z.object({ conversation_variables: z.array(zWorkflowConversationVariableResponse), created_at: z.int(), - created_by: zSimpleAccount.nullish(), + created_by: zSimpleAccountResponse.nullish(), environment_variables: z.array(zWorkflowEnvironmentVariableResponse), features: z.record(z.string(), z.unknown()), graph: z.record(z.string(), z.unknown()), @@ -1763,7 +1609,7 @@ export const zWorkflowResponse = z.object({ rag_pipeline_variables: z.array(zPipelineVariableResponse), tool_published: z.boolean(), updated_at: z.int(), - updated_by: zSimpleAccount.nullish(), + updated_by: zSimpleAccountResponse.nullish(), version: z.string(), }) @@ -1778,9 +1624,9 @@ export const zWorkflowPaginationResponse = z.object({ }) /** - * EnvironmentVariableItemResponse + * WorkflowDraftEnvironmentVariableResponse */ -export const zEnvironmentVariableItemResponse = z.object({ +export const zWorkflowDraftEnvironmentVariableResponse = z.object({ description: z.string().nullish(), editable: z.boolean(), edited: z.boolean(), @@ -1794,10 +1640,10 @@ export const zEnvironmentVariableItemResponse = z.object({ }) /** - * EnvironmentVariableListResponse + * WorkflowDraftEnvironmentVariableListResponse */ -export const zEnvironmentVariableListResponse = z.object({ - items: z.array(zEnvironmentVariableItemResponse), +export const zWorkflowDraftEnvironmentVariableListResponse = z.object({ + items: z.array(zWorkflowDraftEnvironmentVariableResponse), }) /** @@ -1984,21 +1830,61 @@ export const zOutputPreviewView = z.object({ value: z.unknown().optional(), }) -export const zWorkflowDraftVariableWithoutValue = z.object({ - description: z.string().optional(), - edited: z.boolean().optional(), - id: z.string().optional(), - is_truncated: z.boolean().optional(), - name: z.string().optional(), - selector: z.array(z.string()).optional(), - type: z.string().optional(), - value_type: z.string().optional(), - visible: z.boolean().optional(), +/** + * WorkflowDraftVariableWithoutValueResponse + */ +export const zWorkflowDraftVariableWithoutValueResponse = z.object({ + description: z.string(), + edited: z.boolean(), + id: z.string(), + is_truncated: z.boolean(), + name: z.string(), + selector: z.array(z.string()), + type: z.string(), + value_type: z.string(), + visible: z.boolean(), }) -export const zWorkflowDraftVariableListWithoutValue = z.object({ - items: z.array(zWorkflowDraftVariableWithoutValue).optional(), - total: z.int().optional(), +/** + * WorkflowDraftVariableListWithoutValueResponse + */ +export const zWorkflowDraftVariableListWithoutValueResponse = z.object({ + items: z.array(zWorkflowDraftVariableWithoutValueResponse), + total: z.int().nullable(), +}) + +/** + * WorkflowDraftVariableFullContentResponse + */ +export const zWorkflowDraftVariableFullContentResponse = z.object({ + download_url: z.string(), + length: z.int().nullable(), + size_bytes: z.int().nullable(), + value_type: z.string(), +}) + +/** + * WorkflowDraftVariableResponse + */ +export const zWorkflowDraftVariableResponse = z.object({ + description: z.string(), + edited: z.boolean(), + full_content: zWorkflowDraftVariableFullContentResponse.nullable(), + id: z.string(), + is_truncated: z.boolean(), + name: z.string(), + selector: z.array(z.string()), + type: z.string(), + value: z.unknown(), + value_type: z.string(), + visible: z.boolean(), +}) + +/** + * WorkflowDraftVariableListResponse + */ +export const zWorkflowDraftVariableListResponse = z.object({ + items: z.array(zWorkflowDraftVariableResponse), }) /** @@ -2152,20 +2038,6 @@ export const zConversationDetail = z.object({ user_feedback_stats: zFeedbackStat.nullish(), }) -/** - * ConversationMessageDetail - */ -export const zConversationMessageDetail = z.object({ - created_at: z.int().nullish(), - first_message: zMessageDetail.nullish(), - from_account_id: z.string().nullish(), - from_end_user_id: z.string().nullish(), - from_source: z.string(), - id: z.string(), - model_config: zModelConfig.nullish(), - status: z.string(), -}) - /** * Type */ @@ -2366,6 +2238,26 @@ export const zSimpleMessageDetail = z.object({ query: z.string(), }) +/** + * SimpleAccount + */ +export const zSimpleAccount = z.object({ + email: z.string(), + id: z.string(), + name: z.string(), +}) + +/** + * ConversationAnnotation + */ +export const zConversationAnnotation = z.object({ + account: zSimpleAccount.nullish(), + content: z.string(), + created_at: z.int().nullish(), + id: z.string(), + question: z.string().nullish(), +}) + /** * Conversation */ @@ -2398,6 +2290,69 @@ export const zConversationPagination = z.object({ total: z.int(), }) +/** + * ConversationAnnotationHitHistory + */ +export const zConversationAnnotationHitHistory = z.object({ + annotation_create_account: zSimpleAccount.nullish(), + created_at: z.int().nullish(), + id: z.string(), +}) + +/** + * Feedback + */ +export const zFeedback = z.object({ + content: z.string().nullish(), + from_account: zSimpleAccount.nullish(), + from_end_user_id: z.string().nullish(), + from_source: z.string(), + rating: z.string(), +}) + +/** + * MessageDetail + */ +export const zMessageDetail = z.object({ + agent_thoughts: z.array(zAgentThought), + annotation: zConversationAnnotation.nullish(), + annotation_hit_history: zConversationAnnotationHitHistory.nullish(), + answer_tokens: z.int(), + conversation_id: z.string(), + created_at: z.int().nullish(), + error: z.string().nullish(), + feedbacks: z.array(zFeedback), + from_account_id: z.string().nullish(), + from_end_user_id: z.string().nullish(), + from_source: z.string(), + id: z.string(), + inputs: z.record(z.string(), zJsonValue), + message: zJsonValue, + message_files: z.array(zMessageFile), + message_metadata_dict: zJsonValue, + message_tokens: z.int(), + parent_message_id: z.string().nullish(), + provider_response_latency: z.number(), + query: z.string(), + re_sign_file_url_answer: z.string(), + status: z.string(), + workflow_run_id: z.string().nullish(), +}) + +/** + * ConversationMessageDetail + */ +export const zConversationMessageDetail = z.object({ + created_at: z.int().nullish(), + first_message: zMessageDetail.nullish(), + from_account_id: z.string().nullish(), + from_end_user_id: z.string().nullish(), + from_source: z.string(), + id: z.string(), + model_config: zModelConfig.nullish(), + status: z.string(), +}) + /** * ExecutionContentType */ @@ -2425,7 +2380,7 @@ export const zWorkflowRunForLogResponse = z.object({ */ export const zWorkflowAppLogPartialResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), created_by_end_user: zSimpleEndUser.nullish(), created_by_role: z.string().nullish(), created_from: z.string().nullish(), @@ -2461,7 +2416,7 @@ export const zWorkflowRunForArchivedLogResponse = z.object({ */ export const zWorkflowArchivedLogPartialResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), created_by_end_user: zSimpleEndUser.nullish(), id: z.string(), trigger_metadata: z.unknown().optional(), @@ -3492,6 +3447,11 @@ export const zMessageInfiniteScrollPaginationResponse = z.object({ */ export const zGeneratedAppResponseWritable = zJsonValue +/** + * HumanInputDeliveryTestResponse + */ +export const zHumanInputDeliveryTestResponseWritable = z.record(z.string(), z.unknown()) + /** * AppPartial */ @@ -3835,65 +3795,11 @@ export const zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFo }) /** - * Human input form preview + * Human input form preview retrieved */ export const zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormPreviewResponse = zHumanInputFormPreviewResponse -export const zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunBody - = zHumanInputFormSubmitPayload - -export const zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunPath - = z.object({ - app_id: z.uuid(), - node_id: z.string(), - }) - -/** - * Human input form submission result - */ -export const zPostAppsByAppIdAdvancedChatWorkflowsDraftHumanInputNodesByNodeIdFormRunResponse - = zHumanInputFormSubmitResponse - -export const zPostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunBody - = zIterationNodeRunPayload - -export const zPostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunPath = z.object({ - app_id: z.uuid(), - node_id: z.string(), -}) - -/** - * Iteration node run started successfully - */ -export const zPostAppsByAppIdAdvancedChatWorkflowsDraftIterationNodesByNodeIdRunResponse - = zGeneratedAppResponse - -export const zPostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunBody - = zLoopNodeRunPayload - -export const zPostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunPath = z.object({ - app_id: z.uuid(), - node_id: z.string(), -}) - -/** - * Loop node run started successfully - */ -export const zPostAppsByAppIdAdvancedChatWorkflowsDraftLoopNodesByNodeIdRunResponse - = zGeneratedAppResponse - -export const zPostAppsByAppIdAdvancedChatWorkflowsDraftRunBody = zAdvancedChatWorkflowRunPayload - -export const zPostAppsByAppIdAdvancedChatWorkflowsDraftRunPath = z.object({ - app_id: z.uuid(), -}) - -/** - * Workflow run started successfully - */ -export const zPostAppsByAppIdAdvancedChatWorkflowsDraftRunResponse = zGeneratedAppResponse - export const zGetAppsByAppIdAgentDriveFilesPath = z.object({ app_id: z.uuid(), }) @@ -5163,31 +5069,6 @@ export const zGetAppsByAppIdWorkflowsQuery = z.object({ */ export const zGetAppsByAppIdWorkflowsResponse = zWorkflowPaginationResponse -export const zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsPath = z.object({ - app_id: z.uuid(), -}) - -/** - * Default block configurations retrieved successfully - */ -export const zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsResponse - = zDefaultBlockConfigsResponse - -export const zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypePath = z.object({ - app_id: z.uuid(), - block_type: z.string(), -}) - -export const zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeQuery = z.object({ - q: z.string().optional(), -}) - -/** - * Default block configuration retrieved successfully - */ -export const zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeResponse - = zDefaultBlockConfigResponse - export const zGetAppsByAppIdWorkflowsDraftPath = z.object({ app_id: z.uuid(), }) @@ -5215,7 +5096,8 @@ export const zGetAppsByAppIdWorkflowsDraftConversationVariablesPath = z.object({ /** * Conversation variables retrieved successfully */ -export const zGetAppsByAppIdWorkflowsDraftConversationVariablesResponse = zWorkflowDraftVariableList +export const zGetAppsByAppIdWorkflowsDraftConversationVariablesResponse + = zWorkflowDraftVariableListResponse export const zPostAppsByAppIdWorkflowsDraftConversationVariablesBody = zConversationVariableUpdatePayload @@ -5237,7 +5119,7 @@ export const zGetAppsByAppIdWorkflowsDraftEnvironmentVariablesPath = z.object({ * Environment variables retrieved successfully */ export const zGetAppsByAppIdWorkflowsDraftEnvironmentVariablesResponse - = zEnvironmentVariableListResponse + = zWorkflowDraftEnvironmentVariableListResponse export const zPostAppsByAppIdWorkflowsDraftEnvironmentVariablesBody = zEnvironmentVariableUpdatePayload @@ -5271,10 +5153,10 @@ export const zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdDeliveryTestPa }) /** - * Human input delivery test result + * Human input delivery tested */ export const zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdDeliveryTestResponse - = zEmptyObjectResponse + = zHumanInputDeliveryTestResponse export const zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormPreviewBody = zHumanInputFormPreviewPayload @@ -5285,49 +5167,11 @@ export const zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormPreviewPat }) /** - * Human input form preview + * Human input form preview retrieved */ export const zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormPreviewResponse = zHumanInputFormPreviewResponse -export const zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunBody - = zHumanInputFormSubmitPayload - -export const zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunPath = z.object({ - app_id: z.uuid(), - node_id: z.string(), -}) - -/** - * Human input form submission result - */ -export const zPostAppsByAppIdWorkflowsDraftHumanInputNodesByNodeIdFormRunResponse - = zHumanInputFormSubmitResponse - -export const zPostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunBody = zIterationNodeRunPayload - -export const zPostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunPath = z.object({ - app_id: z.uuid(), - node_id: z.string(), -}) - -/** - * Workflow iteration node run started successfully - */ -export const zPostAppsByAppIdWorkflowsDraftIterationNodesByNodeIdRunResponse = zGeneratedAppResponse - -export const zPostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunBody = zLoopNodeRunPayload - -export const zPostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunPath = z.object({ - app_id: z.uuid(), - node_id: z.string(), -}) - -/** - * Workflow loop node run started successfully - */ -export const zPostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunResponse = zGeneratedAppResponse - export const zGetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerPath = z.object({ app_id: z.uuid(), node_id: z.string(), @@ -5451,7 +5295,8 @@ export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdTriggerRunPath = z.objec /** * Trigger event received and node executed successfully */ -export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdTriggerRunResponse = zGeneratedAppResponse +export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdTriggerRunResponse + = zWorkflowRunNodeExecutionResponse export const zDeleteAppsByAppIdWorkflowsDraftNodesByNodeIdVariablesPath = z.object({ app_id: z.uuid(), @@ -5472,18 +5317,7 @@ export const zGetAppsByAppIdWorkflowsDraftNodesByNodeIdVariablesPath = z.object( * Node variables retrieved successfully */ export const zGetAppsByAppIdWorkflowsDraftNodesByNodeIdVariablesResponse - = zWorkflowDraftVariableList - -export const zPostAppsByAppIdWorkflowsDraftRunBody = zDraftWorkflowRunPayload - -export const zPostAppsByAppIdWorkflowsDraftRunPath = z.object({ - app_id: z.uuid(), -}) - -/** - * Draft workflow run started successfully - */ -export const zPostAppsByAppIdWorkflowsDraftRunResponse = zGeneratedAppResponse + = zWorkflowDraftVariableListResponse export const zGetAppsByAppIdWorkflowsDraftRunsByRunIdNodeOutputsPath = z.object({ app_id: z.uuid(), @@ -5538,29 +5372,8 @@ export const zGetAppsByAppIdWorkflowsDraftSystemVariablesPath = z.object({ /** * System variables retrieved successfully */ -export const zGetAppsByAppIdWorkflowsDraftSystemVariablesResponse = zWorkflowDraftVariableList - -export const zPostAppsByAppIdWorkflowsDraftTriggerRunBody = zDraftWorkflowTriggerRunRequest - -export const zPostAppsByAppIdWorkflowsDraftTriggerRunPath = z.object({ - app_id: z.uuid(), -}) - -/** - * Trigger event received and workflow executed successfully - */ -export const zPostAppsByAppIdWorkflowsDraftTriggerRunResponse = zGeneratedAppResponse - -export const zPostAppsByAppIdWorkflowsDraftTriggerRunAllBody = zDraftWorkflowTriggerRunAllPayload - -export const zPostAppsByAppIdWorkflowsDraftTriggerRunAllPath = z.object({ - app_id: z.uuid(), -}) - -/** - * Workflow executed successfully - */ -export const zPostAppsByAppIdWorkflowsDraftTriggerRunAllResponse = zGeneratedAppResponse +export const zGetAppsByAppIdWorkflowsDraftSystemVariablesResponse + = zWorkflowDraftVariableListResponse export const zDeleteAppsByAppIdWorkflowsDraftVariablesPath = z.object({ app_id: z.uuid(), @@ -5583,7 +5396,8 @@ export const zGetAppsByAppIdWorkflowsDraftVariablesQuery = z.object({ /** * Workflow variables retrieved successfully */ -export const zGetAppsByAppIdWorkflowsDraftVariablesResponse = zWorkflowDraftVariableListWithoutValue +export const zGetAppsByAppIdWorkflowsDraftVariablesResponse + = zWorkflowDraftVariableListWithoutValueResponse export const zDeleteAppsByAppIdWorkflowsDraftVariablesByVariableIdPath = z.object({ app_id: z.uuid(), @@ -5603,7 +5417,8 @@ export const zGetAppsByAppIdWorkflowsDraftVariablesByVariableIdPath = z.object({ /** * Variable retrieved successfully */ -export const zGetAppsByAppIdWorkflowsDraftVariablesByVariableIdResponse = zWorkflowDraftVariable +export const zGetAppsByAppIdWorkflowsDraftVariablesByVariableIdResponse + = zWorkflowDraftVariableResponse export const zPatchAppsByAppIdWorkflowsDraftVariablesByVariableIdBody = zWorkflowDraftVariableUpdatePayload @@ -5616,7 +5431,8 @@ export const zPatchAppsByAppIdWorkflowsDraftVariablesByVariableIdPath = z.object /** * Variable updated successfully */ -export const zPatchAppsByAppIdWorkflowsDraftVariablesByVariableIdResponse = zWorkflowDraftVariable +export const zPatchAppsByAppIdWorkflowsDraftVariablesByVariableIdResponse + = zWorkflowDraftVariableResponse export const zPutAppsByAppIdWorkflowsDraftVariablesByVariableIdResetPath = z.object({ app_id: z.uuid(), @@ -5624,7 +5440,7 @@ export const zPutAppsByAppIdWorkflowsDraftVariablesByVariableIdResetPath = z.obj }) export const zPutAppsByAppIdWorkflowsDraftVariablesByVariableIdResetResponse = z.union([ - zWorkflowDraftVariable, + zWorkflowDraftVariableResponse, z.void(), ]) @@ -5646,7 +5462,7 @@ export const zPostAppsByAppIdWorkflowsPublishPath = z.object({ /** * Workflow published successfully */ -export const zPostAppsByAppIdWorkflowsPublishResponse = zWorkflowPublishResponse +export const zPostAppsByAppIdWorkflowsPublishResponse = zPublishWorkflowResponse export const zGetAppsByAppIdWorkflowsPublishedRunsByRunIdNodeOutputsPath = z.object({ app_id: z.uuid(), @@ -5739,7 +5555,7 @@ export const zPostAppsByAppIdWorkflowsByWorkflowIdRestorePath = z.object({ /** * Workflow restored successfully */ -export const zPostAppsByAppIdWorkflowsByWorkflowIdRestoreResponse = zWorkflowRestoreResponse +export const zPostAppsByAppIdWorkflowsByWorkflowIdRestoreResponse = zSyncDraftWorkflowResponse export const zGetAppsByResourceIdApiKeysPath = z.object({ resource_id: z.uuid(), diff --git a/packages/contracts/generated/api/console/rag/orpc.gen.ts b/packages/contracts/generated/api/console/rag/orpc.gen.ts index ea52e39d5f6..20a1da49d9f 100644 --- a/packages/contracts/generated/api/console/rag/orpc.gen.ts +++ b/packages/contracts/generated/api/console/rag/orpc.gen.ts @@ -24,11 +24,6 @@ import { zGetRagPipelinesByPipelineIdWorkflowRunsPath, zGetRagPipelinesByPipelineIdWorkflowRunsQuery, zGetRagPipelinesByPipelineIdWorkflowRunsResponse, - zGetRagPipelinesByPipelineIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypePath, - zGetRagPipelinesByPipelineIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeQuery, - zGetRagPipelinesByPipelineIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeResponse, - zGetRagPipelinesByPipelineIdWorkflowsDefaultWorkflowBlockConfigsPath, - zGetRagPipelinesByPipelineIdWorkflowsDefaultWorkflowBlockConfigsResponse, zGetRagPipelinesByPipelineIdWorkflowsDraftEnvironmentVariablesPath, zGetRagPipelinesByPipelineIdWorkflowsDraftEnvironmentVariablesResponse, zGetRagPipelinesByPipelineIdWorkflowsDraftNodesByNodeIdLastRunPath, @@ -48,7 +43,6 @@ import { zGetRagPipelinesByPipelineIdWorkflowsDraftVariablesByVariableIdPath, zGetRagPipelinesByPipelineIdWorkflowsDraftVariablesByVariableIdResponse, zGetRagPipelinesByPipelineIdWorkflowsDraftVariablesPath, - zGetRagPipelinesByPipelineIdWorkflowsDraftVariablesQuery, zGetRagPipelinesByPipelineIdWorkflowsDraftVariablesResponse, zGetRagPipelinesByPipelineIdWorkflowsPath, zGetRagPipelinesByPipelineIdWorkflowsPublishedPreProcessingParametersPath, @@ -93,35 +87,17 @@ import { zPostRagPipelinesByPipelineIdWorkflowsByWorkflowIdRestorePath, zPostRagPipelinesByPipelineIdWorkflowsByWorkflowIdRestoreResponse, zPostRagPipelinesByPipelineIdWorkflowsDraftBody, - zPostRagPipelinesByPipelineIdWorkflowsDraftDatasourceNodesByNodeIdRunBody, - zPostRagPipelinesByPipelineIdWorkflowsDraftDatasourceNodesByNodeIdRunPath, - zPostRagPipelinesByPipelineIdWorkflowsDraftDatasourceNodesByNodeIdRunResponse, zPostRagPipelinesByPipelineIdWorkflowsDraftDatasourceVariablesInspectBody, zPostRagPipelinesByPipelineIdWorkflowsDraftDatasourceVariablesInspectPath, zPostRagPipelinesByPipelineIdWorkflowsDraftDatasourceVariablesInspectResponse, - zPostRagPipelinesByPipelineIdWorkflowsDraftIterationNodesByNodeIdRunBody, - zPostRagPipelinesByPipelineIdWorkflowsDraftIterationNodesByNodeIdRunPath, - zPostRagPipelinesByPipelineIdWorkflowsDraftIterationNodesByNodeIdRunResponse, - zPostRagPipelinesByPipelineIdWorkflowsDraftLoopNodesByNodeIdRunBody, - zPostRagPipelinesByPipelineIdWorkflowsDraftLoopNodesByNodeIdRunPath, - zPostRagPipelinesByPipelineIdWorkflowsDraftLoopNodesByNodeIdRunResponse, zPostRagPipelinesByPipelineIdWorkflowsDraftNodesByNodeIdRunBody, zPostRagPipelinesByPipelineIdWorkflowsDraftNodesByNodeIdRunPath, zPostRagPipelinesByPipelineIdWorkflowsDraftNodesByNodeIdRunResponse, zPostRagPipelinesByPipelineIdWorkflowsDraftPath, zPostRagPipelinesByPipelineIdWorkflowsDraftResponse, - zPostRagPipelinesByPipelineIdWorkflowsDraftRunBody, - zPostRagPipelinesByPipelineIdWorkflowsDraftRunPath, - zPostRagPipelinesByPipelineIdWorkflowsDraftRunResponse, zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdPreviewBody, zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdPreviewPath, zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdPreviewResponse, - zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdRunBody, - zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdRunPath, - zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdRunResponse, - zPostRagPipelinesByPipelineIdWorkflowsPublishedRunBody, - zPostRagPipelinesByPipelineIdWorkflowsPublishedRunPath, - zPostRagPipelinesByPipelineIdWorkflowsPublishedRunResponse, zPostRagPipelinesByPipelineIdWorkflowsPublishPath, zPostRagPipelinesByPipelineIdWorkflowsPublishResponse, zPostRagPipelinesImportsBody, @@ -506,87 +482,10 @@ export const workflowRuns = { byRunId, } -/** - * Get default block config - */ -export const get10 = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getRagPipelinesByPipelineIdWorkflowsDefaultWorkflowBlockConfigsByBlockType', - path: '/rag/pipelines/{pipeline_id}/workflows/default-workflow-block-configs/{block_type}', - summary: 'Get default block config', - tags: ['console'], - }) - .input( - z.object({ - params: zGetRagPipelinesByPipelineIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypePath, - query: - zGetRagPipelinesByPipelineIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeQuery.optional(), - }), - ) - .output(zGetRagPipelinesByPipelineIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeResponse) - -export const byBlockType = { - get: get10, -} - -/** - * Get default block config - */ -export const get11 = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getRagPipelinesByPipelineIdWorkflowsDefaultWorkflowBlockConfigs', - path: '/rag/pipelines/{pipeline_id}/workflows/default-workflow-block-configs', - summary: 'Get default block config', - tags: ['console'], - }) - .input(z.object({ params: zGetRagPipelinesByPipelineIdWorkflowsDefaultWorkflowBlockConfigsPath })) - .output(zGetRagPipelinesByPipelineIdWorkflowsDefaultWorkflowBlockConfigsResponse) - -export const defaultWorkflowBlockConfigs = { - get: get11, - byBlockType, -} - -/** - * Run rag pipeline datasource - */ -export const post9 = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postRagPipelinesByPipelineIdWorkflowsDraftDatasourceNodesByNodeIdRun', - path: '/rag/pipelines/{pipeline_id}/workflows/draft/datasource/nodes/{node_id}/run', - summary: 'Run rag pipeline datasource', - tags: ['console'], - }) - .input( - z.object({ - body: zPostRagPipelinesByPipelineIdWorkflowsDraftDatasourceNodesByNodeIdRunBody, - params: zPostRagPipelinesByPipelineIdWorkflowsDraftDatasourceNodesByNodeIdRunPath, - }), - ) - .output(zPostRagPipelinesByPipelineIdWorkflowsDraftDatasourceNodesByNodeIdRunResponse) - -export const run = { - post: post9, -} - -export const byNodeId = { - run, -} - -export const nodes = { - byNodeId, -} - /** * Set datasource variables */ -export const post10 = oc +export const post9 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -604,18 +503,17 @@ export const post10 = oc .output(zPostRagPipelinesByPipelineIdWorkflowsDraftDatasourceVariablesInspectResponse) export const variablesInspect = { - post: post10, + post: post9, } export const datasource = { - nodes, variablesInspect, } /** * Get draft workflow */ -export const get12 = oc +export const get10 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -628,82 +526,10 @@ export const get12 = oc .output(zGetRagPipelinesByPipelineIdWorkflowsDraftEnvironmentVariablesResponse) export const environmentVariables = { - get: get12, + get: get10, } -/** - * Run draft workflow iteration node - */ -export const post11 = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postRagPipelinesByPipelineIdWorkflowsDraftIterationNodesByNodeIdRun', - path: '/rag/pipelines/{pipeline_id}/workflows/draft/iteration/nodes/{node_id}/run', - summary: 'Run draft workflow iteration node', - tags: ['console'], - }) - .input( - z.object({ - body: zPostRagPipelinesByPipelineIdWorkflowsDraftIterationNodesByNodeIdRunBody, - params: zPostRagPipelinesByPipelineIdWorkflowsDraftIterationNodesByNodeIdRunPath, - }), - ) - .output(zPostRagPipelinesByPipelineIdWorkflowsDraftIterationNodesByNodeIdRunResponse) - -export const run2 = { - post: post11, -} - -export const byNodeId2 = { - run: run2, -} - -export const nodes2 = { - byNodeId: byNodeId2, -} - -export const iteration = { - nodes: nodes2, -} - -/** - * Run draft workflow loop node - */ -export const post12 = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postRagPipelinesByPipelineIdWorkflowsDraftLoopNodesByNodeIdRun', - path: '/rag/pipelines/{pipeline_id}/workflows/draft/loop/nodes/{node_id}/run', - summary: 'Run draft workflow loop node', - tags: ['console'], - }) - .input( - z.object({ - body: zPostRagPipelinesByPipelineIdWorkflowsDraftLoopNodesByNodeIdRunBody, - params: zPostRagPipelinesByPipelineIdWorkflowsDraftLoopNodesByNodeIdRunPath, - }), - ) - .output(zPostRagPipelinesByPipelineIdWorkflowsDraftLoopNodesByNodeIdRunResponse) - -export const run3 = { - post: post12, -} - -export const byNodeId3 = { - run: run3, -} - -export const nodes3 = { - byNodeId: byNodeId3, -} - -export const loop = { - nodes: nodes3, -} - -export const get13 = oc +export const get11 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -715,13 +541,13 @@ export const get13 = oc .output(zGetRagPipelinesByPipelineIdWorkflowsDraftNodesByNodeIdLastRunResponse) export const lastRun = { - get: get13, + get: get11, } /** * Run draft workflow node */ -export const post13 = oc +export const post10 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -738,8 +564,8 @@ export const post13 = oc ) .output(zPostRagPipelinesByPipelineIdWorkflowsDraftNodesByNodeIdRunResponse) -export const run4 = { - post: post13, +export const run = { + post: post10, } export const delete2 = oc @@ -756,7 +582,7 @@ export const delete2 = oc ) .output(zDeleteRagPipelinesByPipelineIdWorkflowsDraftNodesByNodeIdVariablesResponse) -export const get14 = oc +export const get12 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -769,23 +595,23 @@ export const get14 = oc export const variables = { delete: delete2, - get: get14, + get: get12, } -export const byNodeId4 = { +export const byNodeId = { lastRun, - run: run4, + run, variables, } -export const nodes4 = { - byNodeId: byNodeId4, +export const nodes = { + byNodeId, } /** * Get first step parameters of rag pipeline */ -export const get15 = oc +export const get13 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -803,7 +629,7 @@ export const get15 = oc .output(zGetRagPipelinesByPipelineIdWorkflowsDraftPreProcessingParametersResponse) export const parameters = { - get: get15, + get: get13, } export const preProcessing = { @@ -813,7 +639,7 @@ export const preProcessing = { /** * Get second step parameters of rag pipeline */ -export const get16 = oc +export const get14 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -831,38 +657,14 @@ export const get16 = oc .output(zGetRagPipelinesByPipelineIdWorkflowsDraftProcessingParametersResponse) export const parameters2 = { - get: get16, + get: get14, } export const processing = { parameters: parameters2, } -/** - * Run draft workflow - */ -export const post14 = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postRagPipelinesByPipelineIdWorkflowsDraftRun', - path: '/rag/pipelines/{pipeline_id}/workflows/draft/run', - summary: 'Run draft workflow', - tags: ['console'], - }) - .input( - z.object({ - body: zPostRagPipelinesByPipelineIdWorkflowsDraftRunBody, - params: zPostRagPipelinesByPipelineIdWorkflowsDraftRunPath, - }), - ) - .output(zPostRagPipelinesByPipelineIdWorkflowsDraftRunResponse) - -export const run5 = { - post: post14, -} - -export const get17 = oc +export const get15 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -874,7 +676,7 @@ export const get17 = oc .output(zGetRagPipelinesByPipelineIdWorkflowsDraftSystemVariablesResponse) export const systemVariables = { - get: get17, + get: get15, } export const put = oc @@ -908,7 +710,7 @@ export const delete3 = oc ) .output(zDeleteRagPipelinesByPipelineIdWorkflowsDraftVariablesByVariableIdResponse) -export const get18 = oc +export const get16 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -937,7 +739,7 @@ export const patch2 = oc export const byVariableId = { delete: delete3, - get: get18, + get: get16, patch: patch2, reset, } @@ -957,7 +759,7 @@ export const delete4 = oc /** * Get draft workflow */ -export const get19 = oc +export const get17 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -966,24 +768,19 @@ export const get19 = oc summary: 'Get draft workflow', tags: ['console'], }) - .input( - z.object({ - params: zGetRagPipelinesByPipelineIdWorkflowsDraftVariablesPath, - query: zGetRagPipelinesByPipelineIdWorkflowsDraftVariablesQuery.optional(), - }), - ) + .input(z.object({ params: zGetRagPipelinesByPipelineIdWorkflowsDraftVariablesPath })) .output(zGetRagPipelinesByPipelineIdWorkflowsDraftVariablesResponse) export const variables2 = { delete: delete4, - get: get19, + get: get17, byVariableId, } /** * Get draft rag pipeline's workflow */ -export const get20 = oc +export const get18 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -998,7 +795,7 @@ export const get20 = oc /** * Sync draft workflow */ -export const post15 = oc +export const post11 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -1016,16 +813,13 @@ export const post15 = oc .output(zPostRagPipelinesByPipelineIdWorkflowsDraftResponse) export const draft = { - get: get20, - post: post15, + get: get18, + post: post11, datasource, environmentVariables, - iteration, - loop, - nodes: nodes4, + nodes, preProcessing, processing, - run: run5, systemVariables, variables: variables2, } @@ -1033,7 +827,7 @@ export const draft = { /** * Get published pipeline */ -export const get21 = oc +export const get19 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -1048,7 +842,7 @@ export const get21 = oc /** * Publish workflow */ -export const post16 = oc +export const post12 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -1061,14 +855,14 @@ export const post16 = oc .output(zPostRagPipelinesByPipelineIdWorkflowsPublishResponse) export const publish2 = { - get: get21, - post: post16, + get: get19, + post: post12, } /** * Run datasource content preview */ -export const post17 = oc +export const post13 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -1086,50 +880,25 @@ export const post17 = oc .output(zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdPreviewResponse) export const preview = { - post: post17, + post: post13, } -/** - * Run rag pipeline datasource - */ -export const post18 = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdRun', - path: '/rag/pipelines/{pipeline_id}/workflows/published/datasource/nodes/{node_id}/run', - summary: 'Run rag pipeline datasource', - tags: ['console'], - }) - .input( - z.object({ - body: zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdRunBody, - params: zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdRunPath, - }), - ) - .output(zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdRunResponse) - -export const run6 = { - post: post18, -} - -export const byNodeId5 = { +export const byNodeId2 = { preview, - run: run6, } -export const nodes5 = { - byNodeId: byNodeId5, +export const nodes2 = { + byNodeId: byNodeId2, } export const datasource2 = { - nodes: nodes5, + nodes: nodes2, } /** * Get first step parameters of rag pipeline */ -export const get22 = oc +export const get20 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -1147,7 +916,7 @@ export const get22 = oc .output(zGetRagPipelinesByPipelineIdWorkflowsPublishedPreProcessingParametersResponse) export const parameters3 = { - get: get22, + get: get20, } export const preProcessing2 = { @@ -1157,7 +926,7 @@ export const preProcessing2 = { /** * Get second step parameters of rag pipeline */ -export const get23 = oc +export const get21 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -1175,45 +944,20 @@ export const get23 = oc .output(zGetRagPipelinesByPipelineIdWorkflowsPublishedProcessingParametersResponse) export const parameters4 = { - get: get23, + get: get21, } export const processing2 = { parameters: parameters4, } -/** - * Run published workflow - */ -export const post19 = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postRagPipelinesByPipelineIdWorkflowsPublishedRun', - path: '/rag/pipelines/{pipeline_id}/workflows/published/run', - summary: 'Run published workflow', - tags: ['console'], - }) - .input( - z.object({ - body: zPostRagPipelinesByPipelineIdWorkflowsPublishedRunBody, - params: zPostRagPipelinesByPipelineIdWorkflowsPublishedRunPath, - }), - ) - .output(zPostRagPipelinesByPipelineIdWorkflowsPublishedRunResponse) - -export const run7 = { - post: post19, -} - export const published = { datasource: datasource2, preProcessing: preProcessing2, processing: processing2, - run: run7, } -export const post20 = oc +export const post14 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -1225,7 +969,7 @@ export const post20 = oc .output(zPostRagPipelinesByPipelineIdWorkflowsByWorkflowIdRestoreResponse) export const restore = { - post: post20, + post: post14, } /** @@ -1273,7 +1017,7 @@ export const byWorkflowId = { /** * Get published workflows */ -export const get24 = oc +export const get22 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -1291,8 +1035,7 @@ export const get24 = oc .output(zGetRagPipelinesByPipelineIdWorkflowsResponse) export const workflows = { - get: get24, - defaultWorkflowBlockConfigs, + get: get22, draft, publish: publish2, published, diff --git a/packages/contracts/generated/api/console/rag/types.gen.ts b/packages/contracts/generated/api/console/rag/types.gen.ts index b9862a8d1e8..af02c383bdb 100644 --- a/packages/contracts/generated/api/console/rag/types.gen.ts +++ b/packages/contracts/generated/api/console/rag/types.gen.ts @@ -89,7 +89,7 @@ export type PipelineTemplateDetailResponse = { name: string } -export type RagPipelineOpaqueResponse = unknown +export type DatasourcePluginListResponse = Array export type RagPipelineImportPayload = { description?: string | null @@ -107,6 +107,21 @@ export type RagPipelineImportCheckDependenciesResponse = { leaked_dependencies?: Array } +export type RagPipelineRecommendedPluginResponse = { + installed_recommended_plugins: Array<{ + [key: string]: unknown + }> + uninstalled_recommended_plugins: Array<{ + [key: string]: unknown + }> +} + +export type RagPipelineTransformResponse = { + dataset_id: string + pipeline_id: string + status: string +} + export type WorkflowRunPaginationResponse = { data: Array has_more: boolean @@ -119,7 +134,7 @@ export type SimpleResultResponse = { export type WorkflowRunDetailResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null created_by_role?: string | null elapsed_time?: number | null @@ -147,18 +162,10 @@ export type WorkflowPaginationResponse = { page: number } -export type DefaultBlockConfigsResponse = Array<{ - [key: string]: unknown -}> - -export type DefaultBlockConfigResponse = { - [key: string]: unknown -} - export type WorkflowResponse = { conversation_variables: Array created_at: number - created_by?: SimpleAccount | null + created_by?: SimpleAccountResponse | null environment_variables: Array features: { [key: string]: unknown @@ -173,7 +180,7 @@ export type WorkflowResponse = { rag_pipeline_variables: Array tool_published: boolean updated_at: number - updated_by?: SimpleAccount | null + updated_by?: SimpleAccountResponse | null version: string } @@ -202,14 +209,6 @@ export type RagPipelineWorkflowSyncResponse = { updated_at: number } -export type DatasourceNodeRunPayload = { - credential_id?: string | null - datasource_type: string - inputs: { - [key: string]: unknown - } -} - export type DatasourceVariablesPayload = { datasource_info: { [key: string]: unknown @@ -221,7 +220,7 @@ export type DatasourceVariablesPayload = { export type WorkflowRunNodeExecutionResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null created_by_role?: string | null elapsed_time?: number | null @@ -244,14 +243,8 @@ export type WorkflowRunNodeExecutionResponse = { title?: string | null } -export type EnvironmentVariableListResponse = { - items: Array -} - -export type NodeRunPayload = { - inputs?: { - [key: string]: unknown - } | null +export type RagPipelineEnvironmentVariableListResponse = { + items: Array } export type NodeRunRequiredPayload = { @@ -260,58 +253,36 @@ export type NodeRunRequiredPayload = { } } -export type WorkflowDraftVariableList = { - items?: Array +export type WorkflowDraftVariableListResponse = { + items: Array } -export type RagPipelineStepParametersResponse = { +export type RagPipelineVariablesResponse = { variables: unknown } -export type DraftWorkflowRunPayload = { - datasource_info_list: Array<{ - [key: string]: unknown - }> - datasource_type: string - inputs: { - [key: string]: unknown - } - start_node_id: string +export type WorkflowDraftVariableListWithoutValueResponse = { + items: Array + total: number | null } -export type WorkflowDraftVariableListWithoutValue = { - items?: Array - total?: number -} - -export type WorkflowDraftVariable = { - description?: string - edited?: boolean - full_content?: { - [key: string]: unknown - } - id?: string - is_truncated?: boolean - name?: string - selector?: Array - type?: string - value?: - | string - | number - | number - | boolean - | { - [key: string]: unknown - } - | Array - | null - value_type?: string - visible?: boolean +export type WorkflowDraftVariableResponse = { + description: string + edited: boolean + full_content: WorkflowDraftVariableFullContentResponse | null + id: string + is_truncated: boolean + name: string + selector: Array + type: string + value: unknown + value_type: string + visible: boolean } export type WorkflowDraftVariablePatchPayload = { name?: string | null - value?: unknown | null + value?: unknown } export type RagPipelineWorkflowPublishResponse = { @@ -329,20 +300,6 @@ export type Parser = { export type DataSourceContentPreviewResponse = unknown -export type PublishedWorkflowRunPayload = { - datasource_info_list: Array<{ - [key: string]: unknown - }> - datasource_type: string - inputs: { - [key: string]: unknown - } - is_preview?: boolean - original_document_id?: string | null - response_mode?: 'blocking' | 'streaming' - start_node_id: string -} - export type WorkflowUpdatePayload = { marked_comment?: string | null marked_name?: string | null @@ -413,6 +370,14 @@ export type PipelineTemplateItemResponse = { privacy_policy?: string | null } +export type PluginDatasourceProviderEntity = { + declaration: DatasourceProviderEntityWithPlugin + is_authorized?: boolean + plugin_id: string + plugin_unique_identifier: string + provider: string +} + export type PluginDependency = { current_identifier?: string | null type: Type @@ -421,7 +386,7 @@ export type PluginDependency = { export type WorkflowRunForListResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null elapsed_time?: number | null exceptions_count?: number | null finished_at?: number | null @@ -433,7 +398,7 @@ export type WorkflowRunForListResponse = { version?: string | null } -export type SimpleAccount = { +export type SimpleAccountResponse = { email: string id: string name: string @@ -479,8 +444,8 @@ export type PipelineVariableResponse = { variable: string } -export type EnvironmentVariableItemResponse = { - description?: string | null +export type RagPipelineEnvironmentVariableResponse = { + description: string editable: boolean edited: boolean id: string @@ -492,16 +457,23 @@ export type EnvironmentVariableItemResponse = { visible: boolean } -export type WorkflowDraftVariableWithoutValue = { - description?: string - edited?: boolean - id?: string - is_truncated?: boolean - name?: string - selector?: Array - type?: string - value_type?: string - visible?: boolean +export type WorkflowDraftVariableWithoutValueResponse = { + description: string + edited: boolean + id: string + is_truncated: boolean + name: string + selector: Array + type: string + value_type: string + visible: boolean +} + +export type WorkflowDraftVariableFullContentResponse = { + download_url: string + length: number | null + size_bytes: number | null + value_type: string } export type DatasetRerankingModelResponse = { @@ -515,6 +487,14 @@ export type DatasetWeightedScoreResponse = { weight_type?: string | null } +export type DatasourceProviderEntityWithPlugin = { + credentials_schema?: Array + datasources?: Array + identity: DatasourceProviderIdentity + oauth_schema?: OAuthSchema | null + provider_type: DatasourceProviderType +} + export type Type = 'github' | 'marketplace' | 'package' export type Github = { @@ -544,6 +524,162 @@ export type DatasetVectorSettingResponse = { vector_weight?: number | null } +export type ProviderConfig = { + default?: number | string | number | boolean | null + help?: I18nObject | null + label?: I18nObject | null + multiple?: boolean + name: string + options?: Array