mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
Merge branch 'main' into e-300
This commit is contained in:
@ -6,10 +6,12 @@ NEXT_PUBLIC_EDITION=SELF_HOSTED
|
||||
# different from api or web app domain.
|
||||
# example: http://cloud.dify.ai/console/api
|
||||
NEXT_PUBLIC_API_PREFIX=http://localhost:5001/console/api
|
||||
NEXT_PUBLIC_WEB_PREFIX=http://localhost:3000
|
||||
# The URL for Web APP, refers to the Web App base URL of WEB service if web app domain is different from
|
||||
# console or api domain.
|
||||
# example: http://udify.app/api
|
||||
NEXT_PUBLIC_PUBLIC_API_PREFIX=http://localhost:5001/api
|
||||
NEXT_PUBLIC_PUBLIC_WEB_PREFIX=http://localhost:3000
|
||||
# The API PREFIX for MARKETPLACE
|
||||
NEXT_PUBLIC_MARKETPLACE_API_PREFIX=https://marketplace.dify.ai/api/v1
|
||||
# The URL for MARKETPLACE
|
||||
|
||||
@ -31,10 +31,12 @@ NEXT_PUBLIC_EDITION=SELF_HOSTED
|
||||
# different from api or web app domain.
|
||||
# example: http://cloud.dify.ai/console/api
|
||||
NEXT_PUBLIC_API_PREFIX=http://localhost:5001/console/api
|
||||
NEXT_PUBLIC_WEB_PREFIX=http://localhost:3000
|
||||
# The URL for Web APP, refers to the Web App base URL of WEB service if web app domain is different from
|
||||
# console or api domain.
|
||||
# example: http://udify.app/api
|
||||
NEXT_PUBLIC_PUBLIC_API_PREFIX=http://localhost:5001/api
|
||||
NEXT_PUBLIC_PUBLIC_WEB_PREFIX=http://localhost:3000
|
||||
|
||||
# SENTRY
|
||||
NEXT_PUBLIC_SENTRY_DSN=
|
||||
|
||||
@ -17,7 +17,7 @@ import AppsContext, { useAppContext } from '@/context/app-context'
|
||||
import type { HtmlContentProps } from '@/app/components/base/popover'
|
||||
import CustomPopover from '@/app/components/base/popover'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { WEB_PREFIX } from '@/config'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
@ -235,7 +235,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
||||
try {
|
||||
const { installed_apps }: any = await fetchInstalledAppList(app.id) || {}
|
||||
if (installed_apps?.length > 0)
|
||||
window.open(`${basePath}/explore/installed/${installed_apps[0].id}`, '_blank')
|
||||
window.open(`${WEB_PREFIX}/explore/installed/${installed_apps[0].id}`, '_blank')
|
||||
else
|
||||
throw new Error('No app found in Explore')
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => {
|
||||
className='mt-2 inline-flex cursor-pointer items-center text-xs text-text-accent'
|
||||
href={
|
||||
locale === LanguagesSupported[1]
|
||||
? 'https://docs.dify.ai/v/zh-hans/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
? 'https://docs.dify.ai/zh-hans/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
: 'https://docs.dify.ai/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
}
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
|
||||
@ -87,7 +87,7 @@ const Container = () => {
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className='scroll-container relative flex grow flex-col overflow-y-auto bg-background-body'>
|
||||
<div className='sticky top-0 z-10 flex flex-wrap justify-between gap-y-2 bg-background-body px-12 pb-2 pt-4 leading-[56px]'>
|
||||
<div className='sticky top-0 z-10 flex flex-wrap items-center justify-between gap-y-2 bg-background-body px-12 pb-2 pt-4 leading-[56px]'>
|
||||
<TabSliderNew
|
||||
value={activeTab}
|
||||
onChange={newActiveTab => setActiveTab(newActiveTab)}
|
||||
|
||||
@ -121,7 +121,7 @@ const Doc = ({ apiBaseUrl }: DocProps) => {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<article className={cn('prose-xl prose mx-1 rounded-t-xl bg-background-default px-4 pt-16 sm:mx-12', theme === Theme.dark && 'dark:prose-invert')}>
|
||||
<article className={cn('prose-xl prose mx-1 rounded-t-xl bg-background-default px-4 pt-16 sm:mx-12', theme === Theme.dark && 'prose-invert')}>
|
||||
{Template}
|
||||
</article>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { basePath } from '@/utils/var'
|
||||
import Link from 'next/link'
|
||||
import {
|
||||
RiAddLine,
|
||||
RiArrowRightLine,
|
||||
@ -18,7 +18,7 @@ const CreateAppCard = (
|
||||
<div className='bg-background-default-dimm flex min-h-[160px] flex-col rounded-xl border-[0.5px]
|
||||
border-components-panel-border transition-all duration-200 ease-in-out'
|
||||
>
|
||||
<a ref={ref} className='group flex grow cursor-pointer items-start p-4' href={`${basePath}/datasets/create`}>
|
||||
<Link ref={ref} className='group flex grow cursor-pointer items-start p-4' href={'/datasets/create'}>
|
||||
<div className='flex items-center gap-3'>
|
||||
<div className='flex h-10 w-10 items-center justify-center rounded-lg border border-dashed border-divider-regular bg-background-default-lighter
|
||||
p-2 group-hover:border-solid group-hover:border-effects-highlight group-hover:bg-background-default-dodge'
|
||||
@ -27,12 +27,12 @@ const CreateAppCard = (
|
||||
</div>
|
||||
<div className='system-md-semibold text-text-secondary group-hover:text-text-accent'>{t('dataset.createDataset')}</div>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className='system-xs-regular p-4 pt-0 text-text-tertiary'>{t('dataset.createDatasetIntro')}</div>
|
||||
<a className='group flex cursor-pointer items-center gap-1 rounded-b-xl border-t-[0.5px] border-divider-subtle p-4' href={`${basePath}/datasets/connect`}>
|
||||
<Link className='group flex cursor-pointer items-center gap-1 rounded-b-xl border-t-[0.5px] border-divider-subtle p-4' href={'datasets/connect'}>
|
||||
<div className='system-xs-medium text-text-tertiary group-hover:text-text-accent'>{t('dataset.connectDataset')}</div>
|
||||
<RiArrowRightLine className='h-3.5 w-3.5 text-text-tertiary group-hover:text-text-accent' />
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -314,7 +314,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
</Property>
|
||||
<Property name='indexing_technique' type='string' key='indexing_technique'>
|
||||
Index technique (optional)
|
||||
If this is not set, embedding_model, embedding_provider_name and retrieval_model will be set to null
|
||||
If this is not set, embedding_model, embedding_model_provider and retrieval_model will be set to null
|
||||
- <code>high_quality</code> High quality
|
||||
- <code>economy</code> Economy
|
||||
</Property>
|
||||
@ -338,7 +338,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
<Property name='embedding_model' type='str' key='embedding_model'>
|
||||
Embedding model name (optional)
|
||||
</Property>
|
||||
<Property name='embedding_provider_name' type='str' key='embedding_provider_name'>
|
||||
<Property name='embedding_model_provider' type='str' key='embedding_model_provider'>
|
||||
Embedding model provider name (optional)
|
||||
</Property>
|
||||
<Property name='retrieval_model' type='object' key='retrieval_model'>
|
||||
@ -1040,10 +1040,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
@ -1335,10 +1333,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
@ -1620,10 +1616,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
@ -337,7 +337,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
<Property name='embedding_model' type='str' key='embedding_model'>
|
||||
埋め込みモデル名(任意)
|
||||
</Property>
|
||||
<Property name='embedding_provider_name' type='str' key='embedding_provider_name'>
|
||||
<Property name='embedding_model_provider' type='str' key='embedding_model_provider'>
|
||||
埋め込みモデルのプロバイダ名(任意)
|
||||
</Property>
|
||||
<Property name='retrieval_model' type='object' key='retrieval_model'>
|
||||
@ -501,7 +501,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="レスポンス">
|
||||
```text {{ title: 'Response' }}
|
||||
```text {{ title: 'レスポンス' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
@ -797,10 +797,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="レスポンス">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: 'レスポンス' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
@ -1092,10 +1090,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="レスポンス">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: 'レスポンス' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
@ -1377,10 +1373,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="レスポンス">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: 'レスポンス' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
@ -341,7 +341,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
<Property name='embedding_model' type='str' key='embedding_model'>
|
||||
Embedding 模型名称
|
||||
</Property>
|
||||
<Property name='embedding_provider_name' type='str' key='embedding_provider_name'>
|
||||
<Property name='embedding_model_provider' type='str' key='embedding_model_provider'>
|
||||
Embedding 模型供应商
|
||||
</Property>
|
||||
<Property name='retrieval_model' type='object' key='retrieval_model'>
|
||||
@ -1047,10 +1047,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
@ -1342,10 +1340,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
@ -1628,10 +1624,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
@ -31,7 +31,7 @@ import {
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { WEB_PREFIX } from '@/config'
|
||||
import { fetchInstalledAppList } from '@/service/explore'
|
||||
import EmbeddedModal from '@/app/components/app/overview/embedded'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
@ -89,7 +89,7 @@ const AppPublisher = ({
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { app_base_url: appBaseURL = '', access_token: accessToken = '' } = appDetail?.site ?? {}
|
||||
const appMode = (appDetail?.mode !== 'completion' && appDetail?.mode !== 'workflow') ? 'chat' : appDetail.mode
|
||||
const appURL = `${appBaseURL}${basePath}/${appMode}/${accessToken}`
|
||||
const appURL = `${appBaseURL}/${appMode}/${accessToken}`
|
||||
const isChatApp = ['chat', 'agent-chat', 'completion'].includes(appDetail?.mode || '')
|
||||
const { data: userCanAccessApp, isLoading: isGettingUserCanAccessApp, refetch } = useGetUserCanAccessApp({ appId: appDetail?.id, enabled: false })
|
||||
const { data: appAccessSubjects, isLoading: isGettingAppWhiteListSubjects } = useAppWhiteListSubjects(appDetail?.id, open && systemFeatures.webapp_auth.enabled && appDetail?.access_mode === AccessMode.SPECIFIC_GROUPS_MEMBERS)
|
||||
@ -154,7 +154,7 @@ const AppPublisher = ({
|
||||
try {
|
||||
const { installed_apps }: any = await fetchInstalledAppList(appDetail?.id) || {}
|
||||
if (installed_apps?.length > 0)
|
||||
window.open(`${basePath}/explore/installed/${installed_apps[0].id}`, '_blank')
|
||||
window.open(`${WEB_PREFIX}/explore/installed/${installed_apps[0].id}`, '_blank')
|
||||
else
|
||||
throw new Error('No app found in Explore')
|
||||
}
|
||||
|
||||
@ -46,8 +46,8 @@ const HistoryPanel: FC<Props> = ({
|
||||
<div className='flex justify-between rounded-b-xl bg-background-section-burn px-3 py-2 text-xs text-text-secondary'>
|
||||
<div>{t('appDebug.feature.conversationHistory.tip')}
|
||||
<a href={`${locale === LanguagesSupported[1]
|
||||
? 'https://docs.dify.ai/v/zh-hans/guides/application-design/prompt-engineering'
|
||||
: 'https://docs.dify.ai/features/prompt-engineering'}`}
|
||||
? 'https://docs.dify.ai/zh-hans/learn-more/extended-reading/prompt-engineering/README'
|
||||
: 'https://docs.dify.ai/en/features/prompt-engineering'}`}
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
className='text-[#155EEF]'>{t('appDebug.feature.conversationHistory.learnMore')}
|
||||
</a>
|
||||
|
||||
@ -14,7 +14,6 @@ import Loading from '@/app/components/base/loading'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
import { useKnowledge } from '@/hooks/use-knowledge'
|
||||
import cn from '@/utils/classnames'
|
||||
import { basePath } from '@/utils/var'
|
||||
|
||||
export type ISelectDataSetProps = {
|
||||
isShow: boolean
|
||||
@ -112,7 +111,7 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
|
||||
}}
|
||||
>
|
||||
<span className='text-text-tertiary'>{t('appDebug.feature.dataSet.noDataSet')}</span>
|
||||
<Link href={`${basePath}/datasets/create`} className='font-normal text-text-accent'>{t('appDebug.feature.dataSet.toCreate')}</Link>
|
||||
<Link href={'/datasets/create'} className='font-normal text-text-accent'>{t('appDebug.feature.dataSet.toCreate')}</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ const AdvancedModeWarning: FC<Props> = ({
|
||||
<span className='text-gray-700'>{t('appDebug.promptMode.advancedWarning.description')}</span>
|
||||
<a
|
||||
className='font-medium text-[#155EEF]'
|
||||
href={`https://docs.dify.ai/${locale === LanguagesSupported[1] ? 'v/zh-hans/guides/application-design/prompt-engineering' : 'features/prompt-engineering'}`}
|
||||
href={`https://docs.dify.ai/${locale === LanguagesSupported[1] ? '/guides/features/prompt-engineering' : 'features/prompt-engineering'}`}
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
>
|
||||
{t('appDebug.promptMode.advancedWarning.learnMore')}
|
||||
|
||||
@ -14,7 +14,7 @@ import type { AppIconSelection } from '../../base/app-icon-picker'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import cn from '@/utils/classnames'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { WEB_PREFIX } from '@/config'
|
||||
import AppsContext, { useAppContext } from '@/context/app-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
@ -310,17 +310,17 @@ function AppPreview({ mode }: { mode: AppMode }) {
|
||||
'chat': {
|
||||
title: t('app.types.chatbot'),
|
||||
description: t('app.newApp.chatbotUserDescription'),
|
||||
link: 'https://docs.dify.ai/guides/application-orchestrate#application_type',
|
||||
link: 'https://docs.dify.ai/guides/application-orchestrate/readme',
|
||||
},
|
||||
'advanced-chat': {
|
||||
title: t('app.types.advanced'),
|
||||
description: t('app.newApp.advancedUserDescription'),
|
||||
link: 'https://docs.dify.ai/guides/workflow',
|
||||
link: 'https://docs.dify.ai/en/guides/workflow/README',
|
||||
},
|
||||
'agent-chat': {
|
||||
title: t('app.types.agent'),
|
||||
description: t('app.newApp.agentUserDescription'),
|
||||
link: 'https://docs.dify.ai/guides/application-orchestrate/agent',
|
||||
link: 'https://docs.dify.ai/en/guides/application-orchestrate/agent',
|
||||
},
|
||||
'completion': {
|
||||
title: t('app.newApp.completeApp'),
|
||||
@ -330,7 +330,7 @@ function AppPreview({ mode }: { mode: AppMode }) {
|
||||
'workflow': {
|
||||
title: t('app.types.workflow'),
|
||||
description: t('app.newApp.workflowUserDescription'),
|
||||
link: 'https://docs.dify.ai/guides/workflow',
|
||||
link: 'https://docs.dify.ai/en/guides/workflow/README',
|
||||
},
|
||||
}
|
||||
const previewInfo = modeToPreviewInfoMap[mode]
|
||||
@ -353,11 +353,11 @@ function AppScreenShot({ mode, show }: { mode: AppMode; show: boolean }) {
|
||||
'workflow': 'Workflow',
|
||||
}
|
||||
return <picture>
|
||||
<source media="(resolution: 1x)" srcSet={`${basePath}/screenshots/${theme}/${modeToImageMap[mode]}.png`} />
|
||||
<source media="(resolution: 2x)" srcSet={`${basePath}/screenshots/${theme}/${modeToImageMap[mode]}@2x.png`} />
|
||||
<source media="(resolution: 3x)" srcSet={`${basePath}/screenshots/${theme}/${modeToImageMap[mode]}@3x.png`} />
|
||||
<source media="(resolution: 1x)" srcSet={`${WEB_PREFIX}/screenshots/${theme}/${modeToImageMap[mode]}.png`} />
|
||||
<source media="(resolution: 2x)" srcSet={`${WEB_PREFIX}/screenshots/${theme}/${modeToImageMap[mode]}@2x.png`} />
|
||||
<source media="(resolution: 3x)" srcSet={`${WEB_PREFIX}/screenshots/${theme}/${modeToImageMap[mode]}@3x.png`} />
|
||||
<Image className={show ? '' : 'hidden'}
|
||||
src={`${basePath}/screenshots/${theme}/${modeToImageMap[mode]}.png`}
|
||||
src={`${WEB_PREFIX}/screenshots/${theme}/${modeToImageMap[mode]}.png`}
|
||||
alt='App Screen Shot'
|
||||
width={664} height={448} />
|
||||
</picture>
|
||||
|
||||
@ -262,7 +262,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
{
|
||||
currentTab === CreateFromDSLModalTab.FROM_URL && (
|
||||
<div>
|
||||
<div className='system-md-semibold leading6 mb-1'>DSL URL</div>
|
||||
<div className='system-md-semibold mb-1 text-text-secondary'>DSL URL</div>
|
||||
<Input
|
||||
placeholder={t('app.importFromDSLUrlPlaceholder') || ''}
|
||||
value={dslUrlValue}
|
||||
|
||||
@ -3,6 +3,7 @@ import type { FC } from 'react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
RiDeleteBinLine,
|
||||
RiUploadCloud2Line,
|
||||
} from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
@ -10,8 +11,7 @@ import { formatFileSize } from '@/utils/format'
|
||||
import cn from '@/utils/classnames'
|
||||
import { Yaml as YamlIcon } from '@/app/components/base/icons/src/public/files'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import { UploadCloud01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import Button from '@/app/components/base/button'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
|
||||
export type Props = {
|
||||
file: File | undefined
|
||||
@ -102,19 +102,19 @@ const Uploader: FC<Props> = ({
|
||||
/>
|
||||
<div ref={dropRef}>
|
||||
{!file && (
|
||||
<div className={cn('flex h-12 items-center rounded-xl border border-dashed border-gray-200 bg-gray-50 text-sm font-normal', dragging && 'border border-[#B2CCFF] bg-[#F5F8FF]')}>
|
||||
<div className={cn('flex h-12 items-center rounded-[10px] border border-dashed border-components-dropzone-border bg-components-dropzone-bg text-sm font-normal', dragging && 'border-components-dropzone-border-accent bg-components-dropzone-bg-accent')}>
|
||||
<div className='flex w-full items-center justify-center space-x-2'>
|
||||
<UploadCloud01 className='mr-2 h-6 w-6' />
|
||||
<div className='text-gray-500'>
|
||||
<RiUploadCloud2Line className='h-6 w-6 text-text-tertiary' />
|
||||
<div className='text-text-tertiary'>
|
||||
{t('datasetCreation.stepOne.uploader.button')}
|
||||
<span className='cursor-pointer pl-1 text-[#155eef]' onClick={selectHandle}>{t('datasetDocuments.list.batchModal.browse')}</span>
|
||||
<span className='cursor-pointer pl-1 text-text-accent' onClick={selectHandle}>{t('datasetDocuments.list.batchModal.browse')}</span>
|
||||
</div>
|
||||
</div>
|
||||
{dragging && <div ref={dragRef} className='absolute left-0 top-0 h-full w-full' />}
|
||||
</div>
|
||||
)}
|
||||
{file && (
|
||||
<div className={cn('group flex items-center rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg shadow-xs', 'hover:border-[#B2CCFF] hover:bg-[#F5F8FF]')}>
|
||||
<div className={cn('group flex items-center rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg shadow-xs', ' hover:bg-components-panel-on-panel-item-bg-hover')}>
|
||||
<div className='flex items-center justify-center p-3'>
|
||||
<YamlIcon className="h-6 w-6 shrink-0" />
|
||||
</div>
|
||||
@ -126,12 +126,10 @@ const Uploader: FC<Props> = ({
|
||||
<span>{formatFileSize(file.size)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className='hidden items-center group-hover:flex'>
|
||||
<Button onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.change')}</Button>
|
||||
<div className='mx-2 h-4 w-px bg-gray-200' />
|
||||
<div className='cursor-pointer p-2' onClick={removeFile}>
|
||||
<div className='hidden items-center pr-3 group-hover:flex'>
|
||||
<ActionButton onClick={removeFile}>
|
||||
<RiDeleteBinLine className='h-4 w-4 text-text-tertiary' />
|
||||
</div>
|
||||
</ActionButton>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -7,7 +7,6 @@ import { usePathname } from 'next/navigation'
|
||||
import { useDebounce } from 'ahooks'
|
||||
import { omit } from 'lodash-es'
|
||||
import dayjs from 'dayjs'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import List from './list'
|
||||
import Filter, { TIME_PERIOD_MAPPING } from './filter'
|
||||
@ -110,7 +109,7 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
|
||||
? <Loading type='app' />
|
||||
: total > 0
|
||||
? <List logs={isChatMode ? chatConversations : completionConversations} appDetail={appDetail} onRefresh={isChatMode ? mutateChatList : mutateCompletionList} />
|
||||
: <EmptyElement appUrl={`${appDetail.site.app_base_url}${basePath}/${getWebAppType(appDetail.mode)}/${appDetail.site.access_token}`} />
|
||||
: <EmptyElement appUrl={`${appDetail.site.app_base_url}/${getWebAppType(appDetail.mode)}/${appDetail.site.access_token}`} />
|
||||
}
|
||||
{/* Show Pagination only if the total is more than the limit */}
|
||||
{(total && total > APP_PAGE_LIMIT)
|
||||
|
||||
@ -20,7 +20,6 @@ import Tooltip from '@/app/components/base/tooltip'
|
||||
import AppBasic from '@/app/components/app-sidebar/basic'
|
||||
import { asyncRunSafe, randomString } from '@/utils'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { basePath } from '@/utils/var'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
@ -101,7 +100,7 @@ function AppCard({
|
||||
const runningStatus = isApp ? appInfo.enable_site : appInfo.enable_api
|
||||
const { app_base_url, access_token } = appInfo.site ?? {}
|
||||
const appMode = (appInfo.mode !== 'completion' && appInfo.mode !== 'workflow') ? 'chat' : appInfo.mode
|
||||
const appUrl = `${app_base_url}${basePath}/${appMode}/${access_token}`
|
||||
const appUrl = `${app_base_url}/${appMode}/${access_token}`
|
||||
const apiUrl = appInfo?.api_base_url
|
||||
|
||||
const genClickFuncByName = (opName: string) => {
|
||||
|
||||
@ -103,7 +103,7 @@ const CustomizeModal: FC<IShareLinkProps> = ({
|
||||
window.open(
|
||||
`https://docs.dify.ai/${locale !== LanguagesSupported[1]
|
||||
? 'user-guide/launching-dify-apps/developing-with-apis'
|
||||
: `v/${locale.toLowerCase()}/guides/application-publishing/developing-with-apis`
|
||||
: `${locale.toLowerCase()}/guides/application-publishing/developing-with-apis`
|
||||
}`,
|
||||
'_blank',
|
||||
)
|
||||
|
||||
@ -13,7 +13,6 @@ import { IS_CE_EDITION } from '@/config'
|
||||
import type { SiteInfo } from '@/models/share'
|
||||
import { useThemeContext } from '@/app/components/base/chat/embedded-chatbot/theme/theme-context'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import { basePath } from '@/utils/var'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
@ -29,7 +28,7 @@ const OPTION_MAP = {
|
||||
iframe: {
|
||||
getContent: (url: string, token: string) =>
|
||||
`<iframe
|
||||
src="${url}${basePath}/chatbot/${token}"
|
||||
src="${url}/chatbot/${token}"
|
||||
style="width: 100%; height: 100%; min-height: 700px"
|
||||
frameborder="0"
|
||||
allow="microphone">
|
||||
@ -44,7 +43,7 @@ const OPTION_MAP = {
|
||||
isDev: true`
|
||||
: ''}${IS_CE_EDITION
|
||||
? `,
|
||||
baseUrl: '${url}${basePath}'`
|
||||
baseUrl: '${url}'`
|
||||
: ''},
|
||||
systemVariables: {
|
||||
// user_id: 'YOU CAN DEFINE USER ID HERE',
|
||||
@ -53,7 +52,7 @@ const OPTION_MAP = {
|
||||
}
|
||||
</script>
|
||||
<script
|
||||
src="${url}${basePath}/embed.min.js"
|
||||
src="${url}/embed.min.js"
|
||||
id="${token}"
|
||||
defer>
|
||||
</script>
|
||||
@ -68,7 +67,7 @@ const OPTION_MAP = {
|
||||
</style>`,
|
||||
},
|
||||
chromePlugin: {
|
||||
getContent: (url: string, token: string) => `ChatBot URL: ${url}${basePath}/chatbot/${token}`,
|
||||
getContent: (url: string, token: string) => `ChatBot URL: ${url}/chatbot/${token}`,
|
||||
},
|
||||
}
|
||||
const prefixEmbedded = 'appOverview.overview.appInfo.embedded'
|
||||
|
||||
@ -238,7 +238,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||
</div>
|
||||
<div className='system-xs-regular mt-0.5 text-text-tertiary'>
|
||||
<span>{t(`${prefixSettings}.modalTip`)}</span>
|
||||
<Link href={`${locale === LanguagesSupported[1] ? 'https://docs.dify.ai/zh-hans/guides/application-publishing/launch-your-webapp-quickly#she-zhi-ni-de-ai-zhan-dian' : 'https://docs.dify.ai/guides/application-publishing/launch-your-webapp-quickly#setting-up-your-ai-site'}`} target='_blank' rel='noopener noreferrer' className='text-text-accent'>{t('common.operation.learnMore')}</Link>
|
||||
<Link href={`${locale === LanguagesSupported[1] ? 'https://docs.dify.ai/zh-hans/guides/application-publishing/launch-your-webapp-quickly#she-zhi-ni-de-ai-zhan-dian' : 'https://docs.dify.ai/en/guides/application-publishing/launch-your-webapp-quickly/README'}`} target='_blank' rel='noopener noreferrer' className='text-text-accent'>{t('common.operation.learnMore')}</Link>
|
||||
</div>
|
||||
</div>
|
||||
{/* form body */}
|
||||
|
||||
@ -11,7 +11,6 @@ import timezone from 'dayjs/plugin/timezone'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import Link from 'next/link'
|
||||
import List from './list'
|
||||
import { basePath } from '@/utils/var'
|
||||
import Filter, { TIME_PERIOD_MAPPING } from './filter'
|
||||
import Pagination from '@/app/components/base/pagination'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
@ -101,7 +100,7 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
|
||||
? <Loading type='app' />
|
||||
: total > 0
|
||||
? <List logs={workflowLogs} appDetail={appDetail} onRefresh={mutate} />
|
||||
: <EmptyElement appUrl={`${appDetail.site.app_base_url}${basePath}/${getWebAppType(appDetail.mode)}/${appDetail.site.access_token}`} />
|
||||
: <EmptyElement appUrl={`${appDetail.site.app_base_url}/${getWebAppType(appDetail.mode)}/${appDetail.site.access_token}`} />
|
||||
}
|
||||
{/* Show Pagination only if the total is more than the limit */}
|
||||
{(total && total > APP_PAGE_LIMIT)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useCallback } from 'react'
|
||||
import React, { memo, useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useChatWithHistoryContext } from '../context'
|
||||
import Input from '@/app/components/base/input'
|
||||
@ -112,4 +112,4 @@ const InputsFormContent = ({ showTip }: Props) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default InputsFormContent
|
||||
export default memo(InputsFormContent)
|
||||
|
||||
@ -424,6 +424,8 @@ export const useChat = (
|
||||
const response = responseItem as any
|
||||
if (thought.message_id && !hasSetResponseId)
|
||||
response.id = thought.message_id
|
||||
if (thought.conversation_id)
|
||||
response.conversationId = thought.conversation_id
|
||||
|
||||
if (response.agent_thoughts.length === 0) {
|
||||
response.agent_thoughts.push(thought)
|
||||
|
||||
@ -5,6 +5,8 @@ import type {
|
||||
import {
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import type { ChatItem } from '../types'
|
||||
@ -52,6 +54,8 @@ const Question: FC<QuestionProps> = ({
|
||||
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const [editedContent, setEditedContent] = useState(content)
|
||||
const [contentWidth, setContentWidth] = useState(0)
|
||||
const contentRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const handleEdit = useCallback(() => {
|
||||
setIsEditing(true)
|
||||
@ -75,14 +79,31 @@ const Question: FC<QuestionProps> = ({
|
||||
item.nextSibling && switchSibling?.(item.nextSibling)
|
||||
}, [switchSibling, item.prevSibling, item.nextSibling])
|
||||
|
||||
const getContentWidth = () => {
|
||||
if (contentRef.current)
|
||||
setContentWidth(contentRef.current?.clientWidth)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!contentRef.current)
|
||||
return
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
getContentWidth()
|
||||
})
|
||||
resizeObserver.observe(contentRef.current)
|
||||
return () => {
|
||||
resizeObserver.disconnect()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className='mb-2 flex justify-end pl-14 last:mb-0'>
|
||||
<div className={cn('group relative mr-4 flex max-w-full items-start', isEditing && 'flex-1')}>
|
||||
<div className='mb-2 flex justify-end last:mb-0'>
|
||||
<div className={cn('group relative mr-4 flex max-w-full items-start pl-14', isEditing && 'flex-1')}>
|
||||
<div className={cn('mr-2 gap-1', isEditing ? 'hidden' : 'flex')}>
|
||||
<div className="
|
||||
absolutegap-0.5 hidden rounded-[10px] border-[0.5px] border-components-actionbar-border
|
||||
bg-components-actionbar-bg p-0.5 shadow-md backdrop-blur-sm group-hover:flex
|
||||
">
|
||||
<div
|
||||
className="absolute hidden gap-0.5 rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-md backdrop-blur-sm group-hover:flex"
|
||||
style={{ right: contentWidth + 8 }}
|
||||
>
|
||||
<ActionButton onClick={() => {
|
||||
copy(content)
|
||||
Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') })
|
||||
@ -95,6 +116,7 @@ const Question: FC<QuestionProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
ref={contentRef}
|
||||
className='w-full rounded-2xl bg-[#D1E9FF]/50 px-4 py-3 text-sm text-gray-900'
|
||||
style={theme?.chatBubbleColorStyle ? CssTransform(theme.chatBubbleColorStyle) : {}}
|
||||
>
|
||||
|
||||
@ -41,6 +41,7 @@ export type ThoughtItem = {
|
||||
tool_input: string
|
||||
tool_labels?: { [key: string]: TypeWithI18N }
|
||||
message_id: string
|
||||
conversation_id: string
|
||||
observation: string
|
||||
position: number
|
||||
files?: string[]
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useCallback } from 'react'
|
||||
import React, { memo, useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useEmbeddedChatbotContext } from '../context'
|
||||
import Input from '@/app/components/base/input'
|
||||
@ -112,4 +112,4 @@ const InputsFormContent = ({ showTip }: Props) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default InputsFormContent
|
||||
export default memo(InputsFormContent)
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Icon L">
|
||||
<g id="Vector">
|
||||
<path d="M2.66602 11.3333H0.666016L3.33268 8.66667L5.99935 11.3333H3.99935L3.99935 14H2.66602L2.66602 11.3333Z" fill="#354052"/>
|
||||
<path d="M2.66602 4.66667L2.66602 2L3.99935 2L3.99935 4.66667L5.99935 4.66667L3.33268 7.33333L0.666016 4.66667L2.66602 4.66667Z" fill="#354052"/>
|
||||
<path d="M7.33268 2.66667H13.9993V4H7.33268V2.66667ZM7.33268 12H13.9993V13.3333H7.33268V12ZM5.99935 7.33333H13.9993V8.66667H5.99935V7.33333Z" fill="#354052"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 579 B |
@ -0,0 +1,62 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "16",
|
||||
"height": "16",
|
||||
"viewBox": "0 0 16 16",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Icon L"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Vector"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M2.66602 11.3333H0.666016L3.33268 8.66667L5.99935 11.3333H3.99935L3.99935 14H2.66602L2.66602 11.3333Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M2.66602 4.66667L2.66602 2L3.99935 2L3.99935 4.66667L5.99935 4.66667L3.33268 7.33333L0.666016 4.66667L2.66602 4.66667Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M7.33268 2.66667H13.9993V4H7.33268V2.66667ZM7.33268 12H13.9993V13.3333H7.33268V12ZM5.99935 7.33333H13.9993V8.66667H5.99935V7.33333Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Collapse"
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Collapse.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'Collapse'
|
||||
|
||||
export default Icon
|
||||
@ -1,5 +1,6 @@
|
||||
export { default as AlignLeft } from './AlignLeft'
|
||||
export { default as BezierCurve03 } from './BezierCurve03'
|
||||
export { default as Collapse } from './Collapse'
|
||||
export { default as Colors } from './Colors'
|
||||
export { default as ImageIndentLeft } from './ImageIndentLeft'
|
||||
export { default as LeftIndent02 } from './LeftIndent02'
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { FC } from 'react'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { WEB_PREFIX } from '@/config'
|
||||
|
||||
type LogoEmbeddedChatAvatarProps = {
|
||||
className?: string
|
||||
@ -9,7 +9,7 @@ const LogoEmbeddedChatAvatar: FC<LogoEmbeddedChatAvatarProps> = ({
|
||||
}) => {
|
||||
return (
|
||||
<img
|
||||
src={`${basePath}/logo/logo-embedded-chat-avatar.png`}
|
||||
src={`${WEB_PREFIX}/logo/logo-embedded-chat-avatar.png`}
|
||||
className={`block h-10 w-10 ${className}`}
|
||||
alt='logo'
|
||||
/>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import classNames from '@/utils/classnames'
|
||||
import type { FC } from 'react'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { WEB_PREFIX } from '@/config'
|
||||
|
||||
type LogoEmbeddedChatHeaderProps = {
|
||||
className?: string
|
||||
@ -14,7 +14,7 @@ const LogoEmbeddedChatHeader: FC<LogoEmbeddedChatHeaderProps> = ({
|
||||
<source media="(resolution: 2x)" srcSet='/logo/logo-embedded-chat-header@2x.png' />
|
||||
<source media="(resolution: 3x)" srcSet='/logo/logo-embedded-chat-header@3x.png' />
|
||||
<img
|
||||
src={`${basePath}/logo/logo-embedded-chat-header.png`}
|
||||
src={`${WEB_PREFIX}/logo/logo-embedded-chat-header.png`}
|
||||
alt='logo'
|
||||
className={classNames('block h-6 w-auto', className)}
|
||||
/>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { WEB_PREFIX } from '@/config'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
|
||||
@ -13,7 +13,7 @@ const LogoSite: FC<LogoSiteProps> = ({
|
||||
}) => {
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
|
||||
let src = `${basePath}/logo/logo.png`
|
||||
let src = `${WEB_PREFIX}/logo/logo.png`
|
||||
if (systemFeatures.branding.enabled)
|
||||
src = systemFeatures.branding.workspace_logo
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ const MarkdownButton = ({ node }: any) => {
|
||||
return <Button
|
||||
variant={variant}
|
||||
size={size}
|
||||
className={cn('!h-8 select-none !px-3')}
|
||||
className={cn('!h-auto min-h-8 select-none whitespace-normal !px-3')}
|
||||
onClick={() => {
|
||||
if (is_valid_url(link)) {
|
||||
window.open(link, '_blank')
|
||||
|
||||
@ -41,9 +41,10 @@ const useThinkTimer = (children: any) => {
|
||||
const timerRef = useRef<NodeJS.Timeout>()
|
||||
|
||||
useEffect(() => {
|
||||
if (isComplete) return
|
||||
|
||||
timerRef.current = setInterval(() => {
|
||||
if (!isComplete)
|
||||
setElapsedTime(Math.floor((Date.now() - startTime) / 100) / 10)
|
||||
setElapsedTime(Math.floor((Date.now() - startTime) / 100) / 10)
|
||||
}, 100)
|
||||
|
||||
return () => {
|
||||
@ -53,11 +54,8 @@ const useThinkTimer = (children: any) => {
|
||||
}, [startTime, isComplete])
|
||||
|
||||
useEffect(() => {
|
||||
if (hasEndThink(children)) {
|
||||
if (hasEndThink(children))
|
||||
setIsComplete(true)
|
||||
if (timerRef.current)
|
||||
clearInterval(timerRef.current)
|
||||
}
|
||||
}, [children])
|
||||
|
||||
return { elapsedTime, isComplete }
|
||||
|
||||
@ -128,13 +128,19 @@ const CodeBlock: any = memo(({ inline, className, children = '', ...props }: any
|
||||
const language = match?.[1]
|
||||
const languageShowName = getCorrectCapitalizationLanguageName(language || '')
|
||||
const chartData = useMemo(() => {
|
||||
const str = String(children).replace(/\n$/, '')
|
||||
if (language === 'echarts') {
|
||||
try {
|
||||
return JSON.parse(String(children).replace(/\n$/, ''))
|
||||
return JSON.parse(str)
|
||||
}
|
||||
catch { }
|
||||
try {
|
||||
// eslint-disable-next-line no-new-func, sonarjs/code-eval
|
||||
return new Function(`return ${str}`)()
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return JSON.parse('{"title":{"text":"ECharts error - Wrong JSON format."}}')
|
||||
return JSON.parse('{"title":{"text":"ECharts error - Wrong option."}}')
|
||||
}, [language, children])
|
||||
|
||||
const renderCodeContent = useMemo(() => {
|
||||
|
||||
@ -2,47 +2,55 @@
|
||||
|
||||
@layer components {
|
||||
.premium-badge {
|
||||
@apply inline-flex justify-center items-center rounded-md border box-border border-white/95 text-white
|
||||
@apply shrink-0 relative inline-flex justify-center items-center rounded-md box-border border border-transparent text-white shadow-xs hover:shadow-lg bg-origin-border overflow-hidden;
|
||||
background-clip: padding-box, border-box;
|
||||
}
|
||||
.allowHover {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
|
||||
/* m is for the regular button */
|
||||
.premium-badge-m {
|
||||
@apply border shadow-lg !p-1 h-6 w-auto
|
||||
@apply !p-1 h-6 w-auto
|
||||
}
|
||||
|
||||
.premium-badge-s {
|
||||
@apply border-[0.5px] shadow-xs !px-1 !py-[3px] h-[18px] w-auto
|
||||
@apply border-[0.5px] !px-1 !py-[3px] h-[18px] w-auto
|
||||
}
|
||||
|
||||
.premium-badge-blue {
|
||||
@apply bg-gradient-to-r from-[#5289ffe6] to-[#155aefe6] bg-util-colors-blue-blue-200
|
||||
@apply bg-util-colors-blue-blue-200;
|
||||
background-image: linear-gradient(90deg, #5289ffe6 0%, #155aefe6 100%), linear-gradient(135deg, var(--color-premium-badge-border-highlight-color) 0%, #155aef 100%);
|
||||
}
|
||||
.premium-badge-blue.allowHover:hover {
|
||||
@apply bg-util-colors-blue-blue-300;
|
||||
background-image: linear-gradient(90deg, #296dffe6 0%, #004aebe6 100%), linear-gradient(135deg, var(--color-premium-badge-border-highlight-color) 0%, #00329e 100%);
|
||||
}
|
||||
|
||||
.premium-badge-indigo {
|
||||
@apply bg-gradient-to-r from-[#8098f9e6] to-[#444ce7e6] bg-util-colors-indigo-indigo-200
|
||||
@apply bg-util-colors-indigo-indigo-200;
|
||||
background-image: linear-gradient(90deg, #8098f9e6 0%, #444ce7e6 100%), linear-gradient(135deg, var(--color-premium-badge-border-highlight-color) 0%, #6172f3 100%);
|
||||
}
|
||||
.premium-badge-indigo.allowHover:hover {
|
||||
@apply bg-util-colors-indigo-indigo-300;
|
||||
background-image: linear-gradient(90deg, #6172f3e6 0%, #2d31a6e6 100%), linear-gradient(135deg, var(--color-premium-badge-border-highlight-color) 0%, #2d31a6 100%);
|
||||
}
|
||||
|
||||
.premium-badge-gray {
|
||||
@apply bg-gradient-to-r from-[#98a2b2e6] to-[#676f83e6] bg-util-colors-gray-gray-200
|
||||
@apply bg-util-colors-gray-gray-200;
|
||||
background-image: linear-gradient(90deg, #98a2b2e6 0%, #676f83e6 100%), linear-gradient(135deg, var(--color-premium-badge-border-highlight-color) 0%, #676f83 100%);
|
||||
}
|
||||
.premium-badge-gray.allowHover:hover {
|
||||
@apply bg-util-colors-gray-gray-300;
|
||||
background-image: linear-gradient(90deg, #676f83e6 0%, #354052e6 100%), linear-gradient(135deg, var(--color-premium-badge-border-highlight-color) 0%, #354052 100%);
|
||||
}
|
||||
|
||||
.premium-badge-orange {
|
||||
@apply bg-gradient-to-r from-[#ff692ee6] to-[#e04f16e6] bg-util-colors-orange-orange-200
|
||||
@apply bg-util-colors-orange-orange-200;
|
||||
background-image: linear-gradient(90deg, #ff692ee6 0%, #e04f16e6 100%), linear-gradient(135deg, var(--color-premium-badge-border-highlight-color) 0%, #e62e05 100%);
|
||||
}
|
||||
|
||||
.premium-badge-blue.allowHover:hover {
|
||||
@apply bg-gradient-to-r from-[#296dffe6] to-[#004aebe6] bg-util-colors-blue-blue-300 cursor-pointer
|
||||
}
|
||||
|
||||
.premium-badge-indigo.allowHover:hover {
|
||||
@apply bg-gradient-to-r from-[#6172f3e6] to-[#2d31a6e6] bg-util-colors-indigo-indigo-300 cursor-pointer
|
||||
}
|
||||
|
||||
.premium-badge-gray.allowHover:hover {
|
||||
@apply bg-gradient-to-r from-[#676f83e6] to-[#354052e6] bg-util-colors-gray-gray-300 cursor-pointer
|
||||
}
|
||||
|
||||
.premium-badge-orange.allowHover:hover {
|
||||
@apply bg-gradient-to-r from-[#ff4405e6] to-[#b93815e6] bg-util-colors-orange-orange-300 cursor-pointer
|
||||
@apply bg-util-colors-orange-orange-300;
|
||||
background-image: linear-gradient(90deg, #ff4405e6 0%, #b93815e6 100%), linear-gradient(135deg, var(--color-premium-badge-border-highlight-color) 0%, #e62e05 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,13 +61,9 @@ const PremiumBadge: React.FC<PremiumBadgeProps> = ({
|
||||
{children}
|
||||
<Highlight
|
||||
className={classNames(
|
||||
'absolute top-0 opacity-50 hover:opacity-80',
|
||||
'absolute top-0 opacity-50 right-1/2 translate-x-[20%] transition-all duration-100 ease-out hover:opacity-80 hover:translate-x-[30%]',
|
||||
size === 's' ? 'h-[18px] w-12' : 'h-6 w-12',
|
||||
)}
|
||||
style={{
|
||||
right: '50%',
|
||||
transform: 'translateX(10%)',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
97
web/app/components/base/theme-selector.tsx
Normal file
97
web/app/components/base/theme-selector.tsx
Normal file
@ -0,0 +1,97 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import {
|
||||
RiCheckLine,
|
||||
RiComputerLine,
|
||||
RiMoonLine,
|
||||
RiSunLine,
|
||||
} from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useTheme } from 'next-themes'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
|
||||
export type Theme = 'light' | 'dark' | 'system'
|
||||
|
||||
export default function ThemeSelector() {
|
||||
const { t } = useTranslation()
|
||||
const { theme, setTheme } = useTheme()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const handleThemeChange = (newTheme: Theme) => {
|
||||
setTheme(newTheme)
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
const getCurrentIcon = () => {
|
||||
switch (theme) {
|
||||
case 'light': return <RiSunLine className='h-4 w-4 text-text-tertiary' />
|
||||
case 'dark': return <RiMoonLine className='h-4 w-4 text-text-tertiary' />
|
||||
default: return <RiComputerLine className='h-4 w-4 text-text-tertiary' />
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
placement='bottom-end'
|
||||
offset={{ mainAxis: 6 }}
|
||||
>
|
||||
<PortalToFollowElemTrigger
|
||||
onClick={() => setOpen(!open)}
|
||||
>
|
||||
<ActionButton
|
||||
className={`h-8 w-8 p-[6px] ${open && 'bg-state-base-hover'}`}
|
||||
>
|
||||
{getCurrentIcon()}
|
||||
</ActionButton>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[1000]'>
|
||||
<div className='flex w-[144px] flex-col items-start rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'>
|
||||
<button
|
||||
className='flex w-full items-center gap-1 rounded-lg px-2 py-1.5 text-text-secondary hover:bg-state-base-hover'
|
||||
onClick={() => handleThemeChange('light')}
|
||||
>
|
||||
<RiSunLine className='h-4 w-4 text-text-tertiary' />
|
||||
<div className='flex grow items-center justify-start px-1'>
|
||||
<span className='system-md-regular'>{t('common.theme.light')}</span>
|
||||
</div>
|
||||
{theme === 'light' && <div className='flex h-4 w-4 shrink-0 items-center justify-center'>
|
||||
<RiCheckLine className='h-4 w-4 text-text-accent' />
|
||||
</div>}
|
||||
</button>
|
||||
<button
|
||||
className='flex w-full items-center gap-1 rounded-lg px-2 py-1.5 text-text-secondary hover:bg-state-base-hover'
|
||||
onClick={() => handleThemeChange('dark')}
|
||||
>
|
||||
<RiMoonLine className='h-4 w-4 text-text-tertiary' />
|
||||
<div className='flex grow items-center justify-start px-1'>
|
||||
<span className='system-md-regular'>{t('common.theme.dark')}</span>
|
||||
</div>
|
||||
{theme === 'dark' && <div className='flex h-4 w-4 shrink-0 items-center justify-center'>
|
||||
<RiCheckLine className='h-4 w-4 text-text-accent' />
|
||||
</div>}
|
||||
</button>
|
||||
<button
|
||||
className='flex w-full items-center gap-1 rounded-lg px-2 py-1.5 text-text-secondary hover:bg-state-base-hover'
|
||||
onClick={() => handleThemeChange('system')}
|
||||
>
|
||||
<RiComputerLine className='h-4 w-4 text-text-tertiary' />
|
||||
<div className='flex grow items-center justify-start px-1'>
|
||||
<span className='system-md-regular'>{t('common.theme.auto')}</span>
|
||||
</div>
|
||||
{theme === 'system' && <div className='flex h-4 w-4 shrink-0 items-center justify-center'>
|
||||
<RiCheckLine className='h-4 w-4 text-text-accent' />
|
||||
</div>}
|
||||
</button>
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
)
|
||||
}
|
||||
58
web/app/components/base/theme-switcher.tsx
Normal file
58
web/app/components/base/theme-switcher.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
'use client'
|
||||
import {
|
||||
RiComputerLine,
|
||||
RiMoonLine,
|
||||
RiSunLine,
|
||||
} from '@remixicon/react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
export type Theme = 'light' | 'dark' | 'system'
|
||||
|
||||
export default function ThemeSwitcher() {
|
||||
const { theme, setTheme } = useTheme()
|
||||
|
||||
const handleThemeChange = (newTheme: Theme) => {
|
||||
setTheme(newTheme)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex items-center rounded-[10px] bg-components-segmented-control-bg-normal p-0.5'>
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-lg px-2 py-1 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||
theme === 'system' && 'bg-components-segmented-control-item-active-bg text-text-accent-light-mode-only shadow-sm hover:bg-components-segmented-control-item-active-bg hover:text-text-accent-light-mode-only',
|
||||
)}
|
||||
onClick={() => handleThemeChange('system')}
|
||||
>
|
||||
<div className='p-0.5'>
|
||||
<RiComputerLine className='h-4 w-4' />
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn('h-[14px] w-px bg-transparent', theme === 'dark' && 'bg-divider-regular')}></div>
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-lg px-2 py-1 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||
theme === 'light' && 'bg-components-segmented-control-item-active-bg text-text-accent-light-mode-only shadow-sm hover:bg-components-segmented-control-item-active-bg hover:text-text-accent-light-mode-only',
|
||||
)}
|
||||
onClick={() => handleThemeChange('light')}
|
||||
>
|
||||
<div className='p-0.5'>
|
||||
<RiSunLine className='h-4 w-4' />
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn('h-[14px] w-px bg-transparent', theme === 'system' && 'bg-divider-regular')}></div>
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-lg px-2 py-1 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||
theme === 'dark' && 'bg-components-segmented-control-item-active-bg text-text-accent-light-mode-only shadow-sm hover:bg-components-segmented-control-item-active-bg hover:text-text-accent-light-mode-only',
|
||||
)}
|
||||
onClick={() => handleThemeChange('dark')}
|
||||
>
|
||||
<div className='p-0.5'>
|
||||
<RiMoonLine className='h-4 w-4' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -2,7 +2,7 @@ import React, { type FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiLineHeight } from '@remixicon/react'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { Collapse } from '@/app/components/base/icons/src/public/knowledge'
|
||||
import { Collapse } from '@/app/components/base/icons/src/vender/line/editor'
|
||||
|
||||
type DisplayToggleProps = {
|
||||
isCollapsed: boolean
|
||||
|
||||
@ -264,8 +264,8 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
|
||||
target='_blank'
|
||||
href={
|
||||
locale === LanguagesSupported[1]
|
||||
? 'https://docs.dify.ai/v/zh-hans/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
: 'https://docs.dify.ai/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
? 'https://docs.dify.ai/zh-hans/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
: 'https://docs.dify.ai/en/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
}
|
||||
>
|
||||
<span>{t('datasetDocuments.list.learnMore')}</span>
|
||||
|
||||
@ -16,12 +16,12 @@ const InfoPanel = () => {
|
||||
</span>
|
||||
<span className='system-sm-regular text-text-tertiary'>
|
||||
{t('dataset.connectDatasetIntro.content.front')}
|
||||
<a className='system-sm-regular ml-1 text-text-accent' href='https://docs.dify.ai/guides/knowledge-base/external-knowledge-api-documentation' target='_blank' rel="noopener noreferrer">
|
||||
<a className='system-sm-regular ml-1 text-text-accent' href='https://docs.dify.ai/en/guides/knowledge-base/external-knowledge-api' target='_blank' rel="noopener noreferrer">
|
||||
{t('dataset.connectDatasetIntro.content.link')}
|
||||
</a>
|
||||
{t('dataset.connectDatasetIntro.content.end')}
|
||||
</span>
|
||||
<a className='system-sm-regular self-stretch text-text-accent' href='https://docs.dify.ai/guides/knowledge-base/connect-external-knowledge' target='_blank' rel="noopener noreferrer">
|
||||
<a className='system-sm-regular self-stretch text-text-accent' href='https://docs.dify.ai/en/guides/knowledge-base/connect-external-knowledge-base' target='_blank' rel="noopener noreferrer">
|
||||
{t('dataset.connectDatasetIntro.learnMore')}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@ -59,7 +59,7 @@ const ExternalKnowledgeBaseCreate: React.FC<ExternalKnowledgeBaseCreateProps> =
|
||||
<span>{t('dataset.connectHelper.helper1')}</span>
|
||||
<span className='system-sm-medium text-text-secondary'>{t('dataset.connectHelper.helper2')}</span>
|
||||
<span>{t('dataset.connectHelper.helper3')}</span>
|
||||
<a className='system-sm-regular self-stretch text-text-accent' href='https://docs.dify.ai/guides/knowledge-base/connect-external-knowledge' target='_blank' rel="noopener noreferrer">
|
||||
<a className='system-sm-regular self-stretch text-text-accent' href='https://docs.dify.ai/en/guides/knowledge-base/connect-external-knowledge-base' target='_blank' rel="noopener noreferrer">
|
||||
{t('dataset.connectHelper.helper4')}
|
||||
</a>
|
||||
<span>{t('dataset.connectHelper.helper5')} </span>
|
||||
|
||||
@ -36,7 +36,7 @@ const CreateMetadataModal: FC<Props> = ({
|
||||
{trigger}
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[1000]'>
|
||||
<CreateContent {...createContentProps} onClose={() => setOpen(false)} />
|
||||
<CreateContent {...createContentProps} onClose={() => setOpen(false)} onBack={() => setOpen(false)} />
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem >
|
||||
|
||||
|
||||
@ -71,6 +71,7 @@ const SelectMetadataModal: FC<Props> = ({
|
||||
onSave={handleSave}
|
||||
hasBack
|
||||
onBack={() => setStep(Step.select)}
|
||||
onClose={() => setStep(Step.select)}
|
||||
/>
|
||||
)}
|
||||
</PortalToFollowElemContent>
|
||||
|
||||
@ -121,7 +121,7 @@ const Doc = ({ appDetail }: IDocProps) => {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<article className={cn('prose-xl prose', theme === Theme.dark && 'dark:prose-invert')} >
|
||||
<article className={cn('prose-xl prose', theme === Theme.dark && 'prose-invert')} >
|
||||
{(appDetail?.mode === 'chat' || appDetail?.mode === 'agent-chat') && (
|
||||
(() => {
|
||||
switch (locale) {
|
||||
|
||||
@ -2,20 +2,18 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import { t } from 'i18next'
|
||||
import s from './style.module.css'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import CopyFeedback from '@/app/components/base/copy-feedback'
|
||||
|
||||
type IInputCopyProps = {
|
||||
value?: string
|
||||
className?: string
|
||||
readOnly?: boolean
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
const InputCopy = ({
|
||||
value = '',
|
||||
className,
|
||||
readOnly = true,
|
||||
children,
|
||||
}: IInputCopyProps) => {
|
||||
const [isCopied, setIsCopied] = useState(false)
|
||||
@ -45,23 +43,12 @@ const InputCopy = ({
|
||||
popupContent={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
|
||||
position='bottom'
|
||||
>
|
||||
{value}
|
||||
<span className='text-text-secondary'>{value}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-4 shrink-0 border bg-divider-regular" />
|
||||
<Tooltip
|
||||
popupContent={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
|
||||
position='bottom'
|
||||
>
|
||||
<div className="shrink-0 px-0.5">
|
||||
<div className={`box-border flex h-[30px] w-[30px] cursor-pointer items-center justify-center rounded-lg hover:bg-state-base-hover ${s.copyIcon} ${isCopied ? s.copied : ''}`} onClick={() => {
|
||||
copy(value)
|
||||
setIsCopied(true)
|
||||
}}>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div className="h-4 w-px shrink-0 bg-divider-regular" />
|
||||
<div className='mx-1'><CopyFeedback content={value} /></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -383,6 +383,69 @@ The text generation application offers non-session support and is ideal for tran
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/app/feedbacks'
|
||||
method='GET'
|
||||
title='Get feedbacks of application'
|
||||
name='#app-feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Get application's feedbacks.
|
||||
|
||||
### Query
|
||||
<Properties>
|
||||
<Property name='page' type='string' key='page'>
|
||||
(optional)pagination,default:1
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Properties>
|
||||
<Property name='limit' type='string' key='limit'>
|
||||
(optional) records per page default:20
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Response
|
||||
- `data` (List) return apps feedback list.
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/app/feedbacks" targetCode={`curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "8c0fbed8-e2f9-49ff-9f0e-15a35bdd0e25",
|
||||
"app_id": "f252d396-fe48-450e-94ec-e184218e7346",
|
||||
"conversation_id": "2397604b-9deb-430e-b285-4726e51fd62d",
|
||||
"message_id": "709c0b0f-0a96-4a4e-91a4-ec0889937b11",
|
||||
"rating": "like",
|
||||
"content": "message feedback information-3",
|
||||
"from_source": "user",
|
||||
"from_end_user_id": "74286412-9a1a-42c1-929c-01edb1d381d5",
|
||||
"from_account_id": null,
|
||||
"created_at": "2025-04-24T09:24:38",
|
||||
"updated_at": "2025-04-24T09:24:38"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/text-to-audio'
|
||||
method='POST'
|
||||
@ -584,3 +647,62 @@ The text generation application offers non-session support and is ideal for tran
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='Get Application WebApp Settings'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Used to get the WebApp settings of the application.
|
||||
### Response
|
||||
- `title` (string) WebApp name
|
||||
- `chat_color_theme` (string) Chat color theme, in hex format
|
||||
- `chat_color_theme_inverted` (bool) Whether the chat color theme is inverted
|
||||
- `icon_type` (string) Icon type, `emoji` - emoji, `image` - picture
|
||||
- `icon` (string) Icon. If it's `emoji` type, it's an emoji symbol; if it's `image` type, it's an image URL.
|
||||
- `icon_background` (string) Background color in hex format
|
||||
- `icon_url` (string) Icon URL
|
||||
- `description` (string) Description
|
||||
- `copyright` (string) Copyright information
|
||||
- `privacy_policy` (string) Privacy policy link
|
||||
- `custom_disclaimer` (string) Custom disclaimer
|
||||
- `default_language` (string) Default language
|
||||
- `show_workflow_steps` (bool) Whether to show workflow details
|
||||
- `use_icon_as_answer_icon` (bool) Whether to replace 🤖 in chat with the WebApp icon
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="POST" label="/meta" targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/site' \
|
||||
-H 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"chat_color_theme": "#ff4a4a",
|
||||
"chat_color_theme_inverted": false,
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
"use_icon_as_answer_icon": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||
|
||||
@ -381,6 +381,69 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/app/feedbacks'
|
||||
method='GET'
|
||||
title='アプリのメッセージの「いいね」とフィードバックを取得'
|
||||
name='#app-feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
アプリのエンドユーザーからのフィードバックや「いいね」を取得します。
|
||||
|
||||
### クエリ
|
||||
<Properties>
|
||||
<Property name='page' type='string' key='page'>
|
||||
(任意)ページ番号。デフォルト値:1
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Properties>
|
||||
<Property name='limit' type='string' key='limit'>
|
||||
(任意)1ページあたりの件数。デフォルト値:20
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### レスポンス
|
||||
- `data` (リスト) このアプリの「いいね」とフィードバックの一覧を返します。
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/app/feedbacks" targetCode={`curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "8c0fbed8-e2f9-49ff-9f0e-15a35bdd0e25",
|
||||
"app_id": "f252d396-fe48-450e-94ec-e184218e7346",
|
||||
"conversation_id": "2397604b-9deb-430e-b285-4726e51fd62d",
|
||||
"message_id": "709c0b0f-0a96-4a4e-91a4-ec0889937b11",
|
||||
"rating": "like",
|
||||
"content": "message feedback information-3",
|
||||
"from_source": "user",
|
||||
"from_end_user_id": "74286412-9a1a-42c1-929c-01edb1d381d5",
|
||||
"from_account_id": null,
|
||||
"created_at": "2025-04-24T09:24:38",
|
||||
"updated_at": "2025-04-24T09:24:38"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/text-to-audio'
|
||||
method='POST'
|
||||
@ -582,3 +645,62 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='アプリのWebApp設定を取得'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
アプリのWebApp設定を取得するために使用します。
|
||||
### レスポンス
|
||||
- `title` (string) WebApp名
|
||||
- `chat_color_theme` (string) チャットの色テーマ、16進数形式
|
||||
- `chat_color_theme_inverted` (bool) チャットの色テーマを反転するかどうか
|
||||
- `icon_type` (string) アイコンタイプ、`emoji`-絵文字、`image`-画像
|
||||
- `icon` (string) アイコン。`emoji`タイプの場合は絵文字、`image`タイプの場合は画像URL
|
||||
- `icon_background` (string) 16進数形式の背景色
|
||||
- `icon_url` (string) アイコンのURL
|
||||
- `description` (string) 説明
|
||||
- `copyright` (string) 著作権情報
|
||||
- `privacy_policy` (string) プライバシーポリシーのリンク
|
||||
- `custom_disclaimer` (string) カスタム免責事項
|
||||
- `default_language` (string) デフォルト言語
|
||||
- `show_workflow_steps` (bool) ワークフローの詳細を表示するかどうか
|
||||
- `use_icon_as_answer_icon` (bool) WebAppのアイコンをチャット内の🤖に置き換えるかどうか
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="POST" label="/meta" targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/site' \
|
||||
-H 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"chat_color_theme": "#ff4a4a",
|
||||
"chat_color_theme_inverted": false,
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
"use_icon_as_answer_icon": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||
|
||||
@ -355,6 +355,68 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
<Heading
|
||||
url='/app/feedbacks'
|
||||
method='GET'
|
||||
title='Get feedbacks of application'
|
||||
name='#app-feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Get application's feedbacks.
|
||||
|
||||
### Query
|
||||
<Properties>
|
||||
<Property name='page' type='string' key='page'>
|
||||
(optional)pagination,default:1
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Properties>
|
||||
<Property name='limit' type='string' key='limit'>
|
||||
(optional) records per page default:20
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Response
|
||||
- `data` (List) return apps feedback list.
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/app/feedbacks" targetCode={`curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "8c0fbed8-e2f9-49ff-9f0e-15a35bdd0e25",
|
||||
"app_id": "f252d396-fe48-450e-94ec-e184218e7346",
|
||||
"conversation_id": "2397604b-9deb-430e-b285-4726e51fd62d",
|
||||
"message_id": "709c0b0f-0a96-4a4e-91a4-ec0889937b11",
|
||||
"rating": "like",
|
||||
"content": "message feedback information-3",
|
||||
"from_source": "user",
|
||||
"from_end_user_id": "74286412-9a1a-42c1-929c-01edb1d381d5",
|
||||
"from_account_id": null,
|
||||
"created_at": "2025-04-24T09:24:38",
|
||||
"updated_at": "2025-04-24T09:24:38"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
@ -445,7 +507,6 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
@ -550,6 +611,64 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='获取应用 WebApp 设置'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
用于获取应用的 WebApp 设置
|
||||
### Response
|
||||
- `title` (string) WebApp 名称
|
||||
- `chat_color_theme` (string) 聊天颜色主题, hex 格式
|
||||
- `chat_color_theme_inverted` (bool) 聊天颜色主题是否反转
|
||||
- `icon_type` (string) 图标类型, `emoji`-表情, `image`-图片
|
||||
- `icon` (string) 图标, 如果是 `emoji` 类型, 则是 emoji 表情符号, 如果是 `image` 类型, 则是图片 URL
|
||||
- `icon_background` (string) hex 格式的背景色
|
||||
- `icon_url` (string) 图标 URL
|
||||
- `description` (string) 描述
|
||||
- `copyright` (string) 版权信息
|
||||
- `privacy_policy` (string) 隐私政策链接
|
||||
- `custom_disclaimer` (string) 自定义免责声明
|
||||
- `default_language` (string) 默认语言
|
||||
- `show_workflow_steps` (bool) 是否显示工作流详情
|
||||
- `use_icon_as_answer_icon` (bool) 是否使用 WebApp 图标替换聊天中的 🤖
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="POST" label="/meta" targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/site' \
|
||||
-H 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"chat_color_theme": "#ff4a4a",
|
||||
"chat_color_theme_inverted": false,
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
"use_icon_as_answer_icon": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||
|
||||
<Heading
|
||||
url='/apps/annotations'
|
||||
method='GET'
|
||||
@ -738,8 +857,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{"result": "success"}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
@ -487,6 +487,69 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/app/feedbacks'
|
||||
method='GET'
|
||||
title='Get feedbacks of application'
|
||||
name='#app-feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Get application's feedbacks.
|
||||
|
||||
### Query
|
||||
<Properties>
|
||||
<Property name='page' type='string' key='page'>
|
||||
(optional)pagination,default:1
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Properties>
|
||||
<Property name='limit' type='string' key='limit'>
|
||||
(optional) records per page default:20
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Response
|
||||
- `data` (List) return apps feedback list.
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/app/feedbacks" targetCode={`curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "8c0fbed8-e2f9-49ff-9f0e-15a35bdd0e25",
|
||||
"app_id": "f252d396-fe48-450e-94ec-e184218e7346",
|
||||
"conversation_id": "2397604b-9deb-430e-b285-4726e51fd62d",
|
||||
"message_id": "709c0b0f-0a96-4a4e-91a4-ec0889937b11",
|
||||
"rating": "like",
|
||||
"content": "message feedback information-3",
|
||||
"from_source": "user",
|
||||
"from_end_user_id": "74286412-9a1a-42c1-929c-01edb1d381d5",
|
||||
"from_account_id": null,
|
||||
"created_at": "2025-04-24T09:24:38",
|
||||
"updated_at": "2025-04-24T09:24:38"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/messages/{message_id}/suggested'
|
||||
method='GET'
|
||||
@ -765,10 +828,8 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
@ -1244,6 +1305,64 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='Get Application WebApp Settings'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Used to get the WebApp settings of the application.
|
||||
### Response
|
||||
- `title` (string) WebApp name
|
||||
- `chat_color_theme` (string) Chat color theme, in hex format
|
||||
- `chat_color_theme_inverted` (bool) Whether the chat color theme is inverted
|
||||
- `icon_type` (string) Icon type, `emoji` - emoji, `image` - picture
|
||||
- `icon` (string) Icon. If it's `emoji` type, it's an emoji symbol; if it's `image` type, it's an image URL
|
||||
- `icon_background` (string) Background color in hex format
|
||||
- `icon_url` (string) Icon URL
|
||||
- `description` (string) Description
|
||||
- `copyright` (string) Copyright information
|
||||
- `privacy_policy` (string) Privacy policy link
|
||||
- `custom_disclaimer` (string) Custom disclaimer
|
||||
- `default_language` (string) Default language
|
||||
- `show_workflow_steps` (bool) Whether to show workflow details
|
||||
- `use_icon_as_answer_icon` (bool) Whether to replace 🤖 in chat with the WebApp icon
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="POST" label="/meta" targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/site' \
|
||||
-H 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"chat_color_theme": "#ff4a4a",
|
||||
"chat_color_theme_inverted": false,
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
"use_icon_as_answer_icon": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||
|
||||
<Heading
|
||||
url='/apps/annotations'
|
||||
method='GET'
|
||||
@ -1432,8 +1551,8 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{"result": "success"}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
@ -487,6 +487,70 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/app/feedbacks'
|
||||
method='GET'
|
||||
title='アプリのメッセージの「いいね」とフィードバックを取得'
|
||||
name='#app-feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
アプリのエンドユーザーからのフィードバックや「いいね」を取得します。
|
||||
|
||||
### クエリ
|
||||
<Properties>
|
||||
<Property name='page' type='string' key='page'>
|
||||
(任意)ページ番号。デフォルト値:1
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Properties>
|
||||
<Property name='limit' type='string' key='limit'>
|
||||
(任意)1ページあたりの件数。デフォルト値:20
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### レスポンス
|
||||
- `data` (リスト) このアプリの「いいね」とフィードバックの一覧を返します。
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/app/feedbacks" targetCode={`curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "8c0fbed8-e2f9-49ff-9f0e-15a35bdd0e25",
|
||||
"app_id": "f252d396-fe48-450e-94ec-e184218e7346",
|
||||
"conversation_id": "2397604b-9deb-430e-b285-4726e51fd62d",
|
||||
"message_id": "709c0b0f-0a96-4a4e-91a4-ec0889937b11",
|
||||
"rating": "like",
|
||||
"content": "message feedback information-3",
|
||||
"from_source": "user",
|
||||
"from_end_user_id": "74286412-9a1a-42c1-929c-01edb1d381d5",
|
||||
"from_account_id": null,
|
||||
"created_at": "2025-04-24T09:24:38",
|
||||
"updated_at": "2025-04-24T09:24:38"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
|
||||
<Heading
|
||||
url='/messages/{message_id}/suggested'
|
||||
method='GET'
|
||||
@ -764,10 +828,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="応答">
|
||||
```json {{ title: '応答' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: '応答' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
@ -1241,3 +1303,63 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='アプリのWebApp設定を取得'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
アプリのWebApp設定を取得するために使用します。
|
||||
### 応答
|
||||
- `title` (string) WebApp名
|
||||
- `chat_color_theme` (string) チャットの色テーマ、16進数形式
|
||||
- `chat_color_theme_inverted` (bool) チャットの色テーマを反転するかどうか
|
||||
- `icon_type` (string) アイコンタイプ、`emoji`-絵文字、`image`-画像
|
||||
- `icon` (string) アイコン。`emoji`タイプの場合は絵文字、`image`タイプの場合は画像URL
|
||||
- `icon_background` (string) 16進数形式の背景色
|
||||
- `icon_url` (string) アイコンのURL
|
||||
- `description` (string) 説明
|
||||
- `copyright` (string) 著作権情報
|
||||
- `privacy_policy` (string) プライバシーポリシーのリンク
|
||||
- `custom_disclaimer` (string) カスタム免責事項
|
||||
- `default_language` (string) デフォルト言語
|
||||
- `show_workflow_steps` (bool) ワークフローの詳細を表示するかどうか
|
||||
- `use_icon_as_answer_icon` (bool) WebAppのアイコンをチャット内の🤖に置き換えるかどうか
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="POST" label="/meta" targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/site' \
|
||||
-H 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"chat_color_theme": "#ff4a4a",
|
||||
"chat_color_theme_inverted": false,
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
"use_icon_as_answer_icon": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||
|
||||
@ -493,6 +493,71 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/app/feedbacks'
|
||||
method='GET'
|
||||
title='获取APP的消息点赞和反馈'
|
||||
name='#app-feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
获取应用的终端用户反馈、点赞。
|
||||
|
||||
### Query
|
||||
<Properties>
|
||||
<Property name='page' type='string' key='page'>
|
||||
(选填)分页,默认值:1
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Properties>
|
||||
<Property name='limit' type='string' key='limit'>
|
||||
(选填)每页数量,默认值:20
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Response
|
||||
- `data` (List) 返回该APP的点赞、反馈列表。
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/app/feedbacks" targetCode={`curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "8c0fbed8-e2f9-49ff-9f0e-15a35bdd0e25",
|
||||
"app_id": "f252d396-fe48-450e-94ec-e184218e7346",
|
||||
"conversation_id": "2397604b-9deb-430e-b285-4726e51fd62d",
|
||||
"message_id": "709c0b0f-0a96-4a4e-91a4-ec0889937b11",
|
||||
"rating": "like",
|
||||
"content": "message feedback information-3",
|
||||
"from_source": "user",
|
||||
"from_end_user_id": "74286412-9a1a-42c1-929c-01edb1d381d5",
|
||||
"from_account_id": null,
|
||||
"created_at": "2025-04-24T09:24:38",
|
||||
"updated_at": "2025-04-24T09:24:38"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/messages/{message_id}/suggested'
|
||||
method='GET'
|
||||
@ -799,10 +864,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
@ -1266,7 +1329,63 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
</Row>
|
||||
---
|
||||
|
||||
---
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='获取应用 WebApp 设置'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
用于获取应用的 WebApp 设置
|
||||
### Response
|
||||
- `title` (string) WebApp 名称
|
||||
- `chat_color_theme` (string) 聊天颜色主题, hex 格式
|
||||
- `chat_color_theme_inverted` (bool) 聊天颜色主题是否反转
|
||||
- `icon_type` (string) 图标类型, `emoji`-表情, `image`-图片
|
||||
- `icon` (string) 图标, 如果是 `emoji` 类型, 则是 emoji 表情符号, 如果是 `image` 类型, 则是图片 URL
|
||||
- `icon_background` (string) hex 格式的背景色
|
||||
- `icon_url` (string) 图标 URL
|
||||
- `description` (string) 描述
|
||||
- `copyright` (string) 版权信息
|
||||
- `privacy_policy` (string) 隐私政策链接
|
||||
- `custom_disclaimer` (string) 自定义免责声明
|
||||
- `default_language` (string) 默认语言
|
||||
- `show_workflow_steps` (bool) 是否显示工作流详情
|
||||
- `use_icon_as_answer_icon` (bool) 是否使用 WebApp 图标替换聊天中的 🤖
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="POST" label="/meta" targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/site' \
|
||||
-H 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"chat_color_theme": "#ff4a4a",
|
||||
"chat_color_theme_inverted": false,
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
"use_icon_as_answer_icon": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||
|
||||
<Heading
|
||||
url='/apps/annotations'
|
||||
@ -1456,8 +1575,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{"result": "success"}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
@ -450,6 +450,69 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/app/feedbacks'
|
||||
method='GET'
|
||||
title='Get feedbacks of application'
|
||||
name='#app-feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Get application's feedbacks.
|
||||
|
||||
### Query
|
||||
<Properties>
|
||||
<Property name='page' type='string' key='page'>
|
||||
(optional)pagination,default:1
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Properties>
|
||||
<Property name='limit' type='string' key='limit'>
|
||||
(optional) records per page default:20
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Response
|
||||
- `data` (List) return apps feedback list.
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/app/feedbacks" targetCode={`curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "8c0fbed8-e2f9-49ff-9f0e-15a35bdd0e25",
|
||||
"app_id": "f252d396-fe48-450e-94ec-e184218e7346",
|
||||
"conversation_id": "2397604b-9deb-430e-b285-4726e51fd62d",
|
||||
"message_id": "709c0b0f-0a96-4a4e-91a4-ec0889937b11",
|
||||
"rating": "like",
|
||||
"content": "message feedback information-3",
|
||||
"from_source": "user",
|
||||
"from_end_user_id": "74286412-9a1a-42c1-929c-01edb1d381d5",
|
||||
"from_account_id": null,
|
||||
"created_at": "2025-04-24T09:24:38",
|
||||
"updated_at": "2025-04-24T09:24:38"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/messages/{message_id}/suggested'
|
||||
method='GET'
|
||||
@ -798,10 +861,8 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
@ -1280,6 +1341,64 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='Get Application WebApp Settings'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Used to get the WebApp settings of the application.
|
||||
### Response
|
||||
- `title` (string) WebApp name
|
||||
- `chat_color_theme` (string) Chat color theme, in hex format
|
||||
- `chat_color_theme_inverted` (bool) Whether the chat color theme is inverted
|
||||
- `icon_type` (string) Icon type, `emoji` - emoji, `image` - picture
|
||||
- `icon` (string) Icon. If it's `emoji` type, it's an emoji symbol; if it's `image` type, it's an image URL
|
||||
- `icon_background` (string) Background color in hex format
|
||||
- `icon_url` (string) Icon URL
|
||||
- `description` (string) Description
|
||||
- `copyright` (string) Copyright information
|
||||
- `privacy_policy` (string) Privacy policy link
|
||||
- `custom_disclaimer` (string) Custom disclaimer
|
||||
- `default_language` (string) Default language
|
||||
- `show_workflow_steps` (bool) Whether to show workflow details
|
||||
- `use_icon_as_answer_icon` (bool) Whether to replace 🤖 in chat with the WebApp icon
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="POST" label="/meta" targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/site' \
|
||||
-H 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"chat_color_theme": "#ff4a4a",
|
||||
"chat_color_theme_inverted": false,
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
"use_icon_as_answer_icon": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||
|
||||
<Heading
|
||||
url='/apps/annotations'
|
||||
method='GET'
|
||||
@ -1472,8 +1591,8 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{"result": "success"}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
@ -450,6 +450,70 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/app/feedbacks'
|
||||
method='GET'
|
||||
title='アプリのメッセージの「いいね」とフィードバックを取得'
|
||||
name='#app-feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
アプリのエンドユーザーからのフィードバックや「いいね」を取得します。
|
||||
|
||||
### クエリ
|
||||
<Properties>
|
||||
<Property name='page' type='string' key='page'>
|
||||
(任意)ページ番号。デフォルト値:1
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Properties>
|
||||
<Property name='limit' type='string' key='limit'>
|
||||
(任意)1ページあたりの件数。デフォルト値:20
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### レスポンス
|
||||
- `data` (リスト) このアプリの「いいね」とフィードバックの一覧を返します。
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/app/feedbacks" targetCode={`curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "8c0fbed8-e2f9-49ff-9f0e-15a35bdd0e25",
|
||||
"app_id": "f252d396-fe48-450e-94ec-e184218e7346",
|
||||
"conversation_id": "2397604b-9deb-430e-b285-4726e51fd62d",
|
||||
"message_id": "709c0b0f-0a96-4a4e-91a4-ec0889937b11",
|
||||
"rating": "like",
|
||||
"content": "message feedback information-3",
|
||||
"from_source": "user",
|
||||
"from_end_user_id": "74286412-9a1a-42c1-929c-01edb1d381d5",
|
||||
"from_account_id": null,
|
||||
"created_at": "2025-04-24T09:24:38",
|
||||
"updated_at": "2025-04-24T09:24:38"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
|
||||
<Heading
|
||||
url='/messages/{message_id}/suggested'
|
||||
method='GET'
|
||||
@ -797,10 +861,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="応答">
|
||||
```json {{ title: '応答' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: '応答' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
@ -1268,3 +1330,63 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='アプリのWebApp設定を取得'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
アプリのWebApp設定を取得するために使用します。
|
||||
### 応答
|
||||
- `title` (string) WebApp名
|
||||
- `chat_color_theme` (string) チャットの色テーマ、16進数形式
|
||||
- `chat_color_theme_inverted` (bool) チャットの色テーマを反転するかどうか
|
||||
- `icon_type` (string) アイコンタイプ、`emoji`-絵文字、`image`-画像
|
||||
- `icon` (string) アイコン。`emoji`タイプの場合は絵文字、`image`タイプの場合は画像URL
|
||||
- `icon_background` (string) 16進数形式の背景色
|
||||
- `icon_url` (string) アイコンのURL
|
||||
- `description` (string) 説明
|
||||
- `copyright` (string) 著作権情報
|
||||
- `privacy_policy` (string) プライバシーポリシーのリンク
|
||||
- `custom_disclaimer` (string) カスタム免責事項
|
||||
- `default_language` (string) デフォルト言語
|
||||
- `show_workflow_steps` (bool) ワークフローの詳細を表示するかどうか
|
||||
- `use_icon_as_answer_icon` (bool) WebAppのアイコンをチャット内の🤖に置き換えるかどうか
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="POST" label="/meta" targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/site' \
|
||||
-H 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"chat_color_theme": "#ff4a4a",
|
||||
"chat_color_theme_inverted": false,
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
"use_icon_as_answer_icon": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||
|
||||
@ -464,6 +464,69 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/app/feedbacks'
|
||||
method='GET'
|
||||
title='获取APP的消息点赞和反馈'
|
||||
name='#app-feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
获取应用的终端用户反馈、点赞。
|
||||
|
||||
### Query
|
||||
<Properties>
|
||||
<Property name='page' type='string' key='page'>
|
||||
(选填)分页,默认值:1
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Properties>
|
||||
<Property name='limit' type='string' key='limit'>
|
||||
(选填)每页数量,默认值:20
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Response
|
||||
- `data` (List) 返回该APP的点赞、反馈列表。
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/app/feedbacks" targetCode={`curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "8c0fbed8-e2f9-49ff-9f0e-15a35bdd0e25",
|
||||
"app_id": "f252d396-fe48-450e-94ec-e184218e7346",
|
||||
"conversation_id": "2397604b-9deb-430e-b285-4726e51fd62d",
|
||||
"message_id": "709c0b0f-0a96-4a4e-91a4-ec0889937b11",
|
||||
"rating": "like",
|
||||
"content": "message feedback information-3",
|
||||
"from_source": "user",
|
||||
"from_end_user_id": "74286412-9a1a-42c1-929c-01edb1d381d5",
|
||||
"from_account_id": null,
|
||||
"created_at": "2025-04-24T09:24:38",
|
||||
"updated_at": "2025-04-24T09:24:38"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/messages/{message_id}/suggested'
|
||||
method='GET'
|
||||
@ -811,10 +874,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```text {{ title: 'Response' }}
|
||||
204 No Content
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
@ -1271,3 +1332,63 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='获取应用 WebApp 设置'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
用于获取应用的 WebApp 设置
|
||||
### Response
|
||||
- `title` (string) WebApp 名称
|
||||
- `chat_color_theme` (string) 聊天颜色主题, hex 格式
|
||||
- `chat_color_theme_inverted` (bool) 聊天颜色主题是否反转
|
||||
- `icon_type` (string) 图标类型, `emoji`-表情, `image`-图片
|
||||
- `icon` (string) 图标, 如果是 `emoji` 类型, 则是 emoji 表情符号, 如果是 `image` 类型, 则是图片 URL
|
||||
- `icon_background` (string) hex 格式的背景色
|
||||
- `icon_url` (string) 图标 URL
|
||||
- `description` (string) 描述
|
||||
- `copyright` (string) 版权信息
|
||||
- `privacy_policy` (string) 隐私政策链接
|
||||
- `custom_disclaimer` (string) 自定义免责声明
|
||||
- `default_language` (string) 默认语言
|
||||
- `show_workflow_steps` (bool) 是否显示工作流详情
|
||||
- `use_icon_as_answer_icon` (bool) 是否使用 WebApp 图标替换聊天中的 🤖
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="POST" label="/meta" targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/site' \
|
||||
-H 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"chat_color_theme": "#ff4a4a",
|
||||
"chat_color_theme_inverted": false,
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
"use_icon_as_answer_icon": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||
|
||||
@ -90,7 +90,7 @@ Workflow applications offers non-session support and is ideal for translation, a
|
||||
Each streaming chunk starts with `data:`, separated by two newline characters `\n\n`, as shown below:
|
||||
<CodeGroup>
|
||||
```streaming {{ title: 'Response' }}
|
||||
data: {"event": "message", "task_id": "900bbd43-dc0b-4383-a372-aa6e6c414227", "id": "663c5084-a254-4040-8ad3-51f2a3c1a77c", "answer": "Hi", "created_at": 1705398420}\n\n
|
||||
data: {"event": "text_chunk", "workflow_run_id": "b85e5fc5-751b-454d-b14e-dc5f240b0a31", "task_id": "bd029338-b068-4d34-a331-fc85478922c2", "data": {"text": "\u4e3a\u4e86", "from_variable_selector": ["1745912968134", "text"]}}\n\n
|
||||
```
|
||||
</CodeGroup>
|
||||
The structure of the streaming chunks varies depending on the `event`:
|
||||
@ -116,6 +116,13 @@ Workflow applications offers non-session support and is ideal for translation, a
|
||||
- `predecessor_node_id` (string) optional Prefix node ID, used for canvas display execution path
|
||||
- `inputs` (object) Contents of all preceding node variables used in the node
|
||||
- `created_at` (timestamp) timestamp of start, e.g., 1705395332
|
||||
- `event: text_chunk` Text fragment
|
||||
- `task_id` (string) Task ID, used for request tracking and the below Stop Generate API
|
||||
- `workflow_run_id` (string) Unique ID of workflow execution
|
||||
- `event` (string) fixed to `text_chunk`
|
||||
- `data` (object) detail
|
||||
- `text` (string) Text content
|
||||
- `from_variable_selector` (array) Text source path, helping developers understand which node and variable generated the text
|
||||
- `event: node_finished` node execution ends, success or failure in different states in the same event
|
||||
- `task_id` (string) Task ID, used for request tracking and the below Stop Generate API
|
||||
- `workflow_run_id` (string) Unique ID of workflow execution
|
||||
@ -730,3 +737,56 @@ Workflow applications offers non-session support and is ideal for translation, a
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='Get Application WebApp Settings'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Used to get the WebApp settings of the application.
|
||||
### Response
|
||||
- `title` (string) WebApp name
|
||||
- `icon_type` (string) Icon type, `emoji` - emoji, `image` - picture
|
||||
- `icon` (string) Icon. If it's `emoji` type, it's an emoji symbol; if it's `image` type, it's an image URL.
|
||||
- `icon_background` (string) Background color in hex format
|
||||
- `icon_url` (string) Icon URL
|
||||
- `description` (string) Description
|
||||
- `copyright` (string) Copyright information
|
||||
- `privacy_policy` (string) Privacy policy link
|
||||
- `custom_disclaimer` (string) Custom disclaimer
|
||||
- `default_language` (string) Default language
|
||||
- `show_workflow_steps` (bool) Whether to show workflow details
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="POST" label="/meta" targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/site' \
|
||||
-H 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||
|
||||
@ -93,7 +93,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
各ストリーミングチャンクは`data:`で始まり、2つの改行文字`\n\n`で区切られます。以下のように表示されます:
|
||||
<CodeGroup>
|
||||
```streaming {{ title: '応答' }}
|
||||
data: {"event": "message", "task_id": "900bbd43-dc0b-4383-a372-aa6e6c414227", "id": "663c5084-a254-4040-8ad3-51f2a3c1a77c", "answer": "Hi", "created_at": 1705398420}\n\n
|
||||
data: {"event": "text_chunk", "workflow_run_id": "b85e5fc5-751b-454d-b14e-dc5f240b0a31", "task_id": "bd029338-b068-4d34-a331-fc85478922c2", "data": {"text": "\u4e3a\u4e86", "from_variable_selector": ["1745912968134", "text"]}}\n\n
|
||||
```
|
||||
</CodeGroup>
|
||||
ストリーミングチャンクの構造は`event`に応じて異なります:
|
||||
@ -119,6 +119,13 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `predecessor_node_id` (string) オプションのプレフィックスノードID、キャンバス表示実行パスに使用
|
||||
- `inputs` (object) ノードで使用されるすべての前のノード変数の内容
|
||||
- `created_at` (timestamp) 開始のタイムスタンプ、例:1705395332
|
||||
- `event: text_chunk` テキストフラグメント
|
||||
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
|
||||
- `workflow_run_id` (string) ワークフロー実行の一意のID
|
||||
- `event` (string) `text_chunk`に固定
|
||||
- `data` (object) 詳細
|
||||
- `text` (string) テキスト内容
|
||||
- `from_variable_selector` (array) テキスト生成元パス(開発者がどのノードのどの変数から生成されたかを理解するための情報)
|
||||
- `event: node_finished` ノード実行終了、同じイベントで異なる状態で成功または失敗
|
||||
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
|
||||
- `workflow_run_id` (string) ワークフロー実行の一意のID
|
||||
@ -733,3 +740,57 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
———
|
||||
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='アプリのWebApp設定を取得'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
アプリのWebApp設定を取得するために使用します。
|
||||
### 応答
|
||||
- `title` (string) WebApp名
|
||||
- `icon_type` (string) アイコンタイプ、`emoji`-絵文字、`image`-画像
|
||||
- `icon` (string) アイコン。`emoji`タイプの場合は絵文字、`image`タイプの場合は画像URL
|
||||
- `icon_background` (string) 16進数形式の背景色
|
||||
- `icon_url` (string) アイコンのURL
|
||||
- `description` (string) 説明
|
||||
- `copyright` (string) 著作権情報
|
||||
- `privacy_policy` (string) プライバシーポリシーのリンク
|
||||
- `custom_disclaimer` (string) カスタム免責事項
|
||||
- `default_language` (string) デフォルト言語
|
||||
- `show_workflow_steps` (bool) ワークフローの詳細を表示するかどうか
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="POST" label="/meta" targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/site' \
|
||||
-H 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||
|
||||
|
||||
@ -87,7 +87,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
每个流式块均为 data: 开头,块之间以 `\n\n` 即两个换行符分隔,如下所示:
|
||||
<CodeGroup>
|
||||
```streaming {{ title: 'Response' }}
|
||||
data: {"event": "message", "task_id": "900bbd43-dc0b-4383-a372-aa6e6c414227", "id": "663c5084-a254-4040-8ad3-51f2a3c1a77c", "answer": "Hi", "created_at": 1705398420}\n\n
|
||||
data: {"event": "text_chunk", "workflow_run_id": "b85e5fc5-751b-454d-b14e-dc5f240b0a31", "task_id": "bd029338-b068-4d34-a331-fc85478922c2", "data": {"text": "\u4e3a\u4e86", "from_variable_selector": ["1745912968134", "text"]}}\n\n
|
||||
```
|
||||
</CodeGroup>
|
||||
流式块中根据 `event` 不同,结构也不同,包含以下类型:
|
||||
@ -113,6 +113,13 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
- `predecessor_node_id` (string) 前置节点 ID,用于画布展示执行路径
|
||||
- `inputs` (object) 节点中所有使用到的前置节点变量内容
|
||||
- `created_at` (timestamp) 开始时间
|
||||
- `event: text_chunk` 文本片段
|
||||
- `task_id` (string) 任务 ID,用于请求跟踪和下方的停止响应接口
|
||||
- `workflow_run_id` (string) workflow 执行 ID
|
||||
- `event` (string) 固定为 `text_chunk`
|
||||
- `data` (object) 详细内容
|
||||
- `text` (string) 文本内容
|
||||
- `from_variable_selector` (array) 文本来源路径,帮助开发者了解文本是由哪个节点的哪个变量生成的
|
||||
- `event: node_finished` node 执行结束,成功失败同一事件中不同状态
|
||||
- `task_id` (string) 任务 ID,用于请求跟踪和下方的停止响应接口
|
||||
- `workflow_run_id` (string) workflow 执行 ID
|
||||
@ -720,3 +727,57 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='获取应用 WebApp 设置'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
用于获取应用的 WebApp 设置
|
||||
### Response
|
||||
- `title` (string) WebApp 名称
|
||||
- `icon_type` (string) 图标类型, `emoji`-表情, `image`-图片
|
||||
- `icon` (string) 图标, 如果是 `emoji` 类型, 则是 emoji 表情符号, 如果是 `image` 类型, 则是图片 URL
|
||||
- `icon_background` (string) hex 格式的背景色
|
||||
- `icon_url` (string) 图标 URL
|
||||
- `description` (string) 描述
|
||||
- `copyright` (string) 版权信息
|
||||
- `privacy_policy` (string) 隐私政策链接
|
||||
- `custom_disclaimer` (string) 自定义免责声明
|
||||
- `default_language` (string) 默认语言
|
||||
- `show_workflow_steps` (bool) 是否显示工作流详情
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="POST" label="/meta" targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/site' \
|
||||
-H 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||
|
||||
@ -31,8 +31,8 @@ const Category: FC<ICategoryProps> = ({
|
||||
const isAllCategories = !list.includes(value as AppCategory) || value === allCategoriesEn
|
||||
|
||||
const itemClassName = (isSelected: boolean) => cn(
|
||||
'flex h-[32px] cursor-pointer items-center rounded-lg border-[0.5px] border-transparent px-3 py-[7px] font-medium leading-[18px] text-gray-700 hover:bg-gray-200',
|
||||
isSelected && 'border-gray-200 bg-white text-primary-600 shadow-xs hover:bg-white',
|
||||
'flex h-[32px] cursor-pointer items-center rounded-lg border-[0.5px] border-transparent px-3 py-[7px] font-medium leading-[18px] text-text-tertiary hover:bg-components-main-nav-nav-button-bg-active',
|
||||
isSelected && 'border-components-main-nav-nav-button-border bg-components-main-nav-nav-button-bg-active text-components-main-nav-nav-button-text-active shadow-xs',
|
||||
)
|
||||
|
||||
return (
|
||||
@ -50,7 +50,7 @@ const Category: FC<ICategoryProps> = ({
|
||||
className={itemClassName(name === value)}
|
||||
onClick={() => onChange(name)}
|
||||
>
|
||||
{categoryI18n[name] ? t(`explore.category.${name}`) : name}
|
||||
{(categoryI18n as any)[name] ? t(`explore.category.${name}`) : name}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -13,6 +13,7 @@ import {
|
||||
RiMap2Line,
|
||||
RiSettings3Line,
|
||||
RiStarLine,
|
||||
RiTShirt2Line,
|
||||
} from '@remixicon/react'
|
||||
import Link from 'next/link'
|
||||
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
|
||||
@ -24,6 +25,7 @@ import Compliance from './compliance'
|
||||
import PremiumBadge from '@/app/components/base/premium-badge'
|
||||
import { useGetDocLanguage } from '@/context/i18n'
|
||||
import Avatar from '@/app/components/base/avatar'
|
||||
import ThemeSwitcher from '@/app/components/base/theme-switcher'
|
||||
import { logout } from '@/service/common'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
@ -81,8 +83,8 @@ export default function AppSelector() {
|
||||
<MenuItems
|
||||
className="
|
||||
absolute right-0 mt-1.5 w-60 max-w-80
|
||||
origin-top-right divide-y divide-divider-subtle rounded-xl bg-components-panel-bg-blur
|
||||
shadow-lg focus:outline-none
|
||||
origin-top-right divide-y divide-divider-subtle rounded-xl bg-components-panel-bg-blur shadow-lg
|
||||
backdrop-blur-sm focus:outline-none
|
||||
"
|
||||
>
|
||||
<MenuItem disabled>
|
||||
@ -187,6 +189,15 @@ export default function AppSelector() {
|
||||
}
|
||||
</div>
|
||||
</>}
|
||||
<MenuItem disabled>
|
||||
<div className='p-1'>
|
||||
<div className={cn(itemClassName, 'hover:bg-transparent')}>
|
||||
<RiTShirt2Line className='size-4 shrink-0 text-text-tertiary' />
|
||||
<div className='system-md-regular grow px-1 text-text-secondary'>{t('common.theme.theme')}</div>
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<div className='p-1' onClick={() => handleLogout()}>
|
||||
<div
|
||||
|
||||
@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import { Menu, MenuButton, MenuItems, Transition } from '@headlessui/react'
|
||||
import { RiArrowDownSLine } from '@remixicon/react'
|
||||
import cn from '@/utils/classnames'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { WEB_PREFIX } from '@/config'
|
||||
import PlanBadge from '@/app/components/header/plan-badge'
|
||||
import { switchWorkspace } from '@/service/common'
|
||||
import { useWorkspacesContext } from '@/context/workspace-context'
|
||||
@ -23,7 +23,7 @@ const WorkplaceSelector = () => {
|
||||
return
|
||||
await switchWorkspace({ url: '/workspaces/switch', body: { tenant_id } })
|
||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||
location.assign(`${location.origin}${basePath}`)
|
||||
location.assign(WEB_PREFIX)
|
||||
}
|
||||
catch {
|
||||
notify({ type: 'error', message: t('common.provider.saveFailed') })
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
import cn from '@/utils/classnames'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { WEB_PREFIX } from '@/config'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useState } from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
@ -33,7 +34,7 @@ const EditWorkspaceModal = ({
|
||||
},
|
||||
})
|
||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||
location.assign(`${location.origin}`)
|
||||
location.assign(WEB_PREFIX)
|
||||
}
|
||||
catch {
|
||||
notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
'use client'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { t } from 'i18next'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import s from './index.module.css'
|
||||
@ -19,7 +18,8 @@ const InvitationLink = ({
|
||||
const selector = useRef(`invite-link-${randomString(4)}`)
|
||||
|
||||
const copyHandle = useCallback(() => {
|
||||
copy(`${!value.url.startsWith('http') ? window.location.origin : ''}${basePath}${value.url}`)
|
||||
// No prefix is needed here because the backend has already processed it
|
||||
copy(`${!value.url.startsWith('http') ? window.location.origin : ''}${value.url}`)
|
||||
setIsCopied(true)
|
||||
}, [value])
|
||||
|
||||
@ -42,7 +42,7 @@ const InvitationLink = ({
|
||||
<Tooltip
|
||||
popupContent={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
|
||||
>
|
||||
<div className='r-0 absolute left-0 top-0 w-full cursor-pointer truncate pl-2 pr-2' onClick={copyHandle}>{basePath + value.url}</div>
|
||||
<div className='r-0 absolute left-0 top-0 w-full cursor-pointer truncate pl-2 pr-2' onClick={copyHandle}>{value.url}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="h-4 shrink-0 border bg-divider-regular" />
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Link from 'next/link'
|
||||
import {
|
||||
@ -29,6 +30,7 @@ const InstallFromMarketplace = ({
|
||||
searchText,
|
||||
}: InstallFromMarketplaceProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { theme } = useTheme()
|
||||
const [collapse, setCollapse] = useState(false)
|
||||
const locale = getLocaleOnClient()
|
||||
const {
|
||||
@ -53,7 +55,7 @@ const InstallFromMarketplace = ({
|
||||
</div>
|
||||
<div className='mb-2 flex items-center pt-2'>
|
||||
<span className='system-sm-regular pr-1 text-text-tertiary'>{t('common.modelProvider.discoverMore')}</span>
|
||||
<Link target="_blank" href={`${MARKETPLACE_URL_PREFIX}`} className='system-sm-medium inline-flex items-center text-text-accent'>
|
||||
<Link target="_blank" href={`${MARKETPLACE_URL_PREFIX}${theme ? `?theme=${theme}` : ''}`} className='system-sm-medium inline-flex items-center text-text-accent'>
|
||||
{t('plugin.marketplace.difyMarketplace')}
|
||||
<RiArrowRightUpLine className='h-4 w-4' />
|
||||
</Link>
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import type { FC } from 'react'
|
||||
import type { ModelProvider } from '../declarations'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { useLanguage } from '../hooks'
|
||||
import { Openai } from '@/app/components/base/icons/src/vender/other'
|
||||
import { AnthropicDark, AnthropicLight } from '@/app/components/base/icons/src/public/llm'
|
||||
@ -41,7 +40,7 @@ const ProviderIcon: FC<ProviderIconProps> = ({
|
||||
<div className={cn('inline-flex items-center gap-2', className)}>
|
||||
<img
|
||||
alt='provider-icon'
|
||||
src={basePath + renderI18nObject(provider.icon_small, language)}
|
||||
src={renderI18nObject(provider.icon_small, language)}
|
||||
className='h-6 w-6'
|
||||
/>
|
||||
<div className='system-md-semibold text-text-primary'>
|
||||
|
||||
@ -14,7 +14,6 @@ import Nav from '../nav'
|
||||
import type { NavItem } from '../nav/nav-selector'
|
||||
import { fetchDatasetDetail, fetchDatasets } from '@/service/datasets'
|
||||
import type { DataSetListResponse } from '@/models/datasets'
|
||||
import { basePath } from '@/utils/var'
|
||||
|
||||
const getKey = (pageIndex: number, previousPageData: DataSetListResponse) => {
|
||||
if (!pageIndex || previousPageData.has_more)
|
||||
@ -57,7 +56,7 @@ const DatasetNav = () => {
|
||||
icon_background: dataset.icon_background,
|
||||
})) as NavItem[]}
|
||||
createText={t('common.menus.newDataset')}
|
||||
onCreate={() => router.push(`${basePath}/datasets/create`)}
|
||||
onCreate={() => router.push('/datasets/create')}
|
||||
onLoadmore={handleLoadmore}
|
||||
/>
|
||||
)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
'use client'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { RiArrowRightUpLine } from '@remixicon/react'
|
||||
import { getPluginLinkInMarketplace } from '../utils'
|
||||
import Card from '@/app/components/plugins/card'
|
||||
@ -22,6 +23,7 @@ const CardWrapper = ({
|
||||
locale,
|
||||
}: CardWrapperProps) => {
|
||||
const { t } = useMixedTranslation(locale)
|
||||
const { theme } = useTheme()
|
||||
const [isShowInstallFromMarketplace, {
|
||||
setTrue: showInstallFromMarketplace,
|
||||
setFalse: hideInstallFromMarketplace,
|
||||
@ -54,7 +56,7 @@ const CardWrapper = ({
|
||||
>
|
||||
{t('plugin.detailPanel.operation.install')}
|
||||
</Button>
|
||||
<a href={`${getPluginLinkInMarketplace(plugin)}?language=${localeFromLocale}`} target='_blank' className='block w-[calc(50%-4px)] flex-1 shrink-0'>
|
||||
<a href={`${getPluginLinkInMarketplace(plugin)}?language=${localeFromLocale}${theme ? `&theme=${theme}` : ''}`} target='_blank' className='block w-[calc(50%-4px)] flex-1 shrink-0'>
|
||||
<Button
|
||||
className='w-full gap-0.5'
|
||||
>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import {
|
||||
@ -49,6 +50,7 @@ const DetailHeader = ({
|
||||
onUpdate,
|
||||
}: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { theme } = useTheme()
|
||||
const locale = useGetLanguage()
|
||||
const { checkForUpdates, fetchReleases } = useGitHubReleases()
|
||||
const { setShowUpdatePluginModal } = useModalContext()
|
||||
@ -85,9 +87,9 @@ const DetailHeader = ({
|
||||
if (isFromGitHub)
|
||||
return `https://github.com/${meta!.repo}`
|
||||
if (isFromMarketplace)
|
||||
return `${MARKETPLACE_URL_PREFIX}/plugins/${author}/${name}`
|
||||
return `${MARKETPLACE_URL_PREFIX}/plugins/${author}/${name}${theme ? `?theme=${theme}` : ''}`
|
||||
return ''
|
||||
}, [author, isFromGitHub, isFromMarketplace, meta, name])
|
||||
}, [author, isFromGitHub, isFromMarketplace, meta, name, theme])
|
||||
|
||||
const [isShowUpdateModal, {
|
||||
setTrue: showUpdateModal,
|
||||
|
||||
@ -149,7 +149,7 @@ const EndpointCard = ({
|
||||
</ActionButton>
|
||||
</div>
|
||||
</div>
|
||||
{data.declaration.endpoints.map((endpoint, index) => (
|
||||
{data.declaration.endpoints.filter(endpoint => !endpoint.hidden).map((endpoint, index) => (
|
||||
<div key={index} className='flex h-6 items-center'>
|
||||
<div className='system-xs-regular w-12 shrink-0 text-text-tertiary'>{endpoint.method}</div>
|
||||
<div className='group/item system-xs-regular flex grow items-center truncate text-text-secondary'>
|
||||
|
||||
@ -183,7 +183,7 @@ const ReasoningConfigForm: React.FC<Props> = ({
|
||||
<>
|
||||
{isString && (
|
||||
<Input
|
||||
className={cn(inputsIsFocus[variable] ? 'border-gray-300 bg-gray-50 shadow-xs' : 'border-gray-100 bg-gray-100', 'rounded-lg border px-3 py-[6px]')}
|
||||
className={cn(inputsIsFocus[variable] ? 'border-components-input-border-active bg-components-input-bg-active shadow-xs' : 'border-components-input-border-hover bg-components-input-bg-normal', 'rounded-lg border px-3 py-[6px]')}
|
||||
value={varInput?.value as string || ''}
|
||||
onChange={handleMixedTypeChange(variable)}
|
||||
nodesOutputVars={nodeOutputVars}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import {
|
||||
RiArrowRightUpLine,
|
||||
RiBugLine,
|
||||
@ -38,6 +39,7 @@ const PluginItem: FC<Props> = ({
|
||||
plugin,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { theme } = useTheme()
|
||||
const { categoriesMap } = useSingleCategories()
|
||||
const currentPluginID = usePluginPageContext(v => v.currentPluginID)
|
||||
const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID)
|
||||
@ -164,7 +166,7 @@ const PluginItem: FC<Props> = ({
|
||||
}
|
||||
{source === PluginSource.marketplace
|
||||
&& <>
|
||||
<a href={`${MARKETPLACE_URL_PREFIX}/plugins/${author}/${name}`} target='_blank' className='flex items-center gap-0.5'>
|
||||
<a href={`${MARKETPLACE_URL_PREFIX}/plugins/${author}/${name}${theme ? `?theme=${theme}` : ''}`} target='_blank' className='flex items-center gap-0.5'>
|
||||
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('plugin.from')} <span className='text-text-secondary'>marketplace</span></div>
|
||||
<RiArrowRightUpLine className='h-3 w-3 text-text-tertiary' />
|
||||
</a>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import type { FC } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiArrowRightUpLine } from '@remixicon/react'
|
||||
import Badge from '../base/badge'
|
||||
@ -28,6 +29,7 @@ const ProviderCard: FC<Props> = ({
|
||||
}) => {
|
||||
const getValueFromI18nObject = useRenderI18nObject()
|
||||
const { t } = useTranslation()
|
||||
const { theme } = useTheme()
|
||||
const [isShowInstallFromMarketplace, {
|
||||
setTrue: showInstallFromMarketplace,
|
||||
setFalse: hideInstallFromMarketplace,
|
||||
@ -74,7 +76,7 @@ const ProviderCard: FC<Props> = ({
|
||||
className='grow'
|
||||
variant='secondary'
|
||||
>
|
||||
<a href={`${getPluginLinkInMarketplace(payload)}?language=${locale}`} target='_blank' className='flex items-center gap-0.5'>
|
||||
<a href={`${getPluginLinkInMarketplace(payload)}?language=${locale}${theme ? `&theme=${theme}` : ''}`} target='_blank' className='flex items-center gap-0.5'>
|
||||
{t('plugin.detailPanel.operation.detail')}
|
||||
<RiArrowRightUpLine className='h-4 w-4' />
|
||||
</a>
|
||||
|
||||
@ -36,6 +36,7 @@ export type PluginEndpointDeclaration = {
|
||||
export type EndpointItem = {
|
||||
path: string
|
||||
method: string
|
||||
hidden?: boolean
|
||||
}
|
||||
|
||||
export type EndpointListItem = {
|
||||
|
||||
@ -16,6 +16,7 @@ import {
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import ThemeSwitcher from '@/app/components/base/theme-switcher'
|
||||
import type { SiteInfo } from '@/models/share'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
@ -70,6 +71,13 @@ const MenuDropdown: FC<Props> = ({
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-50'>
|
||||
<div className='w-[224px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-sm'>
|
||||
<div className='p-1'>
|
||||
<div className={cn('system-md-regular flex cursor-pointer items-center rounded-lg py-1.5 pl-3 pr-2 text-text-secondary')}>
|
||||
<div className='grow'>{t('common.theme.theme')}</div>
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
<Divider type='horizontal' className='my-0' />
|
||||
<div className='p-1'>
|
||||
{data?.privacy_policy && (
|
||||
<a href={data.privacy_policy} target='_blank' className='system-md-regular flex cursor-pointer items-center rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'>
|
||||
@ -83,17 +91,6 @@ const MenuDropdown: FC<Props> = ({
|
||||
}}
|
||||
className='system-md-regular cursor-pointer rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'
|
||||
>{t('common.userProfile.about')}</div>
|
||||
{false && (
|
||||
<>
|
||||
<Divider />
|
||||
<div
|
||||
onClick={() => {
|
||||
handleLogout()
|
||||
}}
|
||||
className='system-md-regular cursor-pointer rounded-lg px-3 py-1.5 text-text-destructive hover:bg-state-base-hover'
|
||||
>{t('common.userProfile.logout')}</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
|
||||
@ -2,7 +2,7 @@ import {
|
||||
memo,
|
||||
useCallback,
|
||||
} from 'react'
|
||||
import { basePath } from '@/utils/var'
|
||||
import Link from 'next/link'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiAddLine,
|
||||
@ -54,7 +54,7 @@ const Blocks = ({
|
||||
>
|
||||
<div className='flex h-[22px] w-full items-center justify-between pl-3 pr-1 text-xs font-medium text-gray-500'>
|
||||
{toolWithProvider.label[language]}
|
||||
<a className='hidden cursor-pointer items-center group-hover:flex' href={`${basePath}/tools?category=${toolWithProvider.type}`} target='_blank'>{t('tools.addToolModal.manageInTools')}<ArrowUpRight className='ml-0.5 h-3 w-3' /></a>
|
||||
<Link className='hidden cursor-pointer items-center group-hover:flex' href={`/tools?category=${toolWithProvider.type}`} target='_blank'>{t('tools.addToolModal.manageInTools')}<ArrowUpRight className='ml-0.5 h-3 w-3' /></Link>
|
||||
</div>
|
||||
{list.map((tool) => {
|
||||
const labelContent = (() => {
|
||||
|
||||
@ -2,6 +2,7 @@ import {
|
||||
useEffect,
|
||||
useRef,
|
||||
} from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import {
|
||||
RiArrowRightUpLine,
|
||||
RiArrowUpDoubleLine,
|
||||
@ -25,7 +26,7 @@ const Marketplace = ({
|
||||
}: MarketplaceProps) => {
|
||||
const locale = getLocaleOnClient()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { theme } = useTheme()
|
||||
const {
|
||||
isLoading,
|
||||
marketplaceCollections,
|
||||
@ -83,7 +84,7 @@ const Marketplace = ({
|
||||
</span>
|
||||
{t('common.operation.in')}
|
||||
<a
|
||||
href={`${MARKETPLACE_URL_PREFIX}?language=${locale}&q=${searchPluginText}&tags=${filterPluginTags.join(',')}`}
|
||||
href={`${MARKETPLACE_URL_PREFIX}?language=${locale}&q=${searchPluginText}&tags=${filterPluginTags.join(',')}${theme ? `&theme=${theme}` : ''}`}
|
||||
className='system-sm-medium ml-1 flex items-center text-text-accent'
|
||||
target='_blank'
|
||||
>
|
||||
|
||||
@ -53,7 +53,10 @@ const ProviderList = () => {
|
||||
})
|
||||
}, [activeTab, tagFilterValue, keywords, collectionList])
|
||||
|
||||
const [currentProvider, setCurrentProvider] = useState<Collection | undefined>()
|
||||
const [currentProviderId, setCurrentProviderId] = useState<string | undefined>()
|
||||
const currentProvider = useMemo<Collection | undefined>(() => {
|
||||
return filteredCollectionList.find(collection => collection.id === currentProviderId)
|
||||
}, [currentProviderId, filteredCollectionList])
|
||||
const { data: pluginList } = useInstalledPluginList()
|
||||
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||
const currentPluginDetail = useMemo(() => {
|
||||
@ -70,14 +73,14 @@ const ProviderList = () => {
|
||||
>
|
||||
<div className={cn(
|
||||
'sticky top-0 z-20 flex flex-wrap items-center justify-between gap-y-2 bg-background-body px-12 pb-2 pt-4 leading-[56px]',
|
||||
currentProvider && 'pr-6',
|
||||
currentProviderId && 'pr-6',
|
||||
)}>
|
||||
<TabSliderNew
|
||||
value={activeTab}
|
||||
onChange={(state) => {
|
||||
setActiveTab(state)
|
||||
if (state !== activeTab)
|
||||
setCurrentProvider(undefined)
|
||||
setCurrentProviderId(undefined)
|
||||
}}
|
||||
options={options}
|
||||
/>
|
||||
@ -93,47 +96,43 @@ const ProviderList = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
(filteredCollectionList.length > 0 || activeTab !== 'builtin') && (
|
||||
<div className={cn(
|
||||
'relative grid shrink-0 grid-cols-1 content-start gap-4 px-12 pb-4 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4',
|
||||
!filteredCollectionList.length && activeTab === 'workflow' && 'grow',
|
||||
)}>
|
||||
{activeTab === 'api' && <CustomCreateCard onRefreshData={refetch} />}
|
||||
{filteredCollectionList.map(collection => (
|
||||
<div
|
||||
key={collection.id}
|
||||
onClick={() => setCurrentProvider(collection)}
|
||||
>
|
||||
<Card
|
||||
className={cn(
|
||||
'cursor-pointer border-[1.5px] border-transparent',
|
||||
currentProvider?.id === collection.id && 'border-components-option-card-option-selected-border',
|
||||
)}
|
||||
hideCornerMark
|
||||
payload={{
|
||||
...collection,
|
||||
brief: collection.description,
|
||||
org: collection.plugin_id ? collection.plugin_id.split('/')[0] : '',
|
||||
name: collection.plugin_id ? collection.plugin_id.split('/')[1] : collection.name,
|
||||
} as any}
|
||||
footer={
|
||||
<CardMoreInfo
|
||||
tags={collection.labels}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{!filteredCollectionList.length && activeTab === 'workflow' && <div className='absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'><WorkflowToolEmpty /></div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!filteredCollectionList.length && activeTab === 'builtin' && (
|
||||
<Empty lightCard text={t('tools.noTools')} className='h-[224px] px-12' />
|
||||
)
|
||||
}
|
||||
{(filteredCollectionList.length > 0 || activeTab !== 'builtin') && (
|
||||
<div className={cn(
|
||||
'relative grid shrink-0 grid-cols-1 content-start gap-4 px-12 pb-4 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4',
|
||||
!filteredCollectionList.length && activeTab === 'workflow' && 'grow',
|
||||
)}>
|
||||
{activeTab === 'api' && <CustomCreateCard onRefreshData={refetch} />}
|
||||
{filteredCollectionList.map(collection => (
|
||||
<div
|
||||
key={collection.id}
|
||||
onClick={() => setCurrentProviderId(collection.id)}
|
||||
>
|
||||
<Card
|
||||
className={cn(
|
||||
'cursor-pointer border-[1.5px] border-transparent',
|
||||
currentProviderId === collection.id && 'border-components-option-card-option-selected-border',
|
||||
)}
|
||||
hideCornerMark
|
||||
payload={{
|
||||
...collection,
|
||||
brief: collection.description,
|
||||
org: collection.plugin_id ? collection.plugin_id.split('/')[0] : '',
|
||||
name: collection.plugin_id ? collection.plugin_id.split('/')[1] : collection.name,
|
||||
} as any}
|
||||
footer={
|
||||
<CardMoreInfo
|
||||
tags={collection.labels}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{!filteredCollectionList.length && activeTab === 'workflow' && <div className='absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'><WorkflowToolEmpty /></div>}
|
||||
</div>
|
||||
)}
|
||||
{!filteredCollectionList.length && activeTab === 'builtin' && (
|
||||
<Empty lightCard text={t('tools.noTools')} className='h-[224px] px-12' />
|
||||
)}
|
||||
{
|
||||
enable_marketplace && activeTab === 'builtin' && (
|
||||
<Marketplace
|
||||
@ -150,14 +149,14 @@ const ProviderList = () => {
|
||||
{currentProvider && !currentProvider.plugin_id && (
|
||||
<ProviderDetail
|
||||
collection={currentProvider}
|
||||
onHide={() => setCurrentProvider(undefined)}
|
||||
onHide={() => setCurrentProviderId(undefined)}
|
||||
onRefreshData={refetch}
|
||||
/>
|
||||
)}
|
||||
<PluginDetailPanel
|
||||
detail={currentPluginDetail}
|
||||
onUpdate={() => invalidateInstalledPluginList()}
|
||||
onHide={() => setCurrentProvider(undefined)}
|
||||
onHide={() => setCurrentProviderId(undefined)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -28,7 +28,7 @@ const Contribute = ({ onRefreshData }: Props) => {
|
||||
const linkUrl = useMemo(() => {
|
||||
if (language.startsWith('zh_'))
|
||||
return 'https://docs.dify.ai/zh-hans/guides/tools#ru-he-chuang-jian-zi-ding-yi-gong-ju'
|
||||
return 'https://docs.dify.ai/guides/tools#how-to-create-custom-tools'
|
||||
return 'https://docs.dify.ai/en/guides/tools#how-to-create-custom-tools'
|
||||
}, [language])
|
||||
|
||||
const [isShowEditCollectionToolModal, setIsShowEditCustomCollectionModal] = useState(false)
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
RiCloseLine,
|
||||
} from '@remixicon/react'
|
||||
import { AuthHeaderPrefix, AuthType, CollectionType } from '../types'
|
||||
import { basePath } from '@/utils/var'
|
||||
import Link from 'next/link'
|
||||
import type { Collection, CustomCollectionBackend, Tool, WorkflowToolProviderRequest, WorkflowToolProviderResponse } from '../types'
|
||||
import ToolItem from './tool-item'
|
||||
import cn from '@/utils/classnames'
|
||||
@ -279,10 +279,10 @@ const ProviderDetail = ({
|
||||
variant='primary'
|
||||
className={cn('my-3 w-[183px] shrink-0')}
|
||||
>
|
||||
<a className='flex items-center' href={`${basePath}/app/${(customCollection as WorkflowToolProviderResponse).workflow_app_id}/workflow`} rel='noreferrer' target='_blank'>
|
||||
<Link className='flex items-center' href={`/app/${(customCollection as WorkflowToolProviderResponse).workflow_app_id}/workflow`} rel='noreferrer' target='_blank'>
|
||||
<div className='system-sm-medium'>{t('tools.openInStudio')}</div>
|
||||
<LinkExternal02 className='ml-1 h-4 w-4' />
|
||||
</a>
|
||||
</Link>
|
||||
</Button>
|
||||
<Button
|
||||
className={cn('my-3 w-[183px] shrink-0')}
|
||||
|
||||
@ -8,6 +8,7 @@ import type { WorkflowProps } from '@/app/components/workflow'
|
||||
import WorkflowChildren from './workflow-children'
|
||||
import {
|
||||
useNodesSyncDraft,
|
||||
useWorkflowRefreshDraft,
|
||||
useWorkflowRun,
|
||||
useWorkflowStartRun,
|
||||
} from '../hooks'
|
||||
@ -32,6 +33,7 @@ const WorkflowMain = ({
|
||||
doSyncWorkflowDraft,
|
||||
syncWorkflowDraftWhenPageClose,
|
||||
} = useNodesSyncDraft()
|
||||
const { handleRefreshWorkflowDraft } = useWorkflowRefreshDraft()
|
||||
const {
|
||||
handleBackupDraft,
|
||||
handleLoadBackupDraft,
|
||||
@ -49,6 +51,7 @@ const WorkflowMain = ({
|
||||
return {
|
||||
syncWorkflowDraftWhenPageClose,
|
||||
doSyncWorkflowDraft,
|
||||
handleRefreshWorkflowDraft,
|
||||
handleBackupDraft,
|
||||
handleLoadBackupDraft,
|
||||
handleRestoreFromPublishedWorkflow,
|
||||
@ -61,6 +64,7 @@ const WorkflowMain = ({
|
||||
}, [
|
||||
syncWorkflowDraftWhenPageClose,
|
||||
doSyncWorkflowDraft,
|
||||
handleRefreshWorkflowDraft,
|
||||
handleBackupDraft,
|
||||
handleLoadBackupDraft,
|
||||
handleRestoreFromPublishedWorkflow,
|
||||
|
||||
@ -4,3 +4,4 @@ export * from './use-nodes-sync-draft'
|
||||
export * from './use-workflow-run'
|
||||
export * from './use-workflow-start-run'
|
||||
export * from './use-is-chat-mode'
|
||||
export * from './use-workflow-refresh-draft'
|
||||
|
||||
@ -6,20 +6,20 @@ import {
|
||||
useWorkflowStore,
|
||||
} from '@/app/components/workflow/store'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { useWorkflowUpdate } from '@/app/components/workflow/hooks'
|
||||
import {
|
||||
useNodesReadOnly,
|
||||
} from '@/app/components/workflow/hooks/use-workflow'
|
||||
import { syncWorkflowDraft } from '@/service/workflow'
|
||||
import { useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import { API_PREFIX } from '@/config'
|
||||
import { useWorkflowRefreshDraft } from '.'
|
||||
|
||||
export const useNodesSyncDraft = () => {
|
||||
const store = useStoreApi()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const featuresStore = useFeaturesStore()
|
||||
const { getNodesReadOnly } = useNodesReadOnly()
|
||||
const { handleRefreshWorkflowDraft } = useWorkflowUpdate()
|
||||
const { handleRefreshWorkflowDraft } = useWorkflowRefreshDraft()
|
||||
const params = useParams()
|
||||
|
||||
const getPostParams = useCallback(() => {
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { fetchWorkflowDraft } from '@/service/workflow'
|
||||
import type { WorkflowDataUpdater } from '@/app/components/workflow/types'
|
||||
import { useWorkflowUpdate } from '@/app/components/workflow/hooks'
|
||||
|
||||
export const useWorkflowRefreshDraft = () => {
|
||||
const workflowStore = useWorkflowStore()
|
||||
const { handleUpdateWorkflowCanvas } = useWorkflowUpdate()
|
||||
|
||||
const handleRefreshWorkflowDraft = useCallback(() => {
|
||||
const {
|
||||
appId,
|
||||
setSyncWorkflowDraftHash,
|
||||
setIsSyncingWorkflowDraft,
|
||||
setEnvironmentVariables,
|
||||
setEnvSecrets,
|
||||
setConversationVariables,
|
||||
} = workflowStore.getState()
|
||||
setIsSyncingWorkflowDraft(true)
|
||||
fetchWorkflowDraft(`/apps/${appId}/workflows/draft`).then((response) => {
|
||||
handleUpdateWorkflowCanvas(response.graph as WorkflowDataUpdater)
|
||||
setSyncWorkflowDraftHash(response.hash)
|
||||
setEnvSecrets((response.environment_variables || []).filter(env => env.value_type === 'secret').reduce((acc, env) => {
|
||||
acc[env.id] = env.value
|
||||
return acc
|
||||
}, {} as Record<string, string>))
|
||||
setEnvironmentVariables(response.environment_variables?.map(env => env.value_type === 'secret' ? { ...env, value: '[__HIDDEN__]' } : env) || [])
|
||||
setConversationVariables(response.conversation_variables || [])
|
||||
}).finally(() => setIsSyncingWorkflowDraft(false))
|
||||
}, [handleUpdateWorkflowCanvas, workflowStore])
|
||||
|
||||
return {
|
||||
handleRefreshWorkflowDraft,
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiMoreFill } from '@remixicon/react'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
@ -31,6 +32,7 @@ const OperationDropdown: FC<Props> = ({
|
||||
version,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { theme } = useTheme()
|
||||
const openRef = useRef(open)
|
||||
const setOpen = useCallback((v: boolean) => {
|
||||
onOpenChange(v)
|
||||
@ -78,7 +80,7 @@ const OperationDropdown: FC<Props> = ({
|
||||
<PortalToFollowElemContent className='z-[9999]'>
|
||||
<div className='w-[112px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'>
|
||||
<div onClick={handleDownload} className='system-md-regular cursor-pointer rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'>{t('common.operation.download')}</div>
|
||||
<a href={`${MARKETPLACE_URL_PREFIX}/plugins/${author}/${name}`} target='_blank' className='system-md-regular block cursor-pointer rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'>{t('common.operation.viewDetails')}</a>
|
||||
<a href={`${MARKETPLACE_URL_PREFIX}/plugins/${author}/${name}${theme ? `?theme=${theme}` : ''}`} target='_blank' className='system-md-regular block cursor-pointer rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'>{t('common.operation.viewDetails')}</a>
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
|
||||
@ -31,6 +31,7 @@ type NodesExtraData = {
|
||||
getAvailablePrevNodes: (isChatMode: boolean) => BlockEnum[]
|
||||
getAvailableNextNodes: (isChatMode: boolean) => BlockEnum[]
|
||||
checkValid: any
|
||||
checkVarValid?: any
|
||||
}
|
||||
export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
[BlockEnum.Start]: {
|
||||
@ -59,6 +60,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: AnswerDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: AnswerDefault.getAvailableNextNodes,
|
||||
checkValid: AnswerDefault.checkValid,
|
||||
checkVarValid: AnswerDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.LLM]: {
|
||||
author: 'Dify',
|
||||
@ -68,6 +70,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: LLMDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: LLMDefault.getAvailableNextNodes,
|
||||
checkValid: LLMDefault.checkValid,
|
||||
checkVarValid: LLMDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.KnowledgeRetrieval]: {
|
||||
author: 'Dify',
|
||||
@ -77,6 +80,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: KnowledgeRetrievalDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: KnowledgeRetrievalDefault.getAvailableNextNodes,
|
||||
checkValid: KnowledgeRetrievalDefault.checkValid,
|
||||
checkVarValid: KnowledgeRetrievalDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.IfElse]: {
|
||||
author: 'Dify',
|
||||
@ -86,6 +90,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: IfElseDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: IfElseDefault.getAvailableNextNodes,
|
||||
checkValid: IfElseDefault.checkValid,
|
||||
checkVarValid: IfElseDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.Iteration]: {
|
||||
author: 'Dify',
|
||||
@ -95,6 +100,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: IterationDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: IterationDefault.getAvailableNextNodes,
|
||||
checkValid: IterationDefault.checkValid,
|
||||
checkVarValid: IterationDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.IterationStart]: {
|
||||
author: 'Dify',
|
||||
@ -140,6 +146,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: CodeDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: CodeDefault.getAvailableNextNodes,
|
||||
checkValid: CodeDefault.checkValid,
|
||||
checkVarValid: CodeDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.TemplateTransform]: {
|
||||
author: 'Dify',
|
||||
@ -149,6 +156,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: TemplateTransformDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: TemplateTransformDefault.getAvailableNextNodes,
|
||||
checkValid: TemplateTransformDefault.checkValid,
|
||||
checkVarValid: TemplateTransformDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.QuestionClassifier]: {
|
||||
author: 'Dify',
|
||||
@ -158,6 +166,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: QuestionClassifierDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: QuestionClassifierDefault.getAvailableNextNodes,
|
||||
checkValid: QuestionClassifierDefault.checkValid,
|
||||
checkVarValid: QuestionClassifierDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.HttpRequest]: {
|
||||
author: 'Dify',
|
||||
@ -167,6 +176,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: HttpRequestDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: HttpRequestDefault.getAvailableNextNodes,
|
||||
checkValid: HttpRequestDefault.checkValid,
|
||||
checkVarValid: HttpRequestDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.VariableAssigner]: {
|
||||
author: 'Dify',
|
||||
@ -185,6 +195,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: AssignerDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: AssignerDefault.getAvailableNextNodes,
|
||||
checkValid: AssignerDefault.checkValid,
|
||||
checkVarValid: AssignerDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.VariableAggregator]: {
|
||||
author: 'Dify',
|
||||
@ -194,6 +205,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: VariableAssignerDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: VariableAssignerDefault.getAvailableNextNodes,
|
||||
checkValid: VariableAssignerDefault.checkValid,
|
||||
checkVarValid: VariableAssignerDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.ParameterExtractor]: {
|
||||
author: 'Dify',
|
||||
@ -203,6 +215,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: ParameterExtractorDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: ParameterExtractorDefault.getAvailableNextNodes,
|
||||
checkValid: ParameterExtractorDefault.checkValid,
|
||||
checkVarValid: ParameterExtractorDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.Tool]: {
|
||||
author: 'Dify',
|
||||
@ -212,6 +225,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: ToolDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: ToolDefault.getAvailableNextNodes,
|
||||
checkValid: ToolDefault.checkValid,
|
||||
checkVarValid: ToolDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.DocExtractor]: {
|
||||
author: 'Dify',
|
||||
@ -221,6 +235,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: DocExtractorDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: DocExtractorDefault.getAvailableNextNodes,
|
||||
checkValid: DocExtractorDefault.checkValid,
|
||||
checkVarValid: DocExtractorDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.ListFilter]: {
|
||||
author: 'Dify',
|
||||
@ -230,6 +245,7 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
|
||||
getAvailablePrevNodes: ListFilterDefault.getAvailablePrevNodes,
|
||||
getAvailableNextNodes: ListFilterDefault.getAvailableNextNodes,
|
||||
checkValid: ListFilterDefault.checkValid,
|
||||
checkVarValid: ListFilterDefault.checkVarValid,
|
||||
},
|
||||
[BlockEnum.Agent]: {
|
||||
author: 'Dify',
|
||||
|
||||
@ -140,6 +140,16 @@ const WorkflowChecklist = ({
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
node.varErrorMessage?.map((errorMessage: string) => (
|
||||
<div className='rounded-b-lg bg-gray-25 px-3 py-2'>
|
||||
<div className='flex text-xs leading-[18px] text-gray-500'>
|
||||
<AlertTriangle className='mr-2 mt-[3px] h-3 w-3 text-[#F79009]' />
|
||||
{errorMessage}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
|
||||
@ -18,6 +18,7 @@ type CommonHooksFnMap = {
|
||||
}
|
||||
) => Promise<void>
|
||||
syncWorkflowDraftWhenPageClose: () => void
|
||||
handleRefreshWorkflowDraft: () => void
|
||||
handleBackupDraft: () => void
|
||||
handleLoadBackupDraft: () => void
|
||||
handleRestoreFromPublishedWorkflow: (...args: any[]) => void
|
||||
@ -35,6 +36,7 @@ export type Shape = {
|
||||
export const createHooksStore = ({
|
||||
doSyncWorkflowDraft = async () => noop(),
|
||||
syncWorkflowDraftWhenPageClose = noop,
|
||||
handleRefreshWorkflowDraft = noop,
|
||||
handleBackupDraft = noop,
|
||||
handleLoadBackupDraft = noop,
|
||||
handleRestoreFromPublishedWorkflow = noop,
|
||||
@ -48,6 +50,7 @@ export const createHooksStore = ({
|
||||
refreshAll: props => set(state => ({ ...state, ...props })),
|
||||
doSyncWorkflowDraft,
|
||||
syncWorkflowDraftWhenPageClose,
|
||||
handleRefreshWorkflowDraft,
|
||||
handleBackupDraft,
|
||||
handleLoadBackupDraft,
|
||||
handleRestoreFromPublishedWorkflow,
|
||||
|
||||
@ -16,3 +16,4 @@ export * from './use-shortcuts'
|
||||
export * from './use-workflow-interactions'
|
||||
export * from './use-workflow-mode'
|
||||
export * from './use-format-time-from-now'
|
||||
export * from './use-workflow-refresh-draft'
|
||||
|
||||
@ -15,6 +15,7 @@ import { useStore } from '../store'
|
||||
import {
|
||||
getToolCheckParams,
|
||||
getValidTreeNodes,
|
||||
transformStartNodeVariables,
|
||||
} from '../utils'
|
||||
import {
|
||||
CUSTOM_NODE,
|
||||
@ -45,6 +46,9 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
||||
const { data: strategyProviders } = useStrategyProviders()
|
||||
const datasetsDetail = useDatasetsDetailStore(s => s.datasetsDetail)
|
||||
|
||||
const chatVarList = useStore(s => s.conversationVariables)
|
||||
const environmentVariables = useStore(s => s.environmentVariables)
|
||||
|
||||
const getCheckData = useCallback((data: CommonNodeType<{}>) => {
|
||||
let checkData = data
|
||||
if (data.type === BlockEnum.KnowledgeRetrieval) {
|
||||
@ -64,7 +68,10 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
||||
|
||||
const needWarningNodes = useMemo(() => {
|
||||
const list = []
|
||||
const { validNodes } = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges)
|
||||
const { validNodes } = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges, true)
|
||||
|
||||
const allVariablesMap = transformStartNodeVariables(chatVarList, environmentVariables)
|
||||
const errMessageMap = new Map()
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i]
|
||||
@ -110,8 +117,32 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
||||
toolIcon,
|
||||
unConnected: !validNodes.find(n => n.id === node.id),
|
||||
errorMessage,
|
||||
varErrorMessage: [],
|
||||
})
|
||||
}
|
||||
errMessageMap.set(node.id, list[list.length - 1])
|
||||
if (nodesExtraData[node.data.type as BlockEnum].checkVarValid) {
|
||||
const { errorMessage: varErrorMessages } = nodesExtraData[node.data.type as BlockEnum].checkVarValid(node.data, { ...allVariablesMap, ...node._parentOutputVarMap }, t)
|
||||
|
||||
if (varErrorMessages?.length) {
|
||||
const errMessage = errMessageMap.get(node.id)
|
||||
if (errMessage) {
|
||||
errMessage.varErrorMessage = varErrorMessages
|
||||
}
|
||||
else {
|
||||
list.push({
|
||||
id: node.id,
|
||||
type: node.data.type,
|
||||
title: node.data.title,
|
||||
toolIcon,
|
||||
unConnected: !validNodes.find(n => n.id === node.id),
|
||||
errorMessage: '',
|
||||
varErrorMessage: varErrorMessages,
|
||||
})
|
||||
errMessageMap.set(node.id, list[list.length - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,8 +164,13 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
||||
})
|
||||
}
|
||||
|
||||
for (let i = 0; i < validNodes.length; i++) {
|
||||
const node = validNodes[i]
|
||||
delete node._parentOutputVarMap
|
||||
}
|
||||
|
||||
return list
|
||||
}, [nodes, edges, isChatMode, buildInTools, customTools, workflowTools, language, nodesExtraData, t, strategyProviders, getCheckData])
|
||||
}, [nodes, edges, isChatMode, buildInTools, customTools, workflowTools, language, nodesExtraData, t, strategyProviders, getCheckData, chatVarList, environmentVariables])
|
||||
|
||||
return needWarningNodes
|
||||
}
|
||||
@ -153,6 +189,9 @@ export const useChecklistBeforePublish = () => {
|
||||
const updateDatasetsDetail = useDatasetsDetailStore(s => s.updateDatasetsDetail)
|
||||
const updateTime = useRef(0)
|
||||
|
||||
const chatVarList = useStore(s => s.conversationVariables)
|
||||
const environmentVariables = useStore(s => s.environmentVariables)
|
||||
|
||||
const getCheckData = useCallback((data: CommonNodeType<{}>, datasets: DataSet[]) => {
|
||||
let checkData = data
|
||||
if (data.type === BlockEnum.KnowledgeRetrieval) {
|
||||
@ -183,12 +222,15 @@ export const useChecklistBeforePublish = () => {
|
||||
const {
|
||||
validNodes,
|
||||
maxDepth,
|
||||
} = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges)
|
||||
} = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges, true)
|
||||
|
||||
if (maxDepth > MAX_TREE_DEPTH) {
|
||||
notify({ type: 'error', message: t('workflow.common.maxTreeDepth', { depth: MAX_TREE_DEPTH }) })
|
||||
return false
|
||||
}
|
||||
|
||||
const allVariablesMap = transformStartNodeVariables(chatVarList, environmentVariables)
|
||||
|
||||
// Before publish, we need to fetch datasets detail, in case of the settings of datasets have been changed
|
||||
const knowledgeRetrievalNodes = nodes.filter(node => node.data.type === BlockEnum.KnowledgeRetrieval)
|
||||
const allDatasetIds = knowledgeRetrievalNodes.reduce<string[]>((acc, node) => {
|
||||
@ -239,6 +281,14 @@ export const useChecklistBeforePublish = () => {
|
||||
notify({ type: 'error', message: `[${node.data.title}] ${t('workflow.common.needConnectTip')}` })
|
||||
return false
|
||||
}
|
||||
|
||||
if (nodesExtraData[node.data.type as BlockEnum].checkVarValid) {
|
||||
const { errorMessage: varErrorMessage } = nodesExtraData[node.data.type as BlockEnum].checkVarValid(node.data, { ...allVariablesMap, ...node._parentOutputVarMap }, t)
|
||||
if (varErrorMessage?.length) {
|
||||
notify({ type: 'error', message: `[${node.data.title}] ${varErrorMessage[0]}` })
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isChatMode && !nodes.find(node => node.data.type === BlockEnum.Answer)) {
|
||||
@ -252,7 +302,7 @@ export const useChecklistBeforePublish = () => {
|
||||
}
|
||||
|
||||
return true
|
||||
}, [store, isChatMode, notify, t, buildInTools, customTools, workflowTools, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData])
|
||||
}, [store, isChatMode, notify, t, buildInTools, customTools, workflowTools, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData, chatVarList, environmentVariables])
|
||||
|
||||
return {
|
||||
handleCheckBeforePublish,
|
||||
|
||||
@ -313,7 +313,6 @@ export const useWorkflowZoom = () => {
|
||||
|
||||
export const useWorkflowUpdate = () => {
|
||||
const reactflow = useReactFlow()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
|
||||
const handleUpdateWorkflowCanvas = useCallback((payload: WorkflowDataUpdater) => {
|
||||
@ -333,32 +332,8 @@ export const useWorkflowUpdate = () => {
|
||||
setViewport(viewport)
|
||||
}, [eventEmitter, reactflow])
|
||||
|
||||
const handleRefreshWorkflowDraft = useCallback(() => {
|
||||
const {
|
||||
appId,
|
||||
setSyncWorkflowDraftHash,
|
||||
setIsSyncingWorkflowDraft,
|
||||
setEnvironmentVariables,
|
||||
setEnvSecrets,
|
||||
setConversationVariables,
|
||||
} = workflowStore.getState()
|
||||
setIsSyncingWorkflowDraft(true)
|
||||
fetchWorkflowDraft(`/apps/${appId}/workflows/draft`).then((response) => {
|
||||
handleUpdateWorkflowCanvas(response.graph as WorkflowDataUpdater)
|
||||
setSyncWorkflowDraftHash(response.hash)
|
||||
setEnvSecrets((response.environment_variables || []).filter(env => env.value_type === 'secret').reduce((acc, env) => {
|
||||
acc[env.id] = env.value
|
||||
return acc
|
||||
}, {} as Record<string, string>))
|
||||
setEnvironmentVariables(response.environment_variables?.map(env => env.value_type === 'secret' ? { ...env, value: '[__HIDDEN__]' } : env) || [])
|
||||
// #TODO chatVar sync#
|
||||
setConversationVariables(response.conversation_variables || [])
|
||||
}).finally(() => setIsSyncingWorkflowDraft(false))
|
||||
}, [handleUpdateWorkflowCanvas, workflowStore])
|
||||
|
||||
return {
|
||||
handleUpdateWorkflowCanvas,
|
||||
handleRefreshWorkflowDraft,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import { useHooksStore } from '@/app/components/workflow/hooks-store'
|
||||
|
||||
export const useWorkflowRefreshDraft = () => {
|
||||
const handleRefreshWorkflowDraft = useHooksStore(s => s.handleRefreshWorkflowDraft)
|
||||
|
||||
return {
|
||||
handleRefreshWorkflowDraft,
|
||||
}
|
||||
}
|
||||
@ -67,6 +67,9 @@ export const useWorkflowNodeStarted = () => {
|
||||
|
||||
incomeEdges.forEach((edge) => {
|
||||
const incomeNode = nodes.find(node => node.id === edge.source)!
|
||||
if (!incomeNode || !('data' in incomeNode))
|
||||
return
|
||||
|
||||
if (
|
||||
(!incomeNode.data._runningBranchId && edge.sourceHandle === 'source')
|
||||
|| (incomeNode.data._runningBranchId && edge.sourceHandle === incomeNode.data._runningBranchId)
|
||||
|
||||
@ -44,7 +44,7 @@ import {
|
||||
useShortcuts,
|
||||
useWorkflow,
|
||||
useWorkflowReadOnly,
|
||||
useWorkflowUpdate,
|
||||
useWorkflowRefreshDraft,
|
||||
} from './hooks'
|
||||
import CustomNode from './nodes'
|
||||
import CustomNoteNode from './note-node'
|
||||
@ -160,7 +160,7 @@ export const Workflow: FC<WorkflowProps> = memo(({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const { handleRefreshWorkflowDraft } = useWorkflowUpdate()
|
||||
const { handleRefreshWorkflowDraft } = useWorkflowRefreshDraft()
|
||||
const handleSyncWorkflowDraftWhenPageClose = useCallback(() => {
|
||||
if (document.visibilityState === 'hidden')
|
||||
syncWorkflowDraftWhenPageClose()
|
||||
|
||||
@ -66,6 +66,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
case FormTypeEnum.textInput: {
|
||||
const def = schema as CredentialFormSchemaTextInput
|
||||
const value = props.value[schema.variable] || schema.default
|
||||
const instanceId = schema.variable
|
||||
const onChange = (value: string) => {
|
||||
props.onChange({ ...props.value, [schema.variable]: value })
|
||||
}
|
||||
@ -77,6 +78,8 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onGenerated={handleGenerated}
|
||||
instanceId={instanceId}
|
||||
key={instanceId}
|
||||
title={renderI18nObject(schema.label)}
|
||||
headerClassName='bg-transparent px-0 text-text-secondary system-sm-semibold-uppercase'
|
||||
containerBackgroundClassName='bg-transparent'
|
||||
@ -223,7 +226,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
<Link href={
|
||||
locale === LanguagesSupported[1]
|
||||
? 'https://docs.dify.ai/zh-hans/guides/workflow/node/agent#xuan-ze-agent-ce-le'
|
||||
: 'https://docs.dify.ai/guides/workflow/node/agent#select-an-agent-strategy'
|
||||
: 'https://docs.dify.ai/en/guides/workflow/node/agent#select-an-agent-strategy'
|
||||
} className='text-text-accent-secondary' target='_blank'>
|
||||
{t('workflow.nodes.agent.learnMore')}
|
||||
</Link>
|
||||
|
||||
@ -3,6 +3,7 @@ import type { FC } from 'react'
|
||||
import Editor, { loader } from '@monaco-editor/react'
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import Base from '../base'
|
||||
import { WEB_PREFIX } from '@/config'
|
||||
import cn from '@/utils/classnames'
|
||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||
import {
|
||||
@ -14,7 +15,7 @@ import './style.css'
|
||||
import { noop } from 'lodash-es'
|
||||
|
||||
// load file from local instead of cdn https://github.com/suren-atoyan/monaco-react/issues/482
|
||||
loader.config({ paths: { vs: '/vs' } })
|
||||
loader.config({ paths: { vs: `${WEB_PREFIX}/vs` } })
|
||||
|
||||
const CODE_EDITOR_LINE_HEIGHT = 18
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ const DefaultValue = ({
|
||||
{t('workflow.nodes.common.errorHandle.defaultValue.desc')}
|
||||
|
||||
<a
|
||||
href='https://docs.dify.ai/guides/workflow/error-handling'
|
||||
href='https://docs.dify.ai/en/guides/workflow/error-handling/README'
|
||||
target='_blank'
|
||||
className='text-text-accent'
|
||||
>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user