diff --git a/api/core/app/apps/advanced_chat/generate_task_pipeline.py b/api/core/app/apps/advanced_chat/generate_task_pipeline.py index 8c5645bbb7..e17ed0ebaa 100644 --- a/api/core/app/apps/advanced_chat/generate_task_pipeline.py +++ b/api/core/app/apps/advanced_chat/generate_task_pipeline.py @@ -165,7 +165,7 @@ class AdvancedChatAppGenerateTaskPipeline: ) generator = self._wrapper_process_stream_response(trace_manager=self._application_generate_entity.trace_manager) - + print(f"generator: {generator}") if self._base_task_pipeline._stream: return self._to_stream_response(generator) else: @@ -183,12 +183,13 @@ class AdvancedChatAppGenerateTaskPipeline: extras = {} if stream_response.metadata: extras["metadata"] = stream_response.metadata - + print(f"stream_response: {stream_response}") return ChatbotAppBlockingResponse( task_id=stream_response.task_id, data=ChatbotAppBlockingResponse.Data( id=self._message_id, mode=self._conversation_mode, + outputs=stream_response.data.outputs, conversation_id=self._conversation_id, message_id=self._message_id, answer=self._task_state.answer, @@ -209,6 +210,7 @@ class AdvancedChatAppGenerateTaskPipeline: :return: """ for stream_response in generator: + print(f"stream_response: {stream_response}") yield ChatbotAppStreamResponse( conversation_id=self._conversation_id, message_id=self._message_id, @@ -501,13 +503,12 @@ class AdvancedChatAppGenerateTaskPipeline: conversation_id=self._conversation_id, trace_manager=trace_manager, ) - workflow_finish_resp = self._workflow_response_converter.workflow_finish_to_stream_response( session=session, task_id=self._application_generate_entity.task_id, workflow_execution=workflow_execution, ) - + print(f"workflow_finish_resp: {workflow_finish_resp}") yield workflow_finish_resp self._base_task_pipeline._queue_manager.publish( QueueAdvancedChatMessageEndEvent(), PublishFrom.TASK_PIPELINE diff --git a/api/fields/message_fields.py b/api/fields/message_fields.py index e6aebd810f..deff11a939 100644 --- a/api/fields/message_fields.py +++ b/api/fields/message_fields.py @@ -45,6 +45,7 @@ message_fields = { "conversation_id": fields.String, "parent_message_id": fields.String, "inputs": FilesContainedField, + "outputs": fields.Raw(attribute="outputs_dict"), "query": fields.String, "answer": fields.String(attribute="re_sign_file_url_answer"), "feedback": fields.Nested(feedback_fields, attribute="user_feedback", allow_null=True), diff --git a/api/migrations/versions/2025_06_03_1229-83f1262d3f22_update_message_table_outputs_column_to_.py b/api/migrations/versions/2025_06_03_1229-83f1262d3f22_update_message_table_outputs_column_to_.py new file mode 100644 index 0000000000..aa4d1126bd --- /dev/null +++ b/api/migrations/versions/2025_06_03_1229-83f1262d3f22_update_message_table_outputs_column_to_.py @@ -0,0 +1,33 @@ +"""Update Message table outputs column to text and add outputs_dict property + +Revision ID: 83f1262d3f22 +Revises: 2adcbe1f5dfb +Create Date: 2025-06-03 12:29:08.619582 + +""" +from alembic import op +import models as models +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '83f1262d3f22' +down_revision = '2adcbe1f5dfb' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('messages', schema=None) as batch_op: + batch_op.add_column(sa.Column('outputs', sa.Text(), server_default=sa.text("'{}'::text"), nullable=True)) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('messages', schema=None) as batch_op: + batch_op.drop_column('outputs') + + # ### end Alembic commands ### diff --git a/api/models/model.py b/api/models/model.py index 229e77134e..26c6aa2a47 100644 --- a/api/models/model.py +++ b/api/models/model.py @@ -898,6 +898,7 @@ class Message(Base): message_unit_price = db.Column(db.Numeric(10, 4), nullable=False) message_price_unit = db.Column(db.Numeric(10, 7), nullable=False, server_default=db.text("0.001")) answer: Mapped[str] = db.Column(db.Text, nullable=False) + outputs: Mapped[Optional[str]] = mapped_column("outputs", db.Text, nullable=True, server_default=db.text("'{}'::text")) answer_tokens = db.Column(db.Integer, nullable=False, server_default=db.text("0")) answer_unit_price = db.Column(db.Numeric(10, 4), nullable=False) answer_price_unit = db.Column(db.Numeric(10, 7), nullable=False, server_default=db.text("0.001")) @@ -1086,6 +1087,14 @@ class Message(Base): def message_metadata_dict(self) -> dict: return json.loads(self.message_metadata) if self.message_metadata else {} + @property + def outputs_dict(self) -> dict: + return json.loads(self.outputs) if self.outputs else {} + + @outputs_dict.setter + def outputs_dict(self, value: Mapping[str, Any]): + self.outputs = json.dumps(value, ensure_ascii=False) if value else "{}" + @property def agent_thoughts(self): return ( @@ -1180,6 +1189,7 @@ class Message(Base): "model_id": self.model_id, "inputs": self.inputs, "query": self.query, + "outputs": self.outputs_dict, "total_price": self.total_price, "message": self.message, "answer": self.answer, diff --git a/web/app/components/workflow/nodes/answer/node.tsx b/web/app/components/workflow/nodes/answer/node.tsx index bb28066d95..57e3fab7bd 100644 --- a/web/app/components/workflow/nodes/answer/node.tsx +++ b/web/app/components/workflow/nodes/answer/node.tsx @@ -4,12 +4,44 @@ import { useTranslation } from 'react-i18next' import InfoPanel from '../_base/components/info-panel' import ReadonlyInputWithSelectVar from '../_base/components/readonly-input-with-select-var' import type { AnswerNodeType } from './types' -import type { NodeProps } from '@/app/components/workflow/types' +import { + useIsChatMode, + useWorkflow, + useWorkflowVariables, +} from '@/app/components/workflow/hooks' +import { BlockEnum } from '@/app/components/workflow/types' +import type { NodeProps, Variable } from '@/app/components/workflow/types' +import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { VarBlockIcon } from '@/app/components/workflow/block-icon' +import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' +import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others' +import { Line3 } from '@/app/components/base/icons/src/public/common' +import cn from 'classnames' + const Node: FC> = ({ id, data, }) => { const { t } = useTranslation() + const { getBeforeNodesInSameBranch } = useWorkflow() + const availableNodes = getBeforeNodesInSameBranch(id) + const { getCurrentVariableType } = useWorkflowVariables() + const isChatMode = useIsChatMode() + + const startNode = availableNodes.find((node: any) => { + return node.data.type === BlockEnum.Start + }) + + const getNode = (id: string) => { + return availableNodes.find(node => node.id === id) || startNode + } + + const { outputs = [] } = data + const filteredOutputs = (outputs as Variable[]).filter(({ value_selector }) => value_selector.length > 0) + console.log('filteredOutputs', filteredOutputs) + + if (!filteredOutputs.length) + return null return (
@@ -19,7 +51,52 @@ const Node: FC> = ({ nodeId={id} /> } /> +
+ {filteredOutputs.map(({ value_selector }, index) => { + const node = getNode(value_selector[0]) + const isSystem = isSystemVar(value_selector) + const isEnv = isENV(value_selector) + const isChatVar = isConversationVar(value_selector) + const varName = isSystem ? `sys.${value_selector[value_selector.length - 1]}` : value_selector[value_selector.length - 1] + const varType = getCurrentVariableType({ + valueSelector: value_selector, + availableNodes, + isChatMode, + }) + return ( +
+
+ {!isEnv && ( + <> +
+ +
+
{node?.data.title}
+ + + )} +
+ {!isEnv && !isChatVar && } + {isEnv && } + {isChatVar && } + +
{varName}
+
+
+
+
{varType}
+
+
+ ) + })} + +
+
+ ) } diff --git a/web/app/components/workflow/nodes/answer/panel.tsx b/web/app/components/workflow/nodes/answer/panel.tsx index 2a4b70ee32..0cec7f63f8 100644 --- a/web/app/components/workflow/nodes/answer/panel.tsx +++ b/web/app/components/workflow/nodes/answer/panel.tsx @@ -19,8 +19,12 @@ const Panel: FC> = ({ inputs, handleAnswerChange, filterVar, + handleVarListChange, + handleAddVariable, } = useConfig(id, data) + const outputs = inputs.outputs + const { availableVars, availableNodesWithParent } = useAvailableVarList(id, { onlyLeafNodeVar: false, hideChatVar: false, @@ -29,17 +33,34 @@ const Panel: FC> = ({ }) return ( -
- +
+
+ +
+ {/*
+ : undefined + } + > + + +
*/}
) } diff --git a/web/app/components/workflow/nodes/answer/types.ts b/web/app/components/workflow/nodes/answer/types.ts index 8f1b3cb6cd..721bb0aacb 100644 --- a/web/app/components/workflow/nodes/answer/types.ts +++ b/web/app/components/workflow/nodes/answer/types.ts @@ -1,6 +1,7 @@ import type { CommonNodeType, Variable } from '@/app/components/workflow/types' export type AnswerNodeType = CommonNodeType & { + outputs: Variable[] variables: Variable[] answer: string }