mirror of
				https://github.com/langgenius/dify.git
				synced 2025-10-30 18:33:30 +00:00 
			
		
		
		
	Merge branch 'main' into feat/rag-pipeline
This commit is contained in:
		
						commit
						c4169f8aa0
					
				| @ -2,12 +2,14 @@ from collections.abc import Callable | ||||
| from functools import wraps | ||||
| from typing import Optional | ||||
| 
 | ||||
| from flask import request | ||||
| from flask import current_app, request | ||||
| from flask_login import user_logged_in | ||||
| from flask_restful import reqparse | ||||
| from pydantic import BaseModel | ||||
| from sqlalchemy.orm import Session | ||||
| 
 | ||||
| from extensions.ext_database import db | ||||
| from libs.login import _get_user | ||||
| from models.account import Account, Tenant | ||||
| from models.model import EndUser | ||||
| from services.account_service import AccountService | ||||
| @ -80,7 +82,12 @@ def get_user_tenant(view: Optional[Callable] = None): | ||||
|                 raise ValueError("tenant not found") | ||||
| 
 | ||||
|             kwargs["tenant_model"] = tenant_model | ||||
|             kwargs["user_model"] = get_user(tenant_id, user_id) | ||||
| 
 | ||||
|             user = get_user(tenant_id, user_id) | ||||
|             kwargs["user_model"] = user | ||||
| 
 | ||||
|             current_app.login_manager._update_request_context_with_user(user)  # type: ignore | ||||
|             user_logged_in.send(current_app._get_current_object(), user=_get_user())  # type: ignore | ||||
| 
 | ||||
|             return view_func(*args, **kwargs) | ||||
| 
 | ||||
|  | ||||
| @ -455,8 +455,6 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan | ||||
|         agent_thought: Optional[MessageAgentThought] = ( | ||||
|             db.session.query(MessageAgentThought).filter(MessageAgentThought.id == event.agent_thought_id).first() | ||||
|         ) | ||||
|         db.session.refresh(agent_thought) | ||||
|         db.session.close() | ||||
| 
 | ||||
