Merge branch 'chore/workflow-last-run' into deploy/dev

pull/22036/head
zxhlyh 11 months ago
commit 4200d016f0

@ -3,7 +3,7 @@ import json
from flask import request from flask import request
from flask_restful import marshal, reqparse from flask_restful import marshal, reqparse
from sqlalchemy import desc, select from sqlalchemy import desc, select
from werkzeug.exceptions import NotFound from werkzeug.exceptions import Forbidden, NotFound
import services import services
from controllers.common.errors import FilenameNotExistsError from controllers.common.errors import FilenameNotExistsError
@ -18,6 +18,7 @@ from controllers.service_api.app.error import (
from controllers.service_api.dataset.error import ( from controllers.service_api.dataset.error import (
ArchivedDocumentImmutableError, ArchivedDocumentImmutableError,
DocumentIndexingError, DocumentIndexingError,
InvalidMetadataError,
) )
from controllers.service_api.wraps import ( from controllers.service_api.wraps import (
DatasetApiResource, DatasetApiResource,
@ -466,6 +467,101 @@ class DocumentIndexingStatusApi(DatasetApiResource):
return data return data
class DocumentDetailApi(DatasetApiResource):
METADATA_CHOICES = {"all", "only", "without"}
def get(self, tenant_id, dataset_id, document_id):
dataset_id = str(dataset_id)
document_id = str(document_id)
dataset = self.get_dataset(dataset_id, tenant_id)
document = DocumentService.get_document(dataset.id, document_id)
if not document:
raise NotFound("Document not found.")
if document.tenant_id != str(tenant_id):
raise Forbidden("No permission.")
metadata = request.args.get("metadata", "all")
if metadata not in self.METADATA_CHOICES:
raise InvalidMetadataError(f"Invalid metadata value: {metadata}")
if metadata == "only":
response = {"id": document.id, "doc_type": document.doc_type, "doc_metadata": document.doc_metadata_details}
elif metadata == "without":
dataset_process_rules = DatasetService.get_process_rules(dataset_id)
document_process_rules = document.dataset_process_rule.to_dict()
data_source_info = document.data_source_detail_dict
response = {
"id": document.id,
"position": document.position,
"data_source_type": document.data_source_type,
"data_source_info": data_source_info,
"dataset_process_rule_id": document.dataset_process_rule_id,
"dataset_process_rule": dataset_process_rules,
"document_process_rule": document_process_rules,
"name": document.name,
"created_from": document.created_from,
"created_by": document.created_by,
"created_at": document.created_at.timestamp(),
"tokens": document.tokens,
"indexing_status": document.indexing_status,
"completed_at": int(document.completed_at.timestamp()) if document.completed_at else None,
"updated_at": int(document.updated_at.timestamp()) if document.updated_at else None,
"indexing_latency": document.indexing_latency,
"error": document.error,
"enabled": document.enabled,
"disabled_at": int(document.disabled_at.timestamp()) if document.disabled_at else None,
"disabled_by": document.disabled_by,
"archived": document.archived,
"segment_count": document.segment_count,
"average_segment_length": document.average_segment_length,
"hit_count": document.hit_count,
"display_status": document.display_status,
"doc_form": document.doc_form,
"doc_language": document.doc_language,
}
else:
dataset_process_rules = DatasetService.get_process_rules(dataset_id)
document_process_rules = document.dataset_process_rule.to_dict()
data_source_info = document.data_source_detail_dict
response = {
"id": document.id,
"position": document.position,
"data_source_type": document.data_source_type,
"data_source_info": data_source_info,
"dataset_process_rule_id": document.dataset_process_rule_id,
"dataset_process_rule": dataset_process_rules,
"document_process_rule": document_process_rules,
"name": document.name,
"created_from": document.created_from,
"created_by": document.created_by,
"created_at": document.created_at.timestamp(),
"tokens": document.tokens,
"indexing_status": document.indexing_status,
"completed_at": int(document.completed_at.timestamp()) if document.completed_at else None,
"updated_at": int(document.updated_at.timestamp()) if document.updated_at else None,
"indexing_latency": document.indexing_latency,
"error": document.error,
"enabled": document.enabled,
"disabled_at": int(document.disabled_at.timestamp()) if document.disabled_at else None,
"disabled_by": document.disabled_by,
"archived": document.archived,
"doc_type": document.doc_type,
"doc_metadata": document.doc_metadata_details,
"segment_count": document.segment_count,
"average_segment_length": document.average_segment_length,
"hit_count": document.hit_count,
"display_status": document.display_status,
"doc_form": document.doc_form,
"doc_language": document.doc_language,
}
return response
api.add_resource( api.add_resource(
DocumentAddByTextApi, DocumentAddByTextApi,
"/datasets/<uuid:dataset_id>/document/create_by_text", "/datasets/<uuid:dataset_id>/document/create_by_text",
@ -489,3 +585,4 @@ api.add_resource(
api.add_resource(DocumentDeleteApi, "/datasets/<uuid:dataset_id>/documents/<uuid:document_id>") api.add_resource(DocumentDeleteApi, "/datasets/<uuid:dataset_id>/documents/<uuid:document_id>")
api.add_resource(DocumentListApi, "/datasets/<uuid:dataset_id>/documents") api.add_resource(DocumentListApi, "/datasets/<uuid:dataset_id>/documents")
api.add_resource(DocumentIndexingStatusApi, "/datasets/<uuid:dataset_id>/documents/<string:batch>/indexing-status") api.add_resource(DocumentIndexingStatusApi, "/datasets/<uuid:dataset_id>/documents/<string:batch>/indexing-status")
api.add_resource(DocumentDetailApi, "/datasets/<uuid:dataset_id>/documents/<uuid:document_id>")

@ -11,13 +11,13 @@ from flask_restful import Resource
from pydantic import BaseModel from pydantic import BaseModel
from sqlalchemy import select, update from sqlalchemy import select, update
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from werkzeug.exceptions import Forbidden, Unauthorized from werkzeug.exceptions import Forbidden, NotFound, Unauthorized
from extensions.ext_database import db from extensions.ext_database import db
from extensions.ext_redis import redis_client from extensions.ext_redis import redis_client
from libs.login import _get_user from libs.login import _get_user
from models.account import Account, Tenant, TenantAccountJoin, TenantStatus from models.account import Account, Tenant, TenantAccountJoin, TenantStatus
from models.dataset import RateLimitLog from models.dataset import Dataset, RateLimitLog
from models.model import ApiToken, App, EndUser from models.model import ApiToken, App, EndUser
from services.feature_service import FeatureService from services.feature_service import FeatureService
@ -317,3 +317,11 @@ def create_or_update_end_user_for_user_id(app_model: App, user_id: Optional[str]
class DatasetApiResource(Resource): class DatasetApiResource(Resource):
method_decorators = [validate_dataset_token] method_decorators = [validate_dataset_token]
def get_dataset(self, dataset_id: str, tenant_id: str) -> Dataset:
dataset = db.session.query(Dataset).filter(Dataset.id == dataset_id, Dataset.tenant_id == tenant_id).first()
if not dataset:
raise NotFound("Dataset not found.")
return dataset

@ -1124,6 +1124,129 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
<hr className='ml-0 mr-0' /> <hr className='ml-0 mr-0' />
<Heading
url='/datasets/{dataset_id}/documents/{document_id}'
method='GET'
title='Get Document Detail'
name='#get-document-detail'
/>
<Row>
<Col>
Get a document's detail.
### Path
- `dataset_id` (string) Dataset ID
- `document_id` (string) Document ID
### Query
- `metadata` (string) Metadata filter, can be `all`, `only`, or `without`. Default is `all`.
### Response
Returns the document's detail.
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="GET" label="/datasets/{dataset_id}/documents/{document_id}" targetCode={`curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### Response Example
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"id": "f46ae30c-5c11-471b-96d0-464f5f32a7b2",
"position": 1,
"data_source_type": "upload_file",
"data_source_info": {
"upload_file": {
...
}
},
"dataset_process_rule_id": "24b99906-845e-499f-9e3c-d5565dd6962c",
"dataset_process_rule": {
"mode": "hierarchical",
"rules": {
"pre_processing_rules": [
{
"id": "remove_extra_spaces",
"enabled": true
},
{
"id": "remove_urls_emails",
"enabled": false
}
],
"segmentation": {
"separator": "**********page_ending**********",
"max_tokens": 1024,
"chunk_overlap": 0
},
"parent_mode": "paragraph",
"subchunk_segmentation": {
"separator": "\n",
"max_tokens": 512,
"chunk_overlap": 0
}
}
},
"document_process_rule": {
"id": "24b99906-845e-499f-9e3c-d5565dd6962c",
"dataset_id": "48a0db76-d1a9-46c1-ae35-2baaa919a8a9",
"mode": "hierarchical",
"rules": {
"pre_processing_rules": [
{
"id": "remove_extra_spaces",
"enabled": true
},
{
"id": "remove_urls_emails",
"enabled": false
}
],
"segmentation": {
"separator": "**********page_ending**********",
"max_tokens": 1024,
"chunk_overlap": 0
},
"parent_mode": "paragraph",
"subchunk_segmentation": {
"separator": "\n",
"max_tokens": 512,
"chunk_overlap": 0
}
}
},
"name": "xxxx",
"created_from": "web",
"created_by": "17f71940-a7b5-4c77-b60f-2bd645c1ffa0",
"created_at": 1750464191,
"tokens": null,
"indexing_status": "waiting",
"completed_at": null,
"updated_at": 1750464191,
"indexing_latency": null,
"error": null,
"enabled": true,
"disabled_at": null,
"disabled_by": null,
"archived": false,
"segment_count": 0,
"average_segment_length": 0,
"hit_count": null,
"display_status": "queuing",
"doc_form": "hierarchical_model",
"doc_language": "Chinese Simplified"
}
```
</CodeGroup>
</Col>
</Row>
___
<hr className='ml-0 mr-0' />
<Heading <Heading
url='/datasets/{dataset_id}/documents/status/{action}' url='/datasets/{dataset_id}/documents/status/{action}'
method='PATCH' method='PATCH'

@ -881,6 +881,130 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
<hr className='ml-0 mr-0' /> <hr className='ml-0 mr-0' />
<Heading
url='/datasets/{dataset_id}/documents/{document_id}'
method='GET'
title='ドキュメントの詳細を取得'
name='#get-document-detail'
/>
<Row>
<Col>
ドキュメントの詳細を取得.
### Path
- `dataset_id` (string) ナレッジベースID
- `document_id` (string) ドキュメントID
### Query
- `metadata` (string) metadataのフィルター条件 `all`、`only`、または`without`。デフォルトは `all`。
### Response
ナレッジベースドキュメントの詳細を返す.
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="GET" label="/datasets/{dataset_id}/documents/{document_id}" targetCode={`curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### Response Example
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"id": "f46ae30c-5c11-471b-96d0-464f5f32a7b2",
"position": 1,
"data_source_type": "upload_file",
"data_source_info": {
"upload_file": {
...
}
},
"dataset_process_rule_id": "24b99906-845e-499f-9e3c-d5565dd6962c",
"dataset_process_rule": {
"mode": "hierarchical",
"rules": {
"pre_processing_rules": [
{
"id": "remove_extra_spaces",
"enabled": true
},
{
"id": "remove_urls_emails",
"enabled": false
}
],
"segmentation": {
"separator": "**********page_ending**********",
"max_tokens": 1024,
"chunk_overlap": 0
},
"parent_mode": "paragraph",
"subchunk_segmentation": {
"separator": "\n",
"max_tokens": 512,
"chunk_overlap": 0
}
}
},
"document_process_rule": {
"id": "24b99906-845e-499f-9e3c-d5565dd6962c",
"dataset_id": "48a0db76-d1a9-46c1-ae35-2baaa919a8a9",
"mode": "hierarchical",
"rules": {
"pre_processing_rules": [
{
"id": "remove_extra_spaces",
"enabled": true
},
{
"id": "remove_urls_emails",
"enabled": false
}
],
"segmentation": {
"separator": "**********page_ending**********",
"max_tokens": 1024,
"chunk_overlap": 0
},
"parent_mode": "paragraph",
"subchunk_segmentation": {
"separator": "\n",
"max_tokens": 512,
"chunk_overlap": 0
}
}
},
"name": "xxxx",
"created_from": "web",
"created_by": "17f71940-a7b5-4c77-b60f-2bd645c1ffa0",
"created_at": 1750464191,
"tokens": null,
"indexing_status": "waiting",
"completed_at": null,
"updated_at": 1750464191,
"indexing_latency": null,
"error": null,
"enabled": true,
"disabled_at": null,
"disabled_by": null,
"archived": false,
"segment_count": 0,
"average_segment_length": 0,
"hit_count": null,
"display_status": "queuing",
"doc_form": "hierarchical_model",
"doc_language": "Chinese Simplified"
}
```
</CodeGroup>
</Col>
</Row>
___
<hr className='ml-0 mr-0' />
<Heading <Heading
url='/datasets/{dataset_id}/documents/status/{action}' url='/datasets/{dataset_id}/documents/status/{action}'
method='PATCH' method='PATCH'

@ -1131,6 +1131,130 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
<hr className='ml-0 mr-0' /> <hr className='ml-0 mr-0' />
<Heading
url='/datasets/{dataset_id}/documents/{document_id}'
method='GET'
title='获取文档详情'
name='#get-document-detail'
/>
<Row>
<Col>
获取文档详情.
### Path
- `dataset_id` (string) 知识库 ID
- `document_id` (string) 文档 ID
### Query
- `metadata` (string) metadata 过滤条件 `all`, `only`, 或者 `without`. 默认是 `all`.
### Response
返回知识库文档的详情.
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="GET" label="/datasets/{dataset_id}/documents/{document_id}" targetCode={`curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### Response Example
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"id": "f46ae30c-5c11-471b-96d0-464f5f32a7b2",
"position": 1,
"data_source_type": "upload_file",
"data_source_info": {
"upload_file": {
...
}
},
"dataset_process_rule_id": "24b99906-845e-499f-9e3c-d5565dd6962c",
"dataset_process_rule": {
"mode": "hierarchical",
"rules": {
"pre_processing_rules": [
{
"id": "remove_extra_spaces",
"enabled": true
},
{
"id": "remove_urls_emails",
"enabled": false
}
],
"segmentation": {
"separator": "**********page_ending**********",
"max_tokens": 1024,
"chunk_overlap": 0
},
"parent_mode": "paragraph",
"subchunk_segmentation": {
"separator": "\n",
"max_tokens": 512,
"chunk_overlap": 0
}
}
},
"document_process_rule": {
"id": "24b99906-845e-499f-9e3c-d5565dd6962c",
"dataset_id": "48a0db76-d1a9-46c1-ae35-2baaa919a8a9",
"mode": "hierarchical",
"rules": {
"pre_processing_rules": [
{
"id": "remove_extra_spaces",
"enabled": true
},
{
"id": "remove_urls_emails",
"enabled": false
}
],
"segmentation": {
"separator": "**********page_ending**********",
"max_tokens": 1024,
"chunk_overlap": 0
},
"parent_mode": "paragraph",
"subchunk_segmentation": {
"separator": "\n",
"max_tokens": 512,
"chunk_overlap": 0
}
}
},
"name": "xxxx",
"created_from": "web",
"created_by": "17f71940-a7b5-4c77-b60f-2bd645c1ffa0",
"created_at": 1750464191,
"tokens": null,
"indexing_status": "waiting",
"completed_at": null,
"updated_at": 1750464191,
"indexing_latency": null,
"error": null,
"enabled": true,
"disabled_at": null,
"disabled_by": null,
"archived": false,
"segment_count": 0,
"average_segment_length": 0,
"hit_count": null,
"display_status": "queuing",
"doc_form": "hierarchical_model",
"doc_language": "Chinese Simplified"
}
```
</CodeGroup>
</Col>
</Row>
___
<hr className='ml-0 mr-0' />
<Heading <Heading
url='/datasets/{dataset_id}/documents/status/{action}' url='/datasets/{dataset_id}/documents/status/{action}'
method='PATCH' method='PATCH'

@ -14,7 +14,7 @@ const HeaderWrapper = ({
}: HeaderWrapperProps) => { }: HeaderWrapperProps) => {
const pathname = usePathname() const pathname = usePathname()
const isBordered = ['/apps', '/datasets', '/datasets/create', '/tools'].includes(pathname) const isBordered = ['/apps', '/datasets', '/datasets/create', '/tools'].includes(pathname)
// // Check if the current path is a workflow canvas & fullscreen // Check if the current path is a workflow canvas & fullscreen
const inWorkflowCanvas = pathname.endsWith('/workflow') const inWorkflowCanvas = pathname.endsWith('/workflow')
const workflowCanvasMaximize = localStorage.getItem('workflow-canvas-maximize') === 'true' const workflowCanvasMaximize = localStorage.getItem('workflow-canvas-maximize') === 'true'
const [hideHeader, setHideHeader] = useState(workflowCanvasMaximize) const [hideHeader, setHideHeader] = useState(workflowCanvasMaximize)
@ -25,14 +25,12 @@ const HeaderWrapper = ({
setHideHeader(v.payload) setHideHeader(v.payload)
}) })
if (hideHeader && inWorkflowCanvas)
return null
return ( return (
<div className={classNames( <div className={classNames(
'sticky left-0 right-0 top-0 z-[15] flex min-h-[56px] shrink-0 grow-0 basis-auto flex-col', 'sticky left-0 right-0 top-0 z-[15] flex min-h-[56px] shrink-0 grow-0 basis-auto flex-col',
s.header, s.header,
isBordered ? 'border-b border-divider-regular' : '', isBordered ? 'border-b border-divider-regular' : '',
hideHeader && inWorkflowCanvas && 'hidden',
)} )}
> >
{children} {children}

@ -8,6 +8,7 @@ import type { WorkflowProps } from '@/app/components/workflow'
import WorkflowChildren from './workflow-children' import WorkflowChildren from './workflow-children'
import { import {
useNodesSyncDraft, useNodesSyncDraft,
useSetWorkflowVarsWithValue,
useWorkflowRefreshDraft, useWorkflowRefreshDraft,
useWorkflowRun, useWorkflowRun,
useWorkflowStartRun, useWorkflowStartRun,
@ -61,6 +62,7 @@ const WorkflowMain = ({
handleWorkflowStartRunInChatflow, handleWorkflowStartRunInChatflow,
handleWorkflowStartRunInWorkflow, handleWorkflowStartRunInWorkflow,
} = useWorkflowStartRun() } = useWorkflowStartRun()
const { fetchInspectVars } = useSetWorkflowVarsWithValue()
const hooksStore = useMemo(() => { const hooksStore = useMemo(() => {
return { return {
@ -75,6 +77,7 @@ const WorkflowMain = ({
handleStartWorkflowRun, handleStartWorkflowRun,
handleWorkflowStartRunInChatflow, handleWorkflowStartRunInChatflow,
handleWorkflowStartRunInWorkflow, handleWorkflowStartRunInWorkflow,
fetchInspectVars,
} }
}, [ }, [
syncWorkflowDraftWhenPageClose, syncWorkflowDraftWhenPageClose,
@ -88,6 +91,7 @@ const WorkflowMain = ({
handleStartWorkflowRun, handleStartWorkflowRun,
handleWorkflowStartRunInChatflow, handleWorkflowStartRunInChatflow,
handleWorkflowStartRunInWorkflow, handleWorkflowStartRunInWorkflow,
fetchInspectVars,
]) ])
return ( return (

@ -5,3 +5,4 @@ export * from './use-workflow-run'
export * from './use-workflow-start-run' export * from './use-workflow-start-run'
export * from './use-is-chat-mode' export * from './use-is-chat-mode'
export * from './use-workflow-refresh-draft' export * from './use-workflow-refresh-draft'
export * from './use-fetch-workflow-inspect-vars'

@ -1,11 +1,12 @@
import type { NodeWithVar, VarInInspect } from '@/types/workflow' import type { NodeWithVar, VarInInspect } from '@/types/workflow'
import { useWorkflowStore } from '../../workflow/store' import { useWorkflowStore } from '@/app/components/workflow/store'
import { useStoreApi } from 'reactflow' import { useStoreApi } from 'reactflow'
import type { Node } from '@/app/components/workflow/types' import type { Node } from '@/app/components/workflow/types'
import { fetchAllInspectVars } from '@/service/workflow' import { fetchAllInspectVars } from '@/service/workflow'
import { useInvalidateConversationVarValues, useInvalidateSysVarValues } from '@/service/use-workflow' import { useInvalidateConversationVarValues, useInvalidateSysVarValues } from '@/service/use-workflow'
import { useNodesInteractionsWithoutSync } from '../../workflow/hooks/use-nodes-interactions-without-sync' import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync'
const useSetWorkflowVarsWithValue = () => {
export const useSetWorkflowVarsWithValue = () => {
const workflowStore = useWorkflowStore() const workflowStore = useWorkflowStore()
const { setNodesWithInspectVars, appId } = workflowStore.getState() const { setNodesWithInspectVars, appId } = workflowStore.getState()
const store = useStoreApi() const store = useStoreApi()
@ -64,5 +65,3 @@ const useSetWorkflowVarsWithValue = () => {
fetchInspectVars, fetchInspectVars,
} }
} }
export default useSetWorkflowVarsWithValue

@ -20,7 +20,7 @@ import type { VersionHistory } from '@/types/workflow'
import { noop } from 'lodash-es' import { noop } from 'lodash-es'
import { useNodesSyncDraft } from './use-nodes-sync-draft' import { useNodesSyncDraft } from './use-nodes-sync-draft'
import { useInvalidAllLastRun } from '@/service/use-workflow' import { useInvalidAllLastRun } from '@/service/use-workflow'
import useSetWorkflowVarsWithValue from './use-fetch-workflow-inspect-vars' import { useSetWorkflowVarsWithValue } from '@/app/components/workflow/hooks/use-set-workflow-vars-with-value'
export const useWorkflowRun = () => { export const useWorkflowRun = () => {
const store = useStoreApi() const store = useStoreApi()

@ -7,6 +7,7 @@ import {
} from 'zustand' } from 'zustand'
import { createStore } from 'zustand/vanilla' import { createStore } from 'zustand/vanilla'
import { HooksStoreContext } from './provider' import { HooksStoreContext } from './provider'
import type { IOtherOptions } from '@/service/base'
type CommonHooksFnMap = { type CommonHooksFnMap = {
doSyncWorkflowDraft: ( doSyncWorkflowDraft: (
@ -22,11 +23,12 @@ type CommonHooksFnMap = {
handleBackupDraft: () => void handleBackupDraft: () => void
handleLoadBackupDraft: () => void handleLoadBackupDraft: () => void
handleRestoreFromPublishedWorkflow: (...args: any[]) => void handleRestoreFromPublishedWorkflow: (...args: any[]) => void
handleRun: (...args: any[]) => void handleRun: (params: any, callback?: IOtherOptions,) => void
handleStopRun: (...args: any[]) => void handleStopRun: (...args: any[]) => void
handleStartWorkflowRun: () => void handleStartWorkflowRun: () => void
handleWorkflowStartRunInWorkflow: () => void handleWorkflowStartRunInWorkflow: () => void
handleWorkflowStartRunInChatflow: () => void handleWorkflowStartRunInChatflow: () => void
fetchInspectVars: () => Promise<void>
} }
export type Shape = { export type Shape = {
@ -45,6 +47,7 @@ export const createHooksStore = ({
handleStartWorkflowRun = noop, handleStartWorkflowRun = noop,
handleWorkflowStartRunInWorkflow = noop, handleWorkflowStartRunInWorkflow = noop,
handleWorkflowStartRunInChatflow = noop, handleWorkflowStartRunInChatflow = noop,
fetchInspectVars = async () => noop(),
}: Partial<Shape>) => { }: Partial<Shape>) => {
return createStore<Shape>(set => ({ return createStore<Shape>(set => ({
refreshAll: props => set(state => ({ ...state, ...props })), refreshAll: props => set(state => ({ ...state, ...props })),
@ -59,6 +62,7 @@ export const createHooksStore = ({
handleStartWorkflowRun, handleStartWorkflowRun,
handleWorkflowStartRunInWorkflow, handleWorkflowStartRunInWorkflow,
handleWorkflowStartRunInChatflow, handleWorkflowStartRunInChatflow,
fetchInspectVars,
})) }))
} }

@ -17,3 +17,5 @@ export * from './use-workflow-interactions'
export * from './use-workflow-mode' export * from './use-workflow-mode'
export * from './use-format-time-from-now' export * from './use-format-time-from-now'
export * from './use-workflow-refresh-draft' export * from './use-workflow-refresh-draft'
export * from './use-inspect-vars-crud'
export * from './use-set-workflow-vars-with-value'

@ -0,0 +1,9 @@
import { useHooksStore } from '@/app/components/workflow/hooks-store'
export const useSetWorkflowVarsWithValue = () => {
const fetchInspectVars = useHooksStore(s => s.doSyncWorkflowDraft)
return {
fetchInspectVars,
}
}

@ -42,6 +42,7 @@ import {
useNodesSyncDraft, useNodesSyncDraft,
usePanelInteractions, usePanelInteractions,
useSelectionInteractions, useSelectionInteractions,
useSetWorkflowVarsWithValue,
useShortcuts, useShortcuts,
useWorkflow, useWorkflow,
useWorkflowReadOnly, useWorkflowReadOnly,
@ -82,7 +83,6 @@ import Confirm from '@/app/components/base/confirm'
import DatasetsDetailProvider from './datasets-detail-store/provider' import DatasetsDetailProvider from './datasets-detail-store/provider'
import { HooksStoreContextProvider } from './hooks-store' import { HooksStoreContextProvider } from './hooks-store'
import type { Shape as HooksStoreShape } from './hooks-store' import type { Shape as HooksStoreShape } from './hooks-store'
import useSetWorkflowVarsWithValue from '../workflow-app/hooks/use-fetch-workflow-inspect-vars'
const nodeTypes = { const nodeTypes = {
[CUSTOM_NODE]: CustomNode, [CUSTOM_NODE]: CustomNode,

@ -184,9 +184,11 @@ const VarReferencePicker: FC<Props> = ({
return startNode?.data return startNode?.data
const node = getNodeInfoById(availableNodes, outputVarNodeId)?.data const node = getNodeInfoById(availableNodes, outputVarNodeId)?.data
return { if (node) {
...node, return {
id: outputVarNodeId, ...node,
id: outputVarNodeId,
}
} }
}, [value, hasValue, isConstant, isIterationVar, iterationNode, availableNodes, outputVarNodeId, startNode, isLoopVar, loopNode]) }, [value, hasValue, isConstant, isIterationVar, iterationNode, availableNodes, outputVarNodeId, startNode, isLoopVar, loopNode])

@ -8,7 +8,10 @@ import {
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { produce, setAutoFreeze } from 'immer' import { produce, setAutoFreeze } from 'immer'
import { uniqBy } from 'lodash-es' import { uniqBy } from 'lodash-es'
import { useWorkflowRun } from '../../hooks' import {
useSetWorkflowVarsWithValue,
useWorkflowRun,
} from '../../hooks'
import { NodeRunningStatus, WorkflowRunningStatus } from '../../types' import { NodeRunningStatus, WorkflowRunningStatus } from '../../types'
import { useWorkflowStore } from '../../store' import { useWorkflowStore } from '../../store'
import { DEFAULT_ITER_TIMES, DEFAULT_LOOP_TIMES } from '../../constants' import { DEFAULT_ITER_TIMES, DEFAULT_LOOP_TIMES } from '../../constants'
@ -32,7 +35,6 @@ import type { FileEntity } from '@/app/components/base/file-uploader/types'
import { getThreadMessages } from '@/app/components/base/chat/utils' import { getThreadMessages } from '@/app/components/base/chat/utils'
import { useInvalidAllLastRun } from '@/service/use-workflow' import { useInvalidAllLastRun } from '@/service/use-workflow'
import { useParams } from 'next/navigation' import { useParams } from 'next/navigation'
import useSetWorkflowVarsWithValue from '@/app/components/workflow-app/hooks/use-fetch-workflow-inspect-vars'
type GetAbortController = (abortController: AbortController) => void type GetAbortController = (abortController: AbortController) => void
type SendCallback = { type SendCallback = {
@ -499,7 +501,7 @@ export const useChat = (
}, },
}, },
) )
}, [threadMessages, chatTree.length, updateCurrentQAOnTree, handleResponding, formSettings?.inputsForm, handleRun, notify, t, config?.suggested_questions_after_answer?.enabled]) }, [threadMessages, chatTree.length, updateCurrentQAOnTree, handleResponding, formSettings?.inputsForm, handleRun, notify, t, config?.suggested_questions_after_answer?.enabled, fetchInspectVars, invalidAllLastRun])
return { return {
conversationId: conversationId.current, conversationId: conversationId.current,

@ -63,6 +63,17 @@ const Right = ({
resetConversationVar(currentNodeVar.var.id) resetConversationVar(currentNodeVar.var.id)
} }
const getCopyContent = () => {
const value = currentNodeVar?.var.value
if (value === null || value === undefined)
return ''
if (typeof value === 'object')
return JSON.stringify(value)
return String(value)
}
return ( return (
<div className={cn('flex h-full flex-col')}> <div className={cn('flex h-full flex-col')}>
{/* header */} {/* header */}
@ -124,7 +135,7 @@ const Right = ({
</Tooltip> </Tooltip>
)} )}
{currentNodeVar.var.value_type !== 'secret' && ( {currentNodeVar.var.value_type !== 'secret' && (
<CopyFeedback content={currentNodeVar.var.value ? JSON.stringify(currentNodeVar.var.value) : ''} /> <CopyFeedback content={getCopyContent()} />
)} )}
</> </>
)} )}

Loading…
Cancel
Save