Compare commits

...

12 Commits

Author SHA1 Message Date
NFish
151d68b5b3 fix: add github ci on branch v141-hotfix 2025-05-28 18:28:33 +08:00
NFish
9d38441c8d Merge branch 'fix/branding-broken' into v141-hotfix 2025-05-28 18:23:27 +08:00
NFish
7ab0b7346e fix: branding logo broken 2025-05-28 18:23:10 +08:00
zxhlyh
861cdbd8b6 fix: workflow plugins list update (#20357) 2025-05-28 17:46:36 +08:00
-LAN-
eaaf551497 fix: Instance <Account> is not bound to a Session (#20347)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-05-28 16:36:08 +08:00
-LAN-
f233a64eb5 fix(workflow): fetch user failed when workflow run in parallel mode (#20321)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-05-27 22:41:07 +08:00
AichiB7A
2b81b6673f [Observability] Add type check and try-except in otel (#20319) 2025-05-27 21:17:45 +08:00
-LAN-
4c46f04d77 fix: Enhances tenant ID handling in telemetry (#20304)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-05-27 17:44:40 +08:00
Joel
d467c8536b fix: i18n auto run failed (#20302) 2025-05-27 17:29:56 +08:00
Joel
abc32edf28 chore: enchance the copywriting of tool (#20294) 2025-05-27 16:40:11 +08:00
crazywoola
047a1b5166 Chore/update img (#20292) 2025-05-27 16:33:43 +08:00
crazywoola
a06fa7374d update img (#20291) 2025-05-27 16:30:04 +08:00
69 changed files with 199 additions and 134 deletions

View File

@@ -6,6 +6,7 @@ on:
- "main"
- "deploy/dev"
- "deploy/enterprise"
- "v141-hotfix"
tags:
- "*"

View File

@@ -31,11 +31,19 @@ jobs:
echo "FILES_CHANGED=false" >> $GITHUB_ENV
fi
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
run_install: false
- name: Set up Node.js
if: env.FILES_CHANGED == 'true'
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
cache: pnpm
cache-dependency-path: ./web/package.json
- name: Install dependencies
if: env.FILES_CHANGED == 'true'

View File

@@ -452,7 +452,7 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator):
for var, val in context.items():
var.set(val)
# Save current user before entering new app context
# FIXME(-LAN-): Save current user before entering new app context
from flask import g
saved_user = None

View File

@@ -232,7 +232,7 @@ class AgentChatAppGenerator(MessageBasedAppGenerator):
for var, val in context.items():
var.set(val)
# Save current user before entering new app context
# FIXME(-LAN-): Save current user before entering new app context
from flask import g
saved_user = None

View File

@@ -411,7 +411,7 @@ class WorkflowAppGenerator(BaseAppGenerator):
for var, val in context.items():
var.set(val)
# Save current user before entering new app context
# FIXME(-LAN-): Save current user before entering new app context
from flask import g
saved_user = None

View File

@@ -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(

View File

@@ -9,7 +9,7 @@ from copy import copy, deepcopy
from datetime import UTC, datetime
from typing import Any, Optional, cast
from flask import Flask, current_app
from flask import Flask, current_app, has_request_context
from configs import dify_config
from core.app.apps.base_app_queue_manager import GenerateTaskStoppedError
@@ -540,8 +540,21 @@ class GraphEngine:
for var, val in context.items():
var.set(val)
# FIXME(-LAN-): Save current user before entering new app context
from flask import g
saved_user = None
if has_request_context() and hasattr(g, "_login_user"):
saved_user = g._login_user
with flask_app.app_context():
try:
# Restore user in new app context
if saved_user is not None:
from flask import g
g._login_user = saved_user
q.put(
ParallelBranchRunStartedEvent(
parallel_id=parallel_id,

View File

@@ -7,7 +7,7 @@ from datetime import UTC, datetime
from queue import Empty, Queue
from typing import TYPE_CHECKING, Any, Optional, cast
from flask import Flask, current_app
from flask import Flask, current_app, has_request_context
from configs import dify_config
from core.variables import ArrayVariable, IntegerVariable, NoneVariable
@@ -586,7 +586,21 @@ class IterationNode(BaseNode[IterationNodeData]):
"""
for var, val in context.items():
var.set(val)
# FIXME(-LAN-): Save current user before entering new app context
from flask import g
saved_user = None
if has_request_context() and hasattr(g, "_login_user"):
saved_user = g._login_user
with flask_app.app_context():
# Restore user in new app context
if saved_user is not None:
from flask import g
g._login_user = saved_user
parallel_mode_run_id = uuid.uuid4().hex
graph_engine_copy = graph_engine.create_copy()
variable_pool_copy = graph_engine_copy.graph_runtime_state.variable_pool

View File

@@ -12,19 +12,30 @@ from flask_login import user_loaded_from_request, user_logged_in # type: ignore
from configs import dify_config
from dify_app import DifyApp
from models import Account, EndUser
@user_logged_in.connect
@user_loaded_from_request.connect
def on_user_loaded(_sender, user):
def on_user_loaded(_sender, user: Union["Account", "EndUser"]):
if dify_config.ENABLE_OTEL:
from opentelemetry.trace import get_current_span
if user:
current_span = get_current_span()
if current_span:
current_span.set_attribute("service.tenant.id", user.current_tenant_id)
current_span.set_attribute("service.user.id", user.id)
try:
current_span = get_current_span()
if isinstance(user, Account) and user.current_tenant_id:
tenant_id = user.current_tenant_id
elif isinstance(user, EndUser):
tenant_id = user.tenant_id
else:
return
if current_span:
current_span.set_attribute("service.tenant.id", tenant_id)
current_span.set_attribute("service.user.id", user.id)
except Exception:
logging.exception("Error setting tenant and user attributes")
pass
def init_app(app: DifyApp):
@@ -47,21 +58,25 @@ def init_app(app: DifyApp):
def response_hook(span: Span, status: str, response_headers: list):
if span and span.is_recording():
if status.startswith("2"):
span.set_status(StatusCode.OK)
else:
span.set_status(StatusCode.ERROR, status)
try:
if status.startswith("2"):
span.set_status(StatusCode.OK)
else:
span.set_status(StatusCode.ERROR, status)
status = status.split(" ")[0]
status_code = int(status)
status_class = f"{status_code // 100}xx"
attributes: dict[str, str | int] = {"status_code": status_code, "status_class": status_class}
request = flask.request
if request and request.url_rule:
attributes[SpanAttributes.HTTP_TARGET] = str(request.url_rule.rule)
if request and request.method:
attributes[SpanAttributes.HTTP_METHOD] = str(request.method)
_http_response_counter.add(1, attributes)
status = status.split(" ")[0]
status_code = int(status)
status_class = f"{status_code // 100}xx"
attributes: dict[str, str | int] = {"status_code": status_code, "status_class": status_class}
request = flask.request
if request and request.url_rule:
attributes[SpanAttributes.HTTP_TARGET] = str(request.url_rule.rule)
if request and request.method:
attributes[SpanAttributes.HTTP_METHOD] = str(request.method)
_http_response_counter.add(1, attributes)
except Exception:
logging.exception("Error setting status and attributes")
pass
instrumentor = FlaskInstrumentor()
if dify_config.DEBUG:
@@ -92,7 +107,7 @@ def init_app(app: DifyApp):
class ExceptionLoggingHandler(logging.Handler):
"""Custom logging handler that creates spans for logging.exception() calls"""
def emit(self, record):
def emit(self, record: logging.LogRecord):
try:
if record.exc_info:
tracer = get_tracer_provider().get_tracer("dify.exception.logging")
@@ -107,9 +122,12 @@ def init_app(app: DifyApp):
},
) as span:
span.set_status(StatusCode.ERROR)
span.record_exception(record.exc_info[1])
span.set_attribute("exception.type", record.exc_info[0].__name__)
span.set_attribute("exception.message", str(record.exc_info[1]))
if record.exc_info[1]:
span.record_exception(record.exc_info[1])
span.set_attribute("exception.message", str(record.exc_info[1]))
if record.exc_info[0]:
span.set_attribute("exception.type", record.exc_info[0].__name__)
except Exception:
pass

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -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>

View File

@@ -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>
)}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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'
/>
)
}

View File

@@ -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>

View File

@@ -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,9 +246,12 @@ const CustomWebAppBrand = () => {
{!webappBrandRemoved && (
<>
<div className='system-2xs-medium-uppercase text-text-tertiary'>POWERED BY</div>
{webappLogo
? <img src={`${webappLogo}?hash=${imgKey}`} 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' />
: webappLogo
? <img src={`${webappLogo}?hash=${imgKey}`} alt='logo' className='block h-5 w-auto' />
: <DifyLogo size='small' />
}
</>
)}
@@ -303,9 +308,12 @@ const CustomWebAppBrand = () => {
{!webappBrandRemoved && (
<>
<div className='system-2xs-medium-uppercase text-text-tertiary'>POWERED BY</div>
{webappLogo
? <img src={`${webappLogo}?hash=${imgKey}`} 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' />
: webappLogo
? <img src={`${webappLogo}?hash=${imgKey}`} alt='logo' className='block h-5 w-auto' />
: <DifyLogo size='small' />
}
</>
)}

View File

@@ -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>

View File

@@ -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 />}

View File

@@ -78,7 +78,7 @@ const ActionList = ({
className='w-full'
onClick={() => setShowSettingAuth(true)}
disabled={!isCurrentWorkspaceManager}
>{t('tools.auth.unauthorized')}</Button>
>{t('workflow.nodes.tool.authorize')}</Button>
)}
</div>
<div className='flex flex-col gap-2'>

View File

@@ -141,7 +141,7 @@ const MultipleToolSelector = ({
}
panelShowState={panelShowState}
onPanelShowStateChange={setPanelShowState}
isEdit={false}
/>
{value.length === 0 && (
<div className='system-xs-regular flex justify-center rounded-[10px] bg-background-section p-3 text-text-tertiary'>{t('plugin.detailPanel.toolSelector.empty')}</div>
@@ -158,6 +158,7 @@ const MultipleToolSelector = ({
onSelect={item => handleConfigure(item, index)}
onDelete={() => handleDelete(index)}
supportEnableSwitch
isEdit
/>
</div>
))}

View File

@@ -54,6 +54,7 @@ type Props = {
scope?: string
value?: ToolValue
selectedTools?: ToolValue[]
isEdit?: boolean
onSelect: (tool: {
provider_name: string
tool_name: string
@@ -77,6 +78,7 @@ type Props = {
const ToolSelector: FC<Props> = ({
value,
selectedTools,
isEdit,
disabled,
placement = 'left',
offset = 4,
@@ -277,7 +279,7 @@ const ToolSelector: FC<Props> = ({
<div className={cn('relative max-h-[642px] min-h-20 w-[361px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur pb-4 shadow-lg backdrop-blur-sm', !isShowSettingAuth && 'overflow-y-auto pb-2')}>
{!isShowSettingAuth && (
<>
<div className='system-xl-semibold px-4 pb-1 pt-3.5 text-text-primary'>{t('plugin.detailPanel.toolSelector.title')}</div>
<div className='system-xl-semibold px-4 pb-1 pt-3.5 text-text-primary'>{t(`plugin.detailPanel.toolSelector.${isEdit ? 'toolSetting' : 'title'}`)}</div>
{/* base form */}
<div className='flex flex-col gap-3 px-4 py-2'>
<div className='flex flex-col gap-1'>

View File

@@ -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)

View File

@@ -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>

View File

@@ -79,7 +79,7 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({
className='w-full'
onClick={showSetAuthModal}
>
{t(`${i18nPrefix}.toAuthorize`)}
{t(`${i18nPrefix}.authorize`)}
</Button>
</div>
</>

View File

@@ -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}

View File

@@ -14,7 +14,6 @@ const translation = {
},
author: 'Von',
auth: {
unauthorized: 'Zur Autorisierung',
authorized: 'Autorisiert',
setup: 'Autorisierung einrichten, um zu nutzen',
setupModalTitle: 'Autorisierung einrichten',

View File

@@ -648,7 +648,6 @@ const translation = {
'assignedVarsDescription': 'Zugewiesene Variablen müssen beschreibbare Variablen sein, z. B. Konversationsvariablen.',
},
tool: {
toAuthorize: 'Autorisieren',
inputVars: 'Eingabevariablen',
outputVars: {
text: 'durch das Tool generierter Inhalt',

View File

@@ -77,6 +77,7 @@ const translation = {
modelNum: '{{num}} MODELS INCLUDED',
toolSelector: {
title: 'Add tool',
toolSetting: 'Tool Settings',
toolLabel: 'Tool',
descriptionLabel: 'Tool description',
descriptionPlaceholder: 'Brief description of the tool\'s purpose, e.g., get the temperature for a specific location.',

View File

@@ -15,7 +15,6 @@ const translation = {
},
author: 'By',
auth: {
unauthorized: 'To Authorize',
authorized: 'Authorized',
setup: 'Set up authorization to use',
setupModalTitle: 'Set Up Authorization',

View File

@@ -651,7 +651,7 @@ const translation = {
'assignedVarsDescription': 'Assigned variables must be writable variables, such as conversation variables.',
},
tool: {
toAuthorize: 'To authorize',
authorize: 'Authorize',
inputVars: 'Input Variables',
outputVars: {
text: 'tool generated content',

View File

@@ -15,7 +15,6 @@ const translation = {
},
author: 'Por',
auth: {
unauthorized: 'Para Autorizar',
authorized: 'Autorizado',
setup: 'Configurar la autorización para usar',
setupModalTitle: 'Configurar Autorización',

View File

@@ -646,7 +646,6 @@ const translation = {
'assignedVarsDescription': 'Las variables asignadas deben ser variables grabables, como las variables de conversación.',
},
tool: {
toAuthorize: 'Para autorizar',
inputVars: 'Variables de entrada',
outputVars: {
text: 'Contenido generado por la herramienta',

View File

@@ -15,7 +15,6 @@ const translation = {
},
author: 'توسط',
auth: {
unauthorized: 'برای مجوز دادن',
authorized: 'مجوز داده شده',
setup: 'تنظیم مجوز برای استفاده',
setupModalTitle: 'تنظیم مجوز',

View File

@@ -648,7 +648,6 @@ const translation = {
'varNotSet': 'متغیر NOT Set',
},
tool: {
toAuthorize: 'برای مجوز دادن',
inputVars: 'متغیرهای ورودی',
outputVars: {
text: 'محتوای تولید شده توسط ابزار',

View File

@@ -14,7 +14,6 @@ const translation = {
},
author: 'Par',
auth: {
unauthorized: 'Pour Autoriser',
authorized: 'Autorisé',
setup: 'Mettez en place l\'autorisation à utiliser',
setupModalTitle: 'Configurer l\'Autorisation',

View File

@@ -647,7 +647,6 @@ const translation = {
'selectAssignedVariable': 'Sélectionner la variable affectée...',
},
tool: {
toAuthorize: 'Autoriser',
inputVars: 'Variables de saisie',
outputVars: {
text: 'contenu généré par l\'outil',

View File

@@ -15,7 +15,6 @@ const translation = {
},
author: 'द्वारा',
auth: {
unauthorized: 'अधिकृत करने के लिए',
authorized: 'अधिकृत',
setup: 'उपयोग करने के लिए अधिकृति सेटअप करें',
setupModalTitle: 'अधिकृति सेटअप करें',

View File

@@ -664,7 +664,6 @@ const translation = {
'noAssignedVars': 'कोई उपलब्ध असाइन किए गए चर नहीं',
},
tool: {
toAuthorize: 'अधिकृत करने के लिए',
inputVars: 'इनपुट वेरिएबल्स',
outputVars: {
text: 'उपकरण द्वारा उत्पन्न सामग्री',

View File

@@ -15,7 +15,6 @@ const translation = {
},
author: 'Di',
auth: {
unauthorized: 'Per Autorizzare',
authorized: 'Autorizzato',
setup: 'Configura l\'autorizzazione per utilizzare',
setupModalTitle: 'Configura Autorizzazione',

View File

@@ -666,7 +666,6 @@ const translation = {
'noVarTip': 'Fare clic sul pulsante "+" per aggiungere variabili',
},
tool: {
toAuthorize: 'Per autorizzare',
inputVars: 'Variabili di Input',
outputVars: {
text: 'contenuto generato dallo strumento',

View File

@@ -15,7 +15,6 @@ const translation = {
},
author: '著者:',
auth: {
unauthorized: '認証する',
authorized: '認証済み',
setup: '使用するための認証を設定する',
setupModalTitle: '認証の設定',

View File

@@ -654,7 +654,6 @@ const translation = {
'assignedVarsDescription': '代入される変数は、会話変数などの書き込み可能な変数である必要があります。',
},
tool: {
toAuthorize: '承認するには',
inputVars: '入力変数',
outputVars: {
text: 'ツールが生成したコンテンツ',

View File

@@ -15,7 +15,6 @@ const translation = {
},
author: '저자',
auth: {
unauthorized: '인증되지 않음',
authorized: '인증됨',
setup: '사용을 위한 인증 설정',
setupModalTitle: '인증 설정',

View File

@@ -648,7 +648,6 @@ const translation = {
'varNotSet': '변수가 설정되지 않음',
},
tool: {
toAuthorize: '승인하기',
inputVars: '입력 변수',
outputVars: {
text: '도구가 생성한 내용',

View File

@@ -14,7 +14,6 @@ const translation = {
},
author: 'Przez',
auth: {
unauthorized: 'Autoryzacja',
authorized: 'Zautoryzowane',
setup: 'Skonfiguruj autoryzację aby użyć',
setupModalTitle: 'Konfiguruj autoryzację',

View File

@@ -648,7 +648,6 @@ const translation = {
'noVarTip': 'Kliknij przycisk "+", aby dodać zmienne',
},
tool: {
toAuthorize: 'Do autoryzacji',
inputVars: 'Zmienne wejściowe',
outputVars: {
text: 'treść generowana przez narzędzie',

View File

@@ -14,7 +14,6 @@ const translation = {
},
author: 'Por',
auth: {
unauthorized: 'Para Autorizar',
authorized: 'Autorizado',
setup: 'Configurar autorização para usar',
setupModalTitle: 'Configurar Autorização',

View File

@@ -648,7 +648,6 @@ const translation = {
'variables': 'Variáveis',
},
tool: {
toAuthorize: 'Autorizar',
inputVars: 'Variáveis de entrada',
outputVars: {
text: 'conteúdo gerado pela ferramenta',

View File

@@ -14,7 +14,6 @@ const translation = {
},
author: 'De',
auth: {
unauthorized: 'Pentru a Autoriza',
authorized: 'Autorizat',
setup: 'Configurează autorizarea pentru a utiliza',
setupModalTitle: 'Configurează Autorizarea',

View File

@@ -648,7 +648,6 @@ const translation = {
'variables': 'Variabile',
},
tool: {
toAuthorize: 'Autorizați',
inputVars: 'Variabile de intrare',
outputVars: {
text: 'conținut generat de instrument',

View File

@@ -15,7 +15,6 @@ const translation = {
},
author: 'Автор',
auth: {
unauthorized: 'Авторизовать',
authorized: 'Авторизовано',
setup: 'Настроить авторизацию для использования',
setupModalTitle: 'Настроить авторизацию',

View File

@@ -648,7 +648,6 @@ const translation = {
'selectAssignedVariable': 'Выберите назначенную переменную...',
},
tool: {
toAuthorize: 'Авторизовать',
inputVars: 'Входные переменные',
outputVars: {
text: 'контент, сгенерированный инструментом',

View File

@@ -15,7 +15,6 @@ const translation = {
},
author: 'Avtor',
auth: {
unauthorized: 'Za avtorizacijo',
authorized: 'Avtorizirano',
setup: 'Nastavite avtorizacijo za uporabo',
setupModalTitle: 'Nastavi avtorizacijo',

View File

@@ -488,7 +488,6 @@ const translation = {
'variable': 'Spremenljivka',
},
tool: {
toAuthorize: 'Za avtorizacijo',
inputVars: 'Vhodne spremenljivke',
outputVars: {
text: 'orodje je ustvarilo vsebino',

View File

@@ -15,7 +15,6 @@ const translation = {
},
author: 'โดย',
auth: {
unauthorized: 'การอนุญาต',
authorized: 'อนุญาต',
setup: 'ตั้งค่าการให้สิทธิ์เพื่อใช้',
setupModalTitle: 'ตั้งค่าการให้สิทธิ์',

View File

@@ -647,7 +647,6 @@ const translation = {
'setParameter': 'ตั้งค่าพารามิเตอร์...',
},
tool: {
toAuthorize: 'เพื่ออนุญาต',
inputVars: 'ตัวแปรอินพุต',
outputVars: {
text: 'เนื้อหาที่สร้างขึ้นด้วยเครื่องมือ',

View File

@@ -15,7 +15,6 @@ const translation = {
},
author: 'Tarafından',
auth: {
unauthorized: 'Yetki Ver',
authorized: 'Yetkilendirildi',
setup: 'Kullanmak için yetkilendirmeyi ayarla',
setupModalTitle: 'Yetkilendirmeyi Ayarla',

View File

@@ -649,7 +649,6 @@ const translation = {
'noAssignedVars': 'Kullanılabilir atanmış değişken yok',
},
tool: {
toAuthorize: 'Yetkilendirmek için',
inputVars: 'Giriş Değişkenleri',
outputVars: {
text: 'araç tarafından oluşturulan içerik',

View File

@@ -14,7 +14,6 @@ const translation = {
},
author: 'Автор',
auth: {
unauthorized: 'Авторизуватися',
authorized: 'Авторизовано',
setup: 'Налаштувати авторизацію, щоб використовувати',
setupModalTitle: 'Налаштування авторизації',

View File

@@ -648,7 +648,6 @@ const translation = {
'setParameter': 'Встановити параметр...',
},
tool: {
toAuthorize: 'Авторизувати',
inputVars: 'Вхідні змінні',
outputVars: {
text: 'генерований вміст інструменту',

View File

@@ -14,7 +14,6 @@ const translation = {
},
author: 'Tác giả',
auth: {
unauthorized: 'Chưa xác thực',
authorized: 'Đã xác thực',
setup: 'Thiết lập xác thực để sử dụng',
setupModalTitle: 'Thiết lập xác thực',

View File

@@ -648,7 +648,6 @@ const translation = {
'variables': 'Biến',
},
tool: {
toAuthorize: 'Ủy quyền',
inputVars: 'Biến đầu vào',
outputVars: {
text: 'nội dung do công cụ tạo ra',

View File

@@ -77,6 +77,7 @@ const translation = {
modelNum: '{{num}} 模型已包含',
toolSelector: {
title: '添加工具',
toolSetting: '工具设置',
toolLabel: '工具',
descriptionLabel: '工具描述',
descriptionPlaceholder: '简要描述工具目的,例如,获取特定位置的温度。',

View File

@@ -15,7 +15,6 @@ const translation = {
},
author: '作者',
auth: {
unauthorized: '去授权',
authorized: '已授权',
setup: '要使用请先授权',
setupModalTitle: '设置授权',

View File

@@ -652,7 +652,7 @@ const translation = {
'assignedVarsDescription': '赋值变量必须是可写入的变量,例如会话变量。',
},
tool: {
toAuthorize: '授权',
authorize: '授权',
inputVars: '输入变量',
outputVars: {
text: '工具生成的内容',

View File

@@ -14,7 +14,6 @@ const translation = {
},
author: '作者',
auth: {
unauthorized: '去授權',
authorized: '已授權',
setup: '要使用請先授權',
setupModalTitle: '設定授權',

View File

@@ -648,7 +648,7 @@ const translation = {
'varNotSet': '未設置變數',
},
tool: {
toAuthorize: '授權',
authorize: '授權',
inputVars: '輸入變量',
outputVars: {
text: '工具生成的內容',

View File

@@ -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,
}
}