From 9588a644879df5a38a700ff7a36cd5730e516971 Mon Sep 17 00:00:00 2001 From: NFish Date: Mon, 30 Jun 2025 18:28:31 +0800 Subject: [PATCH 1/6] fix: keep search params in web app url when needs authorize (#21717) --- web/app/(shareLayout)/layout.tsx | 9 ++++++--- .../(shareLayout)/webapp-signin/check-code/page.tsx | 7 +++++-- .../components/external-member-sso-auth.tsx | 5 ++++- .../components/mail-and-password-auth.tsx | 8 ++++++-- .../webapp-signin/components/sso-auth.tsx | 5 ++++- web/app/(shareLayout)/webapp-signin/page.tsx | 11 +++++++---- web/app/components/share/text-generation/index.tsx | 8 -------- web/service/base.ts | 2 +- 8 files changed, 33 insertions(+), 22 deletions(-) diff --git a/web/app/(shareLayout)/layout.tsx b/web/app/(shareLayout)/layout.tsx index 78b8835009..d057ba7599 100644 --- a/web/app/(shareLayout)/layout.tsx +++ b/web/app/(shareLayout)/layout.tsx @@ -25,10 +25,13 @@ const Layout: FC<{ } let appCode: string | null = null - if (redirectUrl) - appCode = redirectUrl?.split('/').pop() || null - else + if (redirectUrl) { + const url = new URL(`${window.location.origin}${decodeURIComponent(redirectUrl)}`) + appCode = url.pathname.split('/').pop() || null + } + else { appCode = pathname.split('/').pop() || null + } if (!appCode) return diff --git a/web/app/(shareLayout)/webapp-signin/check-code/page.tsx b/web/app/(shareLayout)/webapp-signin/check-code/page.tsx index 1b8f18c98f..a2ba620ace 100644 --- a/web/app/(shareLayout)/webapp-signin/check-code/page.tsx +++ b/web/app/(shareLayout)/webapp-signin/check-code/page.tsx @@ -25,7 +25,10 @@ export default function CheckCode() { const redirectUrl = searchParams.get('redirect_url') const getAppCodeFromRedirectUrl = useCallback(() => { - const appCode = redirectUrl?.split('/').pop() + if (!redirectUrl) + return null + const url = new URL(`${window.location.origin}${decodeURIComponent(redirectUrl)}`) + const appCode = url.pathname.split('/').pop() if (!appCode) return null @@ -62,7 +65,7 @@ export default function CheckCode() { localStorage.setItem('webapp_access_token', ret.data.access_token) const tokenResp = await fetchAccessToken({ appCode, webAppAccessToken: ret.data.access_token }) await setAccessToken(appCode, tokenResp.access_token) - router.replace(redirectUrl) + router.replace(decodeURIComponent(redirectUrl)) } } catch (error) { console.error(error) } diff --git a/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx b/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx index e9b15ae331..612a9677a6 100644 --- a/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx +++ b/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx @@ -23,7 +23,10 @@ const ExternalMemberSSOAuth = () => { } const getAppCodeFromRedirectUrl = useCallback(() => { - const appCode = redirectUrl?.split('/').pop() + if (!redirectUrl) + return null + const url = new URL(`${window.location.origin}${decodeURIComponent(redirectUrl)}`) + const appCode = url.pathname.split('/').pop() if (!appCode) return null diff --git a/web/app/(shareLayout)/webapp-signin/components/mail-and-password-auth.tsx b/web/app/(shareLayout)/webapp-signin/components/mail-and-password-auth.tsx index d9e56af1b8..2201b28a2f 100644 --- a/web/app/(shareLayout)/webapp-signin/components/mail-and-password-auth.tsx +++ b/web/app/(shareLayout)/webapp-signin/components/mail-and-password-auth.tsx @@ -1,3 +1,4 @@ +'use client' import Link from 'next/link' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -33,7 +34,10 @@ export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAut const redirectUrl = searchParams.get('redirect_url') const getAppCodeFromRedirectUrl = useCallback(() => { - const appCode = redirectUrl?.split('/').pop() + if (!redirectUrl) + return null + const url = new URL(`${window.location.origin}${decodeURIComponent(redirectUrl)}`) + const appCode = url.pathname.split('/').pop() if (!appCode) return null @@ -87,7 +91,7 @@ export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAut localStorage.setItem('webapp_access_token', res.data.access_token) const tokenResp = await fetchAccessToken({ appCode, webAppAccessToken: res.data.access_token }) await setAccessToken(appCode, tokenResp.access_token) - router.replace(redirectUrl) + router.replace(decodeURIComponent(redirectUrl)) } else { Toast.notify({ diff --git a/web/app/(shareLayout)/webapp-signin/components/sso-auth.tsx b/web/app/(shareLayout)/webapp-signin/components/sso-auth.tsx index 5d649322ba..bcba572644 100644 --- a/web/app/(shareLayout)/webapp-signin/components/sso-auth.tsx +++ b/web/app/(shareLayout)/webapp-signin/components/sso-auth.tsx @@ -23,7 +23,10 @@ const SSOAuth: FC = ({ const redirectUrl = searchParams.get('redirect_url') const getAppCodeFromRedirectUrl = useCallback(() => { - const appCode = redirectUrl?.split('/').pop() + if (!redirectUrl) + return null + const url = new URL(`${window.location.origin}${decodeURIComponent(redirectUrl)}`) + const appCode = url.pathname.split('/').pop() if (!appCode) return null diff --git a/web/app/(shareLayout)/webapp-signin/page.tsx b/web/app/(shareLayout)/webapp-signin/page.tsx index 07b7c88430..967516c416 100644 --- a/web/app/(shareLayout)/webapp-signin/page.tsx +++ b/web/app/(shareLayout)/webapp-signin/page.tsx @@ -46,7 +46,10 @@ const WebSSOForm: FC = () => { } const getAppCodeFromRedirectUrl = useCallback(() => { - const appCode = redirectUrl?.split('/').pop() + if (!redirectUrl) + return null + const url = new URL(`${window.location.origin}${decodeURIComponent(redirectUrl)}`) + const appCode = url.pathname.split('/').pop() if (!appCode) return null @@ -63,20 +66,20 @@ const WebSSOForm: FC = () => { localStorage.setItem('webapp_access_token', tokenFromUrl) const tokenResp = await fetchAccessToken({ appCode, webAppAccessToken: tokenFromUrl }) await setAccessToken(appCode, tokenResp.access_token) - router.replace(redirectUrl) + router.replace(decodeURIComponent(redirectUrl)) return } if (appCode && redirectUrl && localStorage.getItem('webapp_access_token')) { const tokenResp = await fetchAccessToken({ appCode, webAppAccessToken: localStorage.getItem('webapp_access_token') }) await setAccessToken(appCode, tokenResp.access_token) - router.replace(redirectUrl) + router.replace(decodeURIComponent(redirectUrl)) } })() }, [getAppCodeFromRedirectUrl, redirectUrl, router, tokenFromUrl, message]) useEffect(() => { if (webAppAccessMode && webAppAccessMode === AccessMode.PUBLIC && redirectUrl) - router.replace(redirectUrl) + router.replace(decodeURIComponent(redirectUrl)) }, [webAppAccessMode, router, redirectUrl]) if (tokenFromUrl) { diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx index 9dc7ffcd79..4be6b18958 100644 --- a/web/app/components/share/text-generation/index.tsx +++ b/web/app/components/share/text-generation/index.tsx @@ -85,14 +85,6 @@ const TextGeneration: FC = ({ const router = useRouter() const pathname = usePathname() - useEffect(() => { - const params = new URLSearchParams(searchParams) - if (params.has('mode')) { - params.delete('mode') - router.replace(`${pathname}?${params.toString()}`) - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) // Notice this situation isCallBatchAPI but not in batch tab const [isCallBatchAPI, setIsCallBatchAPI] = useState(false) diff --git a/web/service/base.ts b/web/service/base.ts index ba398c07a6..49377be912 100644 --- a/web/service/base.ts +++ b/web/service/base.ts @@ -110,7 +110,7 @@ function unicodeToChar(text: string) { function requiredWebSSOLogin(message?: string, code?: number) { const params = new URLSearchParams() - params.append('redirect_url', globalThis.location.pathname) + params.append('redirect_url', encodeURIComponent(`${globalThis.location.pathname}${globalThis.location.search}`)) if (message) params.append('message', message) if (code) From 96d27d708708ab45781863329d2330ece03bf5b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Mon, 30 Jun 2025 22:00:35 +0800 Subject: [PATCH 2/6] fix: enter and exit full canvas cause nav items missing (#21691) --- web/app/components/header/header-wrapper.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/web/app/components/header/header-wrapper.tsx b/web/app/components/header/header-wrapper.tsx index cd4d229b05..6486e3707a 100644 --- a/web/app/components/header/header-wrapper.tsx +++ b/web/app/components/header/header-wrapper.tsx @@ -14,7 +14,7 @@ const HeaderWrapper = ({ }: HeaderWrapperProps) => { const pathname = usePathname() 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 workflowCanvasMaximize = localStorage.getItem('workflow-canvas-maximize') === 'true' const [hideHeader, setHideHeader] = useState(workflowCanvasMaximize) @@ -25,14 +25,12 @@ const HeaderWrapper = ({ setHideHeader(v.payload) }) - if (hideHeader && inWorkflowCanvas) - return null - return (
{children} From 55a6b330ecd882ea89c99c3fbd3f35e4169ab561 Mon Sep 17 00:00:00 2001 From: Ganondorf <364776488@qq.com> Date: Mon, 30 Jun 2025 22:13:56 +0800 Subject: [PATCH 3/6] Add get document detail service api (#21700) Co-authored-by: lizb --- .../service_api/dataset/document.py | 99 +++++++++++++- api/controllers/service_api/wraps.py | 12 +- .../datasets/template/template.en.mdx | 123 +++++++++++++++++ .../datasets/template/template.ja.mdx | 124 ++++++++++++++++++ .../datasets/template/template.zh.mdx | 124 ++++++++++++++++++ 5 files changed, 479 insertions(+), 3 deletions(-) diff --git a/api/controllers/service_api/dataset/document.py b/api/controllers/service_api/dataset/document.py index 6213fad173..d64d9df059 100644 --- a/api/controllers/service_api/dataset/document.py +++ b/api/controllers/service_api/dataset/document.py @@ -3,7 +3,7 @@ import json from flask import request from flask_restful import marshal, reqparse from sqlalchemy import desc, select -from werkzeug.exceptions import NotFound +from werkzeug.exceptions import Forbidden, NotFound import services from controllers.common.errors import FilenameNotExistsError @@ -18,6 +18,7 @@ from controllers.service_api.app.error import ( from controllers.service_api.dataset.error import ( ArchivedDocumentImmutableError, DocumentIndexingError, + InvalidMetadataError, ) from controllers.service_api.wraps import ( DatasetApiResource, @@ -466,6 +467,101 @@ class DocumentIndexingStatusApi(DatasetApiResource): 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( DocumentAddByTextApi, "/datasets//document/create_by_text", @@ -489,3 +585,4 @@ api.add_resource( api.add_resource(DocumentDeleteApi, "/datasets//documents/") api.add_resource(DocumentListApi, "/datasets//documents") api.add_resource(DocumentIndexingStatusApi, "/datasets//documents//indexing-status") +api.add_resource(DocumentDetailApi, "/datasets//documents/") diff --git a/api/controllers/service_api/wraps.py b/api/controllers/service_api/wraps.py index d3316a5159..5b919a68d4 100644 --- a/api/controllers/service_api/wraps.py +++ b/api/controllers/service_api/wraps.py @@ -11,13 +11,13 @@ from flask_restful import Resource from pydantic import BaseModel from sqlalchemy import select, update 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_redis import redis_client from libs.login import _get_user 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 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): 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 diff --git a/web/app/(commonLayout)/datasets/template/template.en.mdx b/web/app/(commonLayout)/datasets/template/template.en.mdx index 91293768b7..ebb2e6a806 100644 --- a/web/app/(commonLayout)/datasets/template/template.en.mdx +++ b/web/app/(commonLayout)/datasets/template/template.en.mdx @@ -1124,6 +1124,129 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
+ + + + 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. + + + ### Request Example + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \ + -H 'Authorization: Bearer {api_key}' + ``` + + + ### Response Example + + ```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" + } + ``` + + + +___ +
+ + + + + ドキュメントの詳細を取得. + ### Path + - `dataset_id` (string) ナレッジベースID + - `document_id` (string) ドキュメントID + + ### Query + - `metadata` (string) metadataのフィルター条件 `all`、`only`、または`without`。デフォルトは `all`。 + + ### Response + ナレッジベースドキュメントの詳細を返す. + + + ### Request Example + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \ + -H 'Authorization: Bearer {api_key}' + ``` + + + ### Response Example + + ```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" + } + ``` + + + +___ +
+ + + + + + 获取文档详情. + ### Path + - `dataset_id` (string) 知识库 ID + - `document_id` (string) 文档 ID + + ### Query + - `metadata` (string) metadata 过滤条件 `all`, `only`, 或者 `without`. 默认是 `all`. + + ### Response + 返回知识库文档的详情. + + + ### Request Example + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \ + -H 'Authorization: Bearer {api_key}' + ``` + + + ### Response Example + + ```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" + } + ``` + + + +___ +
+ + Date: Mon, 30 Jun 2025 22:14:29 +0800 Subject: [PATCH 4/6] fix: copy inspect variable value get extra quotes (#21680) --- .../components/workflow/variable-inspect/right.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/web/app/components/workflow/variable-inspect/right.tsx b/web/app/components/workflow/variable-inspect/right.tsx index 851078d972..6ddd0d47d3 100644 --- a/web/app/components/workflow/variable-inspect/right.tsx +++ b/web/app/components/workflow/variable-inspect/right.tsx @@ -63,6 +63,17 @@ const Right = ({ 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 (
{/* header */} @@ -124,7 +135,7 @@ const Right = ({ )} {currentNodeVar.var.value_type !== 'secret' && ( - + )} )} From 7c76458b18116e9e7203f6bbc40bdc843fe8dcd9 Mon Sep 17 00:00:00 2001 From: Minamiyama Date: Tue, 1 Jul 2025 10:32:00 +0800 Subject: [PATCH 5/6] fix: fix node valid detect (#21709) --- .../_base/components/variable/var-reference-picker.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx index 2c89e722cd..23ccea2572 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx @@ -184,9 +184,11 @@ const VarReferencePicker: FC = ({ return startNode?.data const node = getNodeInfoById(availableNodes, outputVarNodeId)?.data - return { - ...node, - id: outputVarNodeId, + if (node) { + return { + ...node, + id: outputVarNodeId, + } } }, [value, hasValue, isConstant, isIterationVar, iterationNode, availableNodes, outputVarNodeId, startNode, isLoopVar, loopNode]) From 931a45dbfce62f859911534bd234d7cad3bc61c3 Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Tue, 1 Jul 2025 11:04:22 +0800 Subject: [PATCH 6/6] chore: workflow last run --- .../components/workflow-app/components/workflow-main.tsx | 4 ++++ web/app/components/workflow-app/hooks/index.ts | 1 + .../hooks/use-fetch-workflow-inspect-vars.ts | 9 ++++----- .../components/workflow-app/hooks/use-workflow-run.ts | 2 +- web/app/components/workflow/hooks-store/store.ts | 6 +++++- web/app/components/workflow/hooks/index.ts | 2 ++ .../workflow/hooks/use-set-workflow-vars-with-value.ts | 9 +++++++++ web/app/components/workflow/index.tsx | 2 +- .../components/workflow/panel/debug-and-preview/hooks.ts | 8 +++++--- 9 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 web/app/components/workflow/hooks/use-set-workflow-vars-with-value.ts diff --git a/web/app/components/workflow-app/components/workflow-main.tsx b/web/app/components/workflow-app/components/workflow-main.tsx index 6be190c897..c0747d43cd 100644 --- a/web/app/components/workflow-app/components/workflow-main.tsx +++ b/web/app/components/workflow-app/components/workflow-main.tsx @@ -8,6 +8,7 @@ import type { WorkflowProps } from '@/app/components/workflow' import WorkflowChildren from './workflow-children' import { useNodesSyncDraft, + useSetWorkflowVarsWithValue, useWorkflowRefreshDraft, useWorkflowRun, useWorkflowStartRun, @@ -61,6 +62,7 @@ const WorkflowMain = ({ handleWorkflowStartRunInChatflow, handleWorkflowStartRunInWorkflow, } = useWorkflowStartRun() + const { fetchInspectVars } = useSetWorkflowVarsWithValue() const hooksStore = useMemo(() => { return { @@ -75,6 +77,7 @@ const WorkflowMain = ({ handleStartWorkflowRun, handleWorkflowStartRunInChatflow, handleWorkflowStartRunInWorkflow, + fetchInspectVars, } }, [ syncWorkflowDraftWhenPageClose, @@ -88,6 +91,7 @@ const WorkflowMain = ({ handleStartWorkflowRun, handleWorkflowStartRunInChatflow, handleWorkflowStartRunInWorkflow, + fetchInspectVars, ]) return ( diff --git a/web/app/components/workflow-app/hooks/index.ts b/web/app/components/workflow-app/hooks/index.ts index 6373a8591c..1b2d9bee97 100644 --- a/web/app/components/workflow-app/hooks/index.ts +++ b/web/app/components/workflow-app/hooks/index.ts @@ -5,3 +5,4 @@ export * from './use-workflow-run' export * from './use-workflow-start-run' export * from './use-is-chat-mode' export * from './use-workflow-refresh-draft' +export * from './use-fetch-workflow-inspect-vars' diff --git a/web/app/components/workflow-app/hooks/use-fetch-workflow-inspect-vars.ts b/web/app/components/workflow-app/hooks/use-fetch-workflow-inspect-vars.ts index 9d3ff84929..222f32b215 100644 --- a/web/app/components/workflow-app/hooks/use-fetch-workflow-inspect-vars.ts +++ b/web/app/components/workflow-app/hooks/use-fetch-workflow-inspect-vars.ts @@ -1,11 +1,12 @@ import type { NodeWithVar, VarInInspect } from '@/types/workflow' -import { useWorkflowStore } from '../../workflow/store' +import { useWorkflowStore } from '@/app/components/workflow/store' import { useStoreApi } from 'reactflow' import type { Node } from '@/app/components/workflow/types' import { fetchAllInspectVars } from '@/service/workflow' import { useInvalidateConversationVarValues, useInvalidateSysVarValues } from '@/service/use-workflow' -import { useNodesInteractionsWithoutSync } from '../../workflow/hooks/use-nodes-interactions-without-sync' -const useSetWorkflowVarsWithValue = () => { +import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync' + +export const useSetWorkflowVarsWithValue = () => { const workflowStore = useWorkflowStore() const { setNodesWithInspectVars, appId } = workflowStore.getState() const store = useStoreApi() @@ -64,5 +65,3 @@ const useSetWorkflowVarsWithValue = () => { fetchInspectVars, } } - -export default useSetWorkflowVarsWithValue diff --git a/web/app/components/workflow-app/hooks/use-workflow-run.ts b/web/app/components/workflow-app/hooks/use-workflow-run.ts index 99b88238f1..361d6375c1 100644 --- a/web/app/components/workflow-app/hooks/use-workflow-run.ts +++ b/web/app/components/workflow-app/hooks/use-workflow-run.ts @@ -20,7 +20,7 @@ import type { VersionHistory } from '@/types/workflow' import { noop } from 'lodash-es' import { useNodesSyncDraft } from './use-nodes-sync-draft' 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 = () => { const store = useStoreApi() diff --git a/web/app/components/workflow/hooks-store/store.ts b/web/app/components/workflow/hooks-store/store.ts index 9f5e1a6650..e775afcc00 100644 --- a/web/app/components/workflow/hooks-store/store.ts +++ b/web/app/components/workflow/hooks-store/store.ts @@ -7,6 +7,7 @@ import { } from 'zustand' import { createStore } from 'zustand/vanilla' import { HooksStoreContext } from './provider' +import type { IOtherOptions } from '@/service/base' type CommonHooksFnMap = { doSyncWorkflowDraft: ( @@ -22,11 +23,12 @@ type CommonHooksFnMap = { handleBackupDraft: () => void handleLoadBackupDraft: () => void handleRestoreFromPublishedWorkflow: (...args: any[]) => void - handleRun: (...args: any[]) => void + handleRun: (params: any, callback?: IOtherOptions,) => void handleStopRun: (...args: any[]) => void handleStartWorkflowRun: () => void handleWorkflowStartRunInWorkflow: () => void handleWorkflowStartRunInChatflow: () => void + fetchInspectVars: () => Promise } export type Shape = { @@ -45,6 +47,7 @@ export const createHooksStore = ({ handleStartWorkflowRun = noop, handleWorkflowStartRunInWorkflow = noop, handleWorkflowStartRunInChatflow = noop, + fetchInspectVars = async () => noop(), }: Partial) => { return createStore(set => ({ refreshAll: props => set(state => ({ ...state, ...props })), @@ -59,6 +62,7 @@ export const createHooksStore = ({ handleStartWorkflowRun, handleWorkflowStartRunInWorkflow, handleWorkflowStartRunInChatflow, + fetchInspectVars, })) } diff --git a/web/app/components/workflow/hooks/index.ts b/web/app/components/workflow/hooks/index.ts index fda0f50aa6..c2eebb0533 100644 --- a/web/app/components/workflow/hooks/index.ts +++ b/web/app/components/workflow/hooks/index.ts @@ -17,3 +17,5 @@ export * from './use-workflow-interactions' export * from './use-workflow-mode' export * from './use-format-time-from-now' export * from './use-workflow-refresh-draft' +export * from './use-inspect-vars-crud' +export * from './use-set-workflow-vars-with-value' diff --git a/web/app/components/workflow/hooks/use-set-workflow-vars-with-value.ts b/web/app/components/workflow/hooks/use-set-workflow-vars-with-value.ts new file mode 100644 index 0000000000..83147e7610 --- /dev/null +++ b/web/app/components/workflow/hooks/use-set-workflow-vars-with-value.ts @@ -0,0 +1,9 @@ +import { useHooksStore } from '@/app/components/workflow/hooks-store' + +export const useSetWorkflowVarsWithValue = () => { + const fetchInspectVars = useHooksStore(s => s.doSyncWorkflowDraft) + + return { + fetchInspectVars, + } +} diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx index 429d07853d..8631eb58e3 100644 --- a/web/app/components/workflow/index.tsx +++ b/web/app/components/workflow/index.tsx @@ -42,6 +42,7 @@ import { useNodesSyncDraft, usePanelInteractions, useSelectionInteractions, + useSetWorkflowVarsWithValue, useShortcuts, useWorkflow, useWorkflowReadOnly, @@ -82,7 +83,6 @@ import Confirm from '@/app/components/base/confirm' import DatasetsDetailProvider from './datasets-detail-store/provider' import { HooksStoreContextProvider } from './hooks-store' import type { Shape as HooksStoreShape } from './hooks-store' -import useSetWorkflowVarsWithValue from '../workflow-app/hooks/use-fetch-workflow-inspect-vars' const nodeTypes = { [CUSTOM_NODE]: CustomNode, diff --git a/web/app/components/workflow/panel/debug-and-preview/hooks.ts b/web/app/components/workflow/panel/debug-and-preview/hooks.ts index 8464d04425..26d604ef11 100644 --- a/web/app/components/workflow/panel/debug-and-preview/hooks.ts +++ b/web/app/components/workflow/panel/debug-and-preview/hooks.ts @@ -8,7 +8,10 @@ import { import { useTranslation } from 'react-i18next' import { produce, setAutoFreeze } from 'immer' import { uniqBy } from 'lodash-es' -import { useWorkflowRun } from '../../hooks' +import { + useSetWorkflowVarsWithValue, + useWorkflowRun, +} from '../../hooks' import { NodeRunningStatus, WorkflowRunningStatus } from '../../types' import { useWorkflowStore } from '../../store' 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 { useInvalidAllLastRun } from '@/service/use-workflow' import { useParams } from 'next/navigation' -import useSetWorkflowVarsWithValue from '@/app/components/workflow-app/hooks/use-fetch-workflow-inspect-vars' type GetAbortController = (abortController: AbortController) => void 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 { conversationId: conversationId.current,