|         if agent_thought: | ||||
|             return AgentThoughtStreamResponse( | ||||
|  | ||||
| @ -356,7 +356,9 @@ class AgentNode(ToolNode): | ||||
| 
 | ||||
|     def _remove_unsupported_model_features_for_old_version(self, model_schema: AIModelEntity) -> AIModelEntity: | ||||
|         if model_schema.features: | ||||
|             for feature in model_schema.features: | ||||
|                 if feature.value not in AgentOldVersionModelFeatures: | ||||
|             for feature in model_schema.features[:]:  # Create a copy to safely modify during iteration | ||||
|                 try: | ||||
|                     AgentOldVersionModelFeatures(feature.value)  # Try to create enum member from value | ||||
|                 except ValueError: | ||||
|                     model_schema.features.remove(feature) | ||||
|         return model_schema | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| from enum import Enum | ||||
| from enum import Enum, StrEnum | ||||
| from typing import Any, Literal, Union | ||||
| 
 | ||||
| from pydantic import BaseModel | ||||
| @ -26,7 +26,7 @@ class ParamsAutoGenerated(Enum): | ||||
|     OPEN = 1 | ||||
| 
 | ||||
| 
 | ||||
| class AgentOldVersionModelFeatures(Enum): | ||||
| class AgentOldVersionModelFeatures(StrEnum): | ||||
|     """ | ||||
|     Enum class for old SDK version llm feature. | ||||
|     """ | ||||
|  | ||||
| @ -235,6 +235,10 @@ class Executor: | ||||
|                                 files[key].append(file_tuple) | ||||
| 
 | ||||
|                     # convert files to list for httpx request | ||||
|                     # If there are no actual files, we still need to force httpx to use `multipart/form-data`. | ||||
|                     # This is achieved by inserting a harmless placeholder file that will be ignored by the server. | ||||
|                     if not files: | ||||
|                         self.files = [("__multipart_placeholder__", ("", b"", "application/octet-stream"))] | ||||
|                     if files: | ||||
|                         self.files = [] | ||||
|                         for key, file_tuples in files.items(): | ||||
| @ -373,7 +377,10 @@ class Executor: | ||||
|             raw += f"{k}: {v}\r\n" | ||||
| 
 | ||||
|         body_string = "" | ||||
|         if self.files: | ||||
|         # Only log actual files if present. | ||||
|         # '__multipart_placeholder__' is inserted to force multipart encoding but is not a real file. | ||||
|         # This prevents logging meaningless placeholder entries. | ||||
|         if self.files and not all(f[0] == "__multipart_placeholder__" for f in self.files): | ||||
|             for key, (filename, content, mime_type) in self.files: | ||||
|                 body_string += f"--{boundary}\r\n" | ||||
|                 body_string += f'Content-Disposition: form-data; name="{key}"\r\n\r\n' | ||||
|  | ||||
| @ -149,6 +149,7 @@ dev = [ | ||||
|     "types-tqdm~=4.67.0", | ||||
|     "types-ujson~=5.10.0", | ||||
|     "boto3-stubs>=1.38.20", | ||||
|     "types-jmespath>=1.0.2.20240106", | ||||
| ] | ||||
| 
 | ||||
| ############################################################ | ||||
|  | ||||
| @ -246,7 +246,9 @@ def test_executor_with_form_data(): | ||||
|     assert "multipart/form-data" in executor.headers["Content-Type"] | ||||
|     assert executor.params == [] | ||||
|     assert executor.json is None | ||||
|     assert executor.files is None | ||||
|     # '__multipart_placeholder__' is expected when no file inputs exist, | ||||
|     # to ensure the request is treated as multipart/form-data by the backend. | ||||
|     assert executor.files == [("__multipart_placeholder__", ("", b"", "application/octet-stream"))] | ||||
|     assert executor.content is None | ||||
| 
 | ||||
|     # Check that the form data is correctly loaded in executor.data | ||||
|  | ||||
							
								
								
									
										11
									
								
								api/uv.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11
									
								
								api/uv.lock
									
									
									
										generated
									
									
									
								
							| @ -1315,6 +1315,7 @@ dev = [ | ||||
|     { name = "types-gevent" }, | ||||
|     { name = "types-greenlet" }, | ||||
|     { name = "types-html5lib" }, | ||||
|     { name = "types-jmespath" }, | ||||
|     { name = "types-jsonschema" }, | ||||
|     { name = "types-markdown" }, | ||||
|     { name = "types-oauthlib" }, | ||||
| @ -1486,6 +1487,7 @@ dev = [ | ||||
|     { name = "types-gevent", specifier = "~=24.11.0" }, | ||||
|     { name = "types-greenlet", specifier = "~=3.1.0" }, | ||||
|     { name = "types-html5lib", specifier = "~=1.1.11" }, | ||||
|     { name = "types-jmespath", specifier = ">=1.0.2.20240106" }, | ||||
|     { name = "types-jsonschema", specifier = "~=4.23.0" }, | ||||
|     { name = "types-markdown", specifier = "~=3.7.0" }, | ||||
|     { name = "types-oauthlib", specifier = "~=3.2.0" }, | ||||
| @ -5726,6 +5728,15 @@ wheels = [ | ||||
|     { url = "https://files.pythonhosted.org/packages/ba/7c/f862b1dc31268ef10fe95b43dcdf216ba21a592fafa2d124445cd6b92e93/types_html5lib-1.1.11.20241018-py3-none-any.whl", hash = "sha256:3f1e064d9ed2c289001ae6392c84c93833abb0816165c6ff0abfc304a779f403", size = 17292 }, | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "types-jmespath" | ||||
| version = "1.0.2.20240106" | ||||
| source = { registry = "https://pypi.org/simple" } | ||||
| sdist = { url = "https://files.pythonhosted.org/packages/1b/e4/1f7414dbca03975f66f1ab1b3f7b3deb7c19b104ef14dd3c99036bbc39b2/types-jmespath-1.0.2.20240106.tar.gz", hash = "sha256:b4a65a116bfc1c700a4fd9d24e2e397f4a431122e0320a77b7f1989a6b5d819e", size = 5071 } | ||||
| wheels = [ | ||||
|     { url = "https://files.pythonhosted.org/packages/f5/30/3d6443f782601dd88820ba31e7668abfec7e19d685ac7f6fbcfd6ebba519/types_jmespath-1.0.2.20240106-py3-none-any.whl", hash = "sha256:c3e715fcaae9e5f8d74e14328fdedc4f2b3f0e18df17f3e457ae0a18e245bde0", size = 6087 }, | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "types-jsonschema" | ||||
| version = "4.23.0.20241208" | ||||
|  | ||||
| @ -6,10 +6,12 @@ import Button from '../components/base/button' | ||||
| import Avatar from './avatar' | ||||
| import DifyLogo from '@/app/components/base/logo/dify-logo' | ||||
| import { useCallback } from 'react' | ||||
| import { useGlobalPublicStore } from '@/context/global-public-context' | ||||
| 
 | ||||
| const Header = () => { | ||||
|   const { t } = useTranslation() | ||||
|   const router = useRouter() | ||||
|   const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) | ||||
| 
 | ||||
|   const back = useCallback(() => { | ||||
|     router.back() | ||||
| @ -19,7 +21,13 @@ const Header = () => { | ||||
|     <div className='flex flex-1 items-center justify-between px-4'> | ||||
|       <div className='flex items-center gap-3'> | ||||
|         <div className='flex cursor-pointer items-center' onClick={back}> | ||||
|           <DifyLogo /> | ||||
|           {systemFeatures.branding.enabled && systemFeatures.branding.login_page_logo | ||||
|             ? <img | ||||
|               src={systemFeatures.branding.login_page_logo} | ||||
|               className='block h-[22px] w-auto object-contain' | ||||
|               alt='Dify logo' | ||||
|             /> | ||||
|             : <DifyLogo />} | ||||
|         </div> | ||||
|         <div className='h-4 w-[1px] origin-center rotate-[11.31deg] bg-divider-regular' /> | ||||
|         <p className='title-3xl-semi-bold relative mt-[-2px] text-text-primary'>{t('common.account.account')}</p> | ||||
|  | ||||
| @ -148,10 +148,12 @@ const Sidebar = ({ isPanel }: Props) => { | ||||
|               'flex shrink-0 items-center gap-1.5 px-1', | ||||
|             )}> | ||||
|               <div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div> | ||||
|               {systemFeatures.branding.enabled ? ( | ||||
|                 <img src={systemFeatures.branding.login_page_logo} alt='logo' className='block h-5 w-auto' /> | ||||
|               ) : ( | ||||
|                 <DifyLogo size='small' />) | ||||
|               { | ||||
|                 systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo | ||||
|                   ? <img src={systemFeatures.branding.workspace_logo} alt='logo' className='block h-5 w-auto' /> | ||||
|                   : appData?.custom_config?.replace_webapp_logo | ||||
|                     ? <img src={`${appData?.custom_config?.replace_webapp_logo}`} alt='logo' className='block h-5 w-auto' /> | ||||
|                     : <DifyLogo size='small' /> | ||||
|               } | ||||
|             </div> | ||||
|           )} | ||||
|  | ||||
| @ -13,6 +13,7 @@ import Divider from '@/app/components/base/divider' | ||||
| import ViewFormDropdown from '@/app/components/base/chat/embedded-chatbot/inputs-form/view-form-dropdown' | ||||
| import DifyLogo from '@/app/components/base/logo/dify-logo' | ||||
| import cn from '@/utils/classnames' | ||||
| import { useGlobalPublicStore } from '@/context/global-public-context' | ||||
| 
 | ||||
| export type IHeaderProps = { | ||||
|   isMobile?: boolean | ||||
| @ -42,6 +43,7 @@ const Header: FC<IHeaderProps> = ({ | ||||
|   const [parentOrigin, setParentOrigin] = useState('') | ||||
|   const [showToggleExpandButton, setShowToggleExpandButton] = useState(false) | ||||
|   const [expanded, setExpanded] = useState(false) | ||||
|   const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) | ||||
| 
 | ||||
|   const handleMessageReceived = useCallback((event: MessageEvent) => { | ||||
|     let currentParentOrigin = parentOrigin | ||||
| @ -85,12 +87,13 @@ const Header: FC<IHeaderProps> = ({ | ||||
|                 'flex shrink-0 items-center gap-1.5 px-2', | ||||
|               )}> | ||||
|                 <div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div> | ||||
|                 {appData?.custom_config?.replace_webapp_logo && ( | ||||
|                   <img src={appData?.custom_config?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' /> | ||||
|                 )} | ||||
|                 {!appData?.custom_config?.replace_webapp_logo && ( | ||||
|                   <DifyLogo size='small' /> | ||||
|                 )} | ||||
|                 { | ||||
|                   systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo | ||||
|                     ? <img src={systemFeatures.branding.workspace_logo} alt='logo' className='block h-5 w-auto' /> | ||||
|                     : appData?.custom_config?.replace_webapp_logo | ||||
|                       ? <img src={`${appData?.custom_config?.replace_webapp_logo}`} alt='logo' className='block h-5 w-auto' /> | ||||
|                       : <DifyLogo size='small' /> | ||||
|                 } | ||||
|               </div> | ||||
|             )} | ||||
|           </div> | ||||
|  | ||||
| @ -22,6 +22,7 @@ import ChatWrapper from '@/app/components/base/chat/embedded-chatbot/chat-wrappe | ||||
| import DifyLogo from '@/app/components/base/logo/dify-logo' | ||||
| import cn from '@/utils/classnames' | ||||
| import useDocumentTitle from '@/hooks/use-document-title' | ||||
| import { useGlobalPublicStore } from '@/context/global-public-context' | ||||
| 
 | ||||
| const Chatbot = () => { | ||||
|   const { | ||||
| @ -37,6 +38,7 @@ const Chatbot = () => { | ||||
|     themeBuilder, | ||||
|   } = useEmbeddedChatbotContext() | ||||
|   const { t } = useTranslation() | ||||
|   const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) | ||||
| 
 | ||||
|   const customConfig = appData?.custom_config | ||||
|   const site = appData?.site | ||||
| @ -115,12 +117,13 @@ const Chatbot = () => { | ||||
|               'flex shrink-0 items-center gap-1.5 px-2', | ||||
|             )}> | ||||
|               <div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div> | ||||
|               {appData?.custom_config?.replace_webapp_logo && ( | ||||
|                 <img src={appData?.custom_config?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' /> | ||||
|               )} | ||||
|               {!appData?.custom_config?.replace_webapp_logo && ( | ||||
|                 <DifyLogo size='small' /> | ||||
|               )} | ||||
|               { | ||||
|                 systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo | ||||
|                   ? <img src={systemFeatures.branding.workspace_logo} alt='logo' className='block h-5 w-auto' /> | ||||
|                   : appData?.custom_config?.replace_webapp_logo | ||||
|                     ? <img src={`${appData?.custom_config?.replace_webapp_logo}`} alt='logo' className='block h-5 w-auto' /> | ||||
|                     : <DifyLogo size='small' /> | ||||
|               } | ||||
|             </div> | ||||
|           )} | ||||
|         </div> | ||||
|  | ||||
| @ -3,7 +3,6 @@ import type { FC } from 'react' | ||||
| import classNames from '@/utils/classnames' | ||||
| import useTheme from '@/hooks/use-theme' | ||||
| import { basePath } from '@/utils/var' | ||||
| import { useGlobalPublicStore } from '@/context/global-public-context' | ||||
| export type LogoStyle = 'default' | 'monochromeWhite' | ||||
| 
 | ||||
| export const logoPathMap: Record<LogoStyle, string> = { | ||||
| @ -32,18 +31,12 @@ const DifyLogo: FC<DifyLogoProps> = ({ | ||||
| }) => { | ||||
|   const { theme } = useTheme() | ||||
|   const themedStyle = (theme === 'dark' && style === 'default') ? 'monochromeWhite' : style | ||||
|   const { systemFeatures } = useGlobalPublicStore() | ||||
|   const hasBrandingLogo = Boolean(systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo) | ||||
| 
 | ||||
|   let src = `${basePath}${logoPathMap[themedStyle]}` | ||||
|   if (hasBrandingLogo) | ||||
|     src = systemFeatures.branding.workspace_logo | ||||
| 
 | ||||
|   return ( | ||||
|     <img | ||||
|       src={src} | ||||
|       className={classNames('block object-contain', logoSizeMap[size], hasBrandingLogo && 'w-auto', className)} | ||||
|       alt={hasBrandingLogo ? 'Logo' : 'Dify logo'} | ||||
|       src={`${basePath}${logoPathMap[themedStyle]}`} | ||||
|       className={classNames('block object-contain', logoSizeMap[size], className)} | ||||
|       alt='Dify logo' | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| @ -40,7 +40,7 @@ const TabSlider: FC<TabSliderProps> = ({ | ||||
|     const newIndex = options.findIndex(option => option.value === value) | ||||
|     setActiveIndex(newIndex) | ||||
|     updateSliderStyle(newIndex) | ||||
|   }, [value, options, pluginList]) | ||||
|   }, [value, options, pluginList?.total]) | ||||
| 
 | ||||
|   return ( | ||||
|     <div className={cn(className, 'relative inline-flex items-center justify-center rounded-[10px] bg-components-segmented-control-bg-normal p-0.5')}> | ||||
| @ -69,13 +69,13 @@ const TabSlider: FC<TabSliderProps> = ({ | ||||
|           {option.text} | ||||
|           {/* if no plugin installed, the badge won't show */} | ||||
|           {option.value === 'plugins' | ||||
|             && (pluginList?.plugins.length ?? 0) > 0 | ||||
|             && (pluginList?.total ?? 0) > 0 | ||||
|             && <Badge | ||||
|               size='s' | ||||
|               uppercase={true} | ||||
|               state={BadgeState.Default} | ||||
|             > | ||||
|               {pluginList?.plugins.length} | ||||
|               {pluginList?.total} | ||||
|             </Badge> | ||||
|           } | ||||
|         </div> | ||||
|  | ||||
| @ -24,6 +24,7 @@ import { | ||||
| } from '@/service/common' | ||||
| import { useAppContext } from '@/context/app-context' | ||||
| import cn from '@/utils/classnames' | ||||
| import { useGlobalPublicStore } from '@/context/global-public-context' | ||||
| 
 | ||||
| const ALLOW_FILE_EXTENSIONS = ['svg', 'png'] | ||||
| 
 | ||||
| @ -39,6 +40,7 @@ const CustomWebAppBrand = () => { | ||||
|   const [fileId, setFileId] = useState('') | ||||
|   const [imgKey, setImgKey] = useState(Date.now()) | ||||
|   const [uploadProgress, setUploadProgress] = useState(0) | ||||
|   const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) | ||||
|   const isSandbox = enableBilling && plan.type === Plan.sandbox | ||||
|   const uploading = uploadProgress > 0 && uploadProgress < 100 | ||||
|   const webappLogo = currentWorkspace.custom_config?.replace_webapp_logo || '' | ||||
| @ -244,7 +246,10 @@ const CustomWebAppBrand = () => { | ||||
|                 {!webappBrandRemoved && ( | ||||
|                   <> | ||||
|                     <div className='system-2xs-medium-uppercase text-text-tertiary'>POWERED BY</div> | ||||
|                     {webappLogo | ||||
|                     { | ||||
|                       systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo | ||||
|                         ? <img src={systemFeatures.branding.workspace_logo} alt='logo' className='block h-5 w-auto' /> | ||||
|                         : webappLogo | ||||
|                           ? <img src={`${webappLogo}?hash=${imgKey}`} alt='logo' className='block h-5 w-auto' /> | ||||
|                           : <DifyLogo size='small' /> | ||||
|                     } | ||||
| @ -303,7 +308,10 @@ const CustomWebAppBrand = () => { | ||||
|             {!webappBrandRemoved && ( | ||||
|               <> | ||||
|                 <div className='system-2xs-medium-uppercase text-text-tertiary'>POWERED BY</div> | ||||
|                 {webappLogo | ||||
|                 { | ||||
|                   systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo | ||||
|                     ? <img src={systemFeatures.branding.workspace_logo} alt='logo' className='block h-5 w-auto' /> | ||||
|                     : webappLogo | ||||
|                       ? <img src={`${webappLogo}?hash=${imgKey}`} alt='logo' className='block h-5 w-auto' /> | ||||
|                       : <DifyLogo size='small' /> | ||||
|                 } | ||||
|  | ||||
| @ -9,6 +9,7 @@ import type { LangGeniusVersionResponse } from '@/models/common' | ||||
| import { IS_CE_EDITION } from '@/config' | ||||
| import DifyLogo from '@/app/components/base/logo/dify-logo' | ||||
| import { noop } from 'lodash-es' | ||||
| import { useGlobalPublicStore } from '@/context/global-public-context' | ||||
| 
 | ||||
| type IAccountSettingProps = { | ||||
|   langeniusVersionInfo: LangGeniusVersionResponse | ||||
| @ -21,6 +22,7 @@ export default function AccountAbout({ | ||||
| }: IAccountSettingProps) { | ||||
|   const { t } = useTranslation() | ||||
|   const isLatest = langeniusVersionInfo.current_version === langeniusVersionInfo.latest_version | ||||
|   const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) | ||||
| 
 | ||||
|   return ( | ||||
|     <Modal | ||||
| @ -33,7 +35,14 @@ export default function AccountAbout({ | ||||
|           <RiCloseLine className='h-4 w-4 text-text-tertiary' /> | ||||
|         </div> | ||||
|         <div className='flex flex-col items-center gap-4 py-8'> | ||||
|           <DifyLogo size='large' className='mx-auto' /> | ||||
|           {systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo | ||||
|             ? <img | ||||
|               src={systemFeatures.branding.workspace_logo} | ||||
|               className='block h-7 w-auto object-contain' | ||||
|               alt='logo' | ||||
|             /> | ||||
|             : <DifyLogo size='large' className='mx-auto' />} | ||||
| 
 | ||||
|           <div className='text-center text-xs font-normal text-text-tertiary'>Version {langeniusVersionInfo?.current_version}</div> | ||||
|           <div className='flex flex-col items-center gap-2 text-center text-xs font-normal text-text-secondary'> | ||||
|             <div>© {dayjs().year()} LangGenius, Inc., Contributors.</div> | ||||
|  | ||||
| @ -21,6 +21,7 @@ import { useModalContext } from '@/context/modal-context' | ||||
| import PlanBadge from './plan-badge' | ||||
| import LicenseNav from './license-env' | ||||
| import { Plan } from '../billing/type' | ||||
| import { useGlobalPublicStore } from '@/context/global-public-context' | ||||
| 
 | ||||
| const navClassName = ` | ||||
|   flex items-center relative mr-0 sm:mr-3 px-3 h-8 rounded-xl | ||||
| @ -36,6 +37,7 @@ const Header = () => { | ||||
|   const [isShowNavMenu, { toggle, setFalse: hideNavMenu }] = useBoolean(false) | ||||
|   const { enableBilling, plan } = useProviderContext() | ||||
|   const { setShowPricingModal, setShowAccountSettingModal } = useModalContext() | ||||
|   const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) | ||||
|   const isFreePlan = plan.type === Plan.sandbox | ||||
|   const handlePlanClick = useCallback(() => { | ||||
|     if (isFreePlan) | ||||
| @ -61,7 +63,13 @@ const Header = () => { | ||||
|           !isMobile | ||||
|           && <div className='flex shrink-0 items-center gap-1.5 self-stretch pl-3'> | ||||
|             <Link href="/apps" className='flex h-8 shrink-0 items-center justify-center gap-2 px-0.5'> | ||||
|               <DifyLogo /> | ||||
|               {systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo | ||||
|                 ? <img | ||||
|                   src={systemFeatures.branding.workspace_logo} | ||||
|                   className='block h-[22px] w-auto object-contain' | ||||
|                   alt='logo' | ||||
|                 /> | ||||
|                 : <DifyLogo />} | ||||
|             </Link> | ||||
|             <div className='font-light text-divider-deep'>/</div> | ||||
|             <div className='flex items-center gap-0.5'> | ||||
| @ -76,7 +84,13 @@ const Header = () => { | ||||
|       {isMobile && ( | ||||
|         <div className='flex'> | ||||
|           <Link href="/apps" className='mr-4 flex items-center'> | ||||
|             <DifyLogo /> | ||||
|             {systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo | ||||
|               ? <img | ||||
|                 src={systemFeatures.branding.workspace_logo} | ||||
|                 className='block h-[22px] w-auto object-contain' | ||||
|                 alt='logo' | ||||
|               /> | ||||
|               : <DifyLogo />} | ||||
|           </Link> | ||||
|           <div className='font-light text-divider-deep'>/</div> | ||||
|           {enableBilling ? <PlanBadge allowHover sandboxAsUpgrade plan={plan.type} onClick={handlePlanClick} /> : <LicenseNav />} | ||||
|  | ||||
| @ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next' | ||||
| import type { FilterState } from './filter-management' | ||||
| import FilterManagement from './filter-management' | ||||
| import List from './list' | ||||
| import { useInstalledLatestVersion, useInstalledPluginListWithPagination, useInvalidateInstalledPluginList } from '@/service/use-plugins' | ||||
| import { useInstalledLatestVersion, useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins' | ||||
| import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel' | ||||
| import { usePluginPageContext } from './context' | ||||
| import { useDebounceFn } from 'ahooks' | ||||
| @ -17,7 +17,7 @@ const PluginsPanel = () => { | ||||
|   const { t } = useTranslation() | ||||
|   const filters = usePluginPageContext(v => v.filters) as FilterState | ||||
|   const setFilters = usePluginPageContext(v => v.setFilters) | ||||
|   const { data: pluginList, isLoading: isPluginListLoading, isFetching, isLastPage, loadNextPage } = useInstalledPluginListWithPagination() | ||||
|   const { data: pluginList, isLoading: isPluginListLoading, isFetching, isLastPage, loadNextPage } = useInstalledPluginList() | ||||
|   const { data: installedLatestVersion } = useInstalledLatestVersion( | ||||
|     pluginList?.plugins | ||||
|       .filter(plugin => plugin.source === PluginSource.marketplace) | ||||
|  | ||||
| @ -641,11 +641,13 @@ const TextGeneration: FC<IMainProps> = ({ | ||||
|             !isPC && resultExisted && 'rounded-b-2xl border-b-[0.5px] border-divider-regular', | ||||
|           )}> | ||||
|             <div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div> | ||||
|             {systemFeatures.branding.enabled ? ( | ||||
|               <img src={systemFeatures.branding.login_page_logo} alt='logo' className='block h-5 w-auto' /> | ||||
|             ) : ( | ||||
|               <DifyLogo size='small' /> | ||||
|             )} | ||||
|             { | ||||
|               systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo | ||||
|                 ? <img src={systemFeatures.branding.workspace_logo} alt='logo' className='block h-5 w-auto' /> | ||||
|                 : customConfig?.replace_webapp_logo | ||||
|                   ? <img src={`${customConfig?.replace_webapp_logo}`} alt='logo' className='block h-5 w-auto' /> | ||||
|                   : <DifyLogo size='small' /> | ||||
|             } | ||||
|           </div> | ||||
|         )} | ||||
|       </div> | ||||
|  | ||||
| @ -105,7 +105,7 @@ const ChangePasswordForm = () => { | ||||
|           </div> | ||||
| 
 | ||||
|           <div className="mx-auto mt-6 w-full"> | ||||
|             <div className="bg-white"> | ||||
|             <div> | ||||
|               {/* Password */} | ||||
|               <div className='mb-5'> | ||||
|                 <label htmlFor="password" className="system-md-semibold my-2 text-text-secondary"> | ||||
|  | ||||
| @ -7,6 +7,7 @@ import { languages } from '@/i18n/language' | ||||
| import type { Locale } from '@/i18n' | ||||
| import I18n from '@/context/i18n' | ||||
| import dynamic from 'next/dynamic' | ||||
| import { useGlobalPublicStore } from '@/context/global-public-context' | ||||
| 
 | ||||
| // Avoid rendering the logo and theme selector on the server
 | ||||
| const DifyLogo = dynamic(() => import('@/app/components/base/logo/dify-logo'), { | ||||
| @ -20,10 +21,17 @@ const ThemeSelector = dynamic(() => import('@/app/components/base/theme-selector | ||||
| 
 | ||||
| const Header = () => { | ||||
|   const { locale, setLocaleOnClient } = useContext(I18n) | ||||
|   const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) | ||||
| 
 | ||||
|   return ( | ||||
|     <div className='flex w-full items-center justify-between p-6'> | ||||
|       <DifyLogo size='large' /> | ||||
|       {systemFeatures.branding.enabled && systemFeatures.branding.login_page_logo | ||||
|         ? <img | ||||
|           src={systemFeatures.branding.login_page_logo} | ||||
|           className='block h-7 w-auto object-contain' | ||||
|           alt='logo' | ||||
|         /> | ||||
|         : <DifyLogo size='large' />} | ||||
|       <div className='flex items-center gap-1'> | ||||
|         <Select | ||||
|           value={locale} | ||||
|  | ||||
| @ -645,9 +645,9 @@ const translation = { | ||||
|     perPage: '페이지당 항목 수', | ||||
|   }, | ||||
|   theme: { | ||||
|     theme: '주제', | ||||
|     light: '빛', | ||||
|     dark: '어둠', | ||||
|     theme: '테마', | ||||
|     light: '밝은', | ||||
|     dark: '어두운', | ||||
|     auto: '시스템', | ||||
|   }, | ||||
|   compliance: { | ||||
|  | ||||
| @ -301,7 +301,7 @@ const translation = { | ||||
|         designDocument: '디자인 문서', | ||||
|         productSpecification: '제품 사양서', | ||||
|         financialReport: '재무 보고서', | ||||
|         marketAnalysis: '시장 분석', | ||||
|         marketAnalysis: '마켓 분석', | ||||
|         projectPlan: '프로젝트 계획서', | ||||
|         teamStructure: '팀 구조', | ||||
|         policiesProcedures: '정책 및 절차', | ||||
|  | ||||
| @ -194,7 +194,7 @@ const translation = { | ||||
|     datasetMetadata: { | ||||
|       name: '이름', | ||||
|       deleteTitle: '삭제 확인', | ||||
|       disabled: '장애인', | ||||
|       disabled: '사용안함', | ||||
|       addMetaData: '메타데이터 추가', | ||||
|       values: '{{num}} 값들', | ||||
|       namePlaceholder: '메타데이터 이름', | ||||
|  | ||||
| @ -25,7 +25,7 @@ const translation = { | ||||
|   }, | ||||
|   source: { | ||||
|     local: '로컬 패키지 파일', | ||||
|     marketplace: '시장', | ||||
|     marketplace: '마켓', | ||||
|     github: '깃허브', | ||||
|   }, | ||||
|   detailPanel: { | ||||
| @ -197,7 +197,7 @@ const translation = { | ||||
|   endpointsEnabled: '{{num}}개의 엔드포인트 집합이 활성화되었습니다.', | ||||
|   installFrom: '에서 설치', | ||||
|   allCategories: '모든 카테고리', | ||||
|   submitPlugin: '제출 플러그인', | ||||
|   submitPlugin: '플러그인 제출', | ||||
|   findMoreInMarketplace: 'Marketplace에서 더 알아보기', | ||||
|   searchCategories: '검색 카테고리', | ||||
|   search: '검색', | ||||
|  | ||||
| @ -200,7 +200,7 @@ const translation = { | ||||
|       code: '코드', | ||||
|       model: '모델', | ||||
|       rerankModel: '재정렬 모델', | ||||
|       visionVariable: '시력 변수', | ||||
|       visionVariable: '비전 변수', | ||||
|     }, | ||||
|     invalidVariable: '잘못된 변수', | ||||
|     rerankModelRequired: 'Rerank Model을 켜기 전에 설정에서 모델이 성공적으로 구성되었는지 확인하십시오.', | ||||
| @ -463,7 +463,7 @@ const translation = { | ||||
|       metadata: { | ||||
|         options: { | ||||
|           disabled: { | ||||
|             title: '장애인', | ||||
|             title: '사용안함', | ||||
|             subTitle: '메타데이터 필터링을 활성화하지 않음', | ||||
|           }, | ||||
|           automatic: { | ||||
|  | ||||
| @ -10,7 +10,6 @@ import type { | ||||
|   GitHubItemAndMarketPlaceDependency, | ||||
|   InstallPackageResponse, | ||||
|   InstalledLatestVersionResponse, | ||||
|   InstalledPluginListResponse, | ||||
|   InstalledPluginListWithTotalResponse, | ||||
|   PackageDependency, | ||||
|   Permissions, | ||||
| @ -67,16 +66,7 @@ export const useCheckInstalled = ({ | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| export const useInstalledPluginList = (disable?: boolean) => { | ||||
|   return useQuery<InstalledPluginListResponse>({ | ||||
|     queryKey: useInstalledPluginListKey, | ||||
|     queryFn: () => get<InstalledPluginListResponse>('/workspaces/current/plugin/list'), | ||||
|     enabled: !disable, | ||||
|     initialData: !disable ? undefined : { plugins: [] }, | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| export const useInstalledPluginListWithPagination = (pageSize = 100) => { | ||||
| export const useInstalledPluginList = (disable?: boolean, pageSize = 100) => { | ||||
|   const fetchPlugins = async ({ pageParam = 1 }) => { | ||||
|     const response = await get<InstalledPluginListWithTotalResponse>( | ||||
|       `/workspaces/current/plugin/list?page=${pageParam}&page_size=${pageSize}`, | ||||
| @ -91,8 +81,10 @@ export const useInstalledPluginListWithPagination = (pageSize = 100) => { | ||||
|     hasNextPage, | ||||
|     isFetchingNextPage, | ||||
|     isLoading, | ||||
|     isSuccess, | ||||
|   } = useInfiniteQuery({ | ||||
|     queryKey: ['installed-plugins', pageSize], | ||||
|     enabled: !disable, | ||||
|     queryKey: useInstalledPluginListKey, | ||||
|     queryFn: fetchPlugins, | ||||
|     getNextPageParam: (lastPage, pages) => { | ||||
|       const totalItems = lastPage.total | ||||
| @ -108,10 +100,12 @@ export const useInstalledPluginListWithPagination = (pageSize = 100) => { | ||||
|   }) | ||||
| 
 | ||||
|   const plugins = data?.pages.flatMap(page => page.plugins) ?? [] | ||||
|   const total = data?.pages[0].total ?? 0 | ||||
| 
 | ||||
|   return { | ||||
|     data: { | ||||
|     data: disable ? undefined : { | ||||
|       plugins, | ||||
|       total, | ||||
|     }, | ||||
|     isLastPage: !hasNextPage, | ||||
|     loadNextPage: () => { | ||||
| @ -120,6 +114,7 @@ export const useInstalledPluginListWithPagination = (pageSize = 100) => { | ||||
|     isLoading, | ||||
|     isFetching: isFetchingNextPage, | ||||
|     error, | ||||
|     isSuccess, | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zxhlyh
						zxhlyh