mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 17:08:03 +08:00
Merge branch 'main' into feat/rag-pipeline
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import copy from 'copy-to-clipboard'
|
||||
@ -32,6 +32,7 @@ import cn from '@/utils/classnames'
|
||||
import ToolPicker from '@/app/components/workflow/block-selector/tool-picker'
|
||||
import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types'
|
||||
import { canFindTool } from '@/utils'
|
||||
import { useMittContextSelector } from '@/context/mitt-context'
|
||||
|
||||
type AgentToolWithMoreInfo = AgentTool & { icon: any; collection?: Collection } | null
|
||||
const AgentTools: FC = () => {
|
||||
@ -39,7 +40,6 @@ const AgentTools: FC = () => {
|
||||
const [isShowChooseTool, setIsShowChooseTool] = useState(false)
|
||||
const { modelConfig, setModelConfig, collectionList } = useContext(ConfigContext)
|
||||
const formattingChangedDispatcher = useFormattingChangedDispatcher()
|
||||
|
||||
const [currentTool, setCurrentTool] = useState<AgentToolWithMoreInfo>(null)
|
||||
const currentCollection = useMemo(() => {
|
||||
if (!currentTool) return null
|
||||
@ -61,6 +61,17 @@ const AgentTools: FC = () => {
|
||||
collection,
|
||||
}
|
||||
})
|
||||
const useSubscribe = useMittContextSelector(s => s.useSubscribe)
|
||||
const handleUpdateToolsWhenInstallToolSuccess = useCallback((installedPluginNames: string[]) => {
|
||||
const newModelConfig = produce(modelConfig, (draft) => {
|
||||
draft.agentConfig.tools.forEach((item: any) => {
|
||||
if (item.isDeleted && installedPluginNames.includes(item.provider_id))
|
||||
item.isDeleted = false
|
||||
})
|
||||
})
|
||||
setModelConfig(newModelConfig)
|
||||
}, [modelConfig, setModelConfig])
|
||||
useSubscribe('plugin:install:success', handleUpdateToolsWhenInstallToolSuccess as any)
|
||||
|
||||
const handleToolSettingChange = (value: Record<string, any>) => {
|
||||
const newModelConfig = produce(modelConfig, (draft) => {
|
||||
@ -132,7 +143,7 @@ const AgentTools: FC = () => {
|
||||
disabled={false}
|
||||
supportAddCustomTool
|
||||
onSelect={handleSelectTool}
|
||||
selectedTools={tools}
|
||||
selectedTools={tools as any}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -99,7 +99,15 @@ const DebugWithMultipleModel = () => {
|
||||
}, [twoLine, threeLine, fourLine])
|
||||
|
||||
const setShowAppConfigureFeaturesModal = useAppStore(s => s.setShowAppConfigureFeaturesModal)
|
||||
const inputsForm = modelConfig.configs.prompt_variables.filter(item => item.type !== 'api').map(item => ({ ...item, label: item.name, variable: item.key })) as InputForm[]
|
||||
const inputsForm = modelConfig.configs.prompt_variables
|
||||
.filter(item => item.type !== 'api')
|
||||
.map(item => ({
|
||||
...item,
|
||||
label: item.name,
|
||||
variable: item.key,
|
||||
hide: item.hide ?? false,
|
||||
required: item.required ?? false,
|
||||
})) as InputForm[]
|
||||
|
||||
return (
|
||||
<div className='flex h-full flex-col'>
|
||||
@ -133,6 +141,7 @@ const DebugWithMultipleModel = () => {
|
||||
{isChatMode && (
|
||||
<div className='shrink-0 px-6 pb-0'>
|
||||
<ChatInputArea
|
||||
botName='Bot'
|
||||
showFeatureBar
|
||||
showFileUpload={false}
|
||||
onFeatureBarClick={setShowAppConfigureFeaturesModal}
|
||||
|
||||
@ -79,6 +79,7 @@ import {
|
||||
} from '@/utils'
|
||||
import PluginDependency from '@/app/components/workflow/plugin-dependency'
|
||||
import { supportFunctionCall } from '@/utils/tool-call'
|
||||
import { MittProvider } from '@/context/mitt-context'
|
||||
|
||||
type PublishConfig = {
|
||||
modelConfig: ModelConfig
|
||||
@ -908,7 +909,7 @@ const Configuration: FC = () => {
|
||||
}}
|
||||
>
|
||||
<FeaturesProvider features={featuresData}>
|
||||
<>
|
||||
<MittProvider>
|
||||
<div className="flex h-full flex-col">
|
||||
<div className='relative flex h-[200px] grow pt-14'>
|
||||
{/* Header */}
|
||||
@ -1060,7 +1061,7 @@ const Configuration: FC = () => {
|
||||
/>
|
||||
)}
|
||||
<PluginDependency />
|
||||
</>
|
||||
</MittProvider>
|
||||
</FeaturesProvider>
|
||||
</ConfigContext.Provider>
|
||||
)
|
||||
|
||||
@ -29,6 +29,7 @@ import type { FileUpload } from '@/app/components/base/features/types'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
|
||||
type ChatInputAreaProps = {
|
||||
botName?: string
|
||||
showFeatureBar?: boolean
|
||||
showFileUpload?: boolean
|
||||
featureBarDisabled?: boolean
|
||||
@ -43,6 +44,7 @@ type ChatInputAreaProps = {
|
||||
disabled?: boolean
|
||||
}
|
||||
const ChatInputArea = ({
|
||||
botName,
|
||||
showFeatureBar,
|
||||
showFileUpload,
|
||||
featureBarDisabled,
|
||||
@ -192,7 +194,7 @@ const ChatInputArea = ({
|
||||
className={cn(
|
||||
'body-lg-regular w-full resize-none bg-transparent p-1 leading-6 text-text-tertiary outline-none',
|
||||
)}
|
||||
placeholder={t('common.chat.inputPlaceholder') || ''}
|
||||
placeholder={t('common.chat.inputPlaceholder', { botName }) || ''}
|
||||
autoFocus
|
||||
minRows={1}
|
||||
onResize={handleTextareaResize}
|
||||
|
||||
@ -303,6 +303,7 @@ const Chat: FC<ChatProps> = ({
|
||||
{
|
||||
!noChatInput && (
|
||||
<ChatInputArea
|
||||
botName={appData?.site.title || ''}
|
||||
disabled={inputDisabled}
|
||||
showFeatureBar={showFeatureBar}
|
||||
showFileUpload={showFileUpload}
|
||||
|
||||
@ -117,7 +117,7 @@ const Question: FC<QuestionProps> = ({
|
||||
</div>
|
||||
<div
|
||||
ref={contentRef}
|
||||
className='w-full rounded-2xl bg-[#D1E9FF]/50 px-4 py-3 text-sm text-gray-900'
|
||||
className='w-full rounded-2xl bg-background-gradient-bg-fill-chat-bubble-bg-3 px-4 py-3 text-sm text-text-primary'
|
||||
style={theme?.chatBubbleColorStyle ? CssTransform(theme.chatBubbleColorStyle) : {}}
|
||||
>
|
||||
{
|
||||
|
||||
@ -12,8 +12,7 @@ export class Theme {
|
||||
public colorPathOnHeader = 'text-text-primary-on-surface'
|
||||
public backgroundButtonDefaultColorStyle = 'backgroundColor: #1C64F2'
|
||||
public roundedBackgroundColorStyle = 'backgroundColor: rgb(245 248 255)'
|
||||
public chatBubbleColorStyle = 'backgroundColor: rgb(225 239 254)'
|
||||
public chatBubbleColor = 'rgb(225 239 254)'
|
||||
public chatBubbleColorStyle = ''
|
||||
|
||||
constructor(chatColorTheme: string | null = null, chatColorThemeInverted = false) {
|
||||
this.chatColorTheme = chatColorTheme
|
||||
@ -29,7 +28,6 @@ export class Theme {
|
||||
this.backgroundButtonDefaultColorStyle = `backgroundColor: ${this.primaryColor}; color: ${this.colorFontOnHeaderStyle};`
|
||||
this.roundedBackgroundColorStyle = `backgroundColor: ${hexToRGBA(this.primaryColor, 0.05)}`
|
||||
this.chatBubbleColorStyle = `backgroundColor: ${hexToRGBA(this.primaryColor, 0.15)}`
|
||||
this.chatBubbleColor = `${hexToRGBA(this.primaryColor, 0.15)}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
37
web/app/components/base/icons/src/public/llm/OpenaiTale.json
Normal file
37
web/app/components/base/icons/src/public/llm/OpenaiTale.json
Normal file
File diff suppressed because one or more lines are too long
20
web/app/components/base/icons/src/public/llm/OpenaiTale.tsx
Normal file
20
web/app/components/base/icons/src/public/llm/OpenaiTale.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './OpenaiTale.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 = 'OpenaiTale'
|
||||
|
||||
export default Icon
|
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './OpenaiYellow.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 = 'OpenaiYellow'
|
||||
|
||||
export default Icon
|
||||
@ -30,7 +30,9 @@ export { default as OpenaiBlue } from './OpenaiBlue'
|
||||
export { default as OpenaiGreen } from './OpenaiGreen'
|
||||
export { default as OpenaiText } from './OpenaiText'
|
||||
export { default as OpenaiTransparent } from './OpenaiTransparent'
|
||||
export { default as OpenaiTale } from './OpenaiTale'
|
||||
export { default as OpenaiViolet } from './OpenaiViolet'
|
||||
export { default as OpenaiYellow } from './OpenaiYellow'
|
||||
export { default as OpenllmText } from './OpenllmText'
|
||||
export { default as Openllm } from './Openllm'
|
||||
export { default as ReplicateText } from './ReplicateText'
|
||||
|
||||
21
web/app/components/base/markdown-blocks/audio-block.tsx
Normal file
21
web/app/components/base/markdown-blocks/audio-block.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @fileoverview AudioBlock component for rendering audio elements in Markdown.
|
||||
* Extracted from the main markdown renderer for modularity.
|
||||
* Uses the AudioGallery component to display audio players.
|
||||
*/
|
||||
import React, { memo } from 'react'
|
||||
import AudioGallery from '@/app/components/base/audio-gallery'
|
||||
|
||||
const AudioBlock: any = memo(({ node }: any) => {
|
||||
const srcs = node.children.filter((child: any) => 'properties' in child).map((child: any) => (child as any).properties.src)
|
||||
if (srcs.length === 0) {
|
||||
const src = node.properties?.src
|
||||
if (src)
|
||||
return <AudioGallery key={src} srcs={[src]} />
|
||||
return null
|
||||
}
|
||||
return <AudioGallery key={srcs.join()} srcs={srcs} />
|
||||
})
|
||||
AudioBlock.displayName = 'AudioBlock'
|
||||
|
||||
export default AudioBlock
|
||||
@ -1,34 +1,19 @@
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { memo, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import ReactEcharts from 'echarts-for-react'
|
||||
import 'katex/dist/katex.min.css'
|
||||
import RemarkMath from 'remark-math'
|
||||
import RemarkBreaks from 'remark-breaks'
|
||||
import RehypeKatex from 'rehype-katex'
|
||||
import RemarkGfm from 'remark-gfm'
|
||||
import RehypeRaw from 'rehype-raw'
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter'
|
||||
import {
|
||||
atelierHeathDark,
|
||||
atelierHeathLight,
|
||||
} from 'react-syntax-highlighter/dist/esm/styles/hljs'
|
||||
import { Component, memo, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { flow } from 'lodash-es'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import CopyIcon from '@/app/components/base/copy-icon'
|
||||
import SVGBtn from '@/app/components/base/svg'
|
||||
import Flowchart from '@/app/components/base/mermaid'
|
||||
import ImageGallery from '@/app/components/base/image-gallery'
|
||||
import { useChatContext } from '@/app/components/base/chat/chat/context'
|
||||
import VideoGallery from '@/app/components/base/video-gallery'
|
||||
import AudioGallery from '@/app/components/base/audio-gallery'
|
||||
import MarkdownButton from '@/app/components/base/markdown-blocks/button'
|
||||
import MarkdownForm from '@/app/components/base/markdown-blocks/form'
|
||||
import MarkdownMusic from '@/app/components/base/markdown-blocks/music'
|
||||
import ThinkBlock from '@/app/components/base/markdown-blocks/think-block'
|
||||
import { Theme } from '@/types/app'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import cn from '@/utils/classnames'
|
||||
import SVGRenderer from './svg-gallery'
|
||||
import SVGRenderer from '../svg-gallery' // Assumes svg-gallery.tsx is in /base directory
|
||||
import MarkdownMusic from '@/app/components/base/markdown-blocks/music'
|
||||
import ErrorBoundary from '@/app/components/base/markdown/error-boundary'
|
||||
|
||||
// Available language https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/AVAILABLE_LANGUAGES_HLJS.MD
|
||||
const capitalizationLanguageNameMap: Record<string, string> = {
|
||||
@ -64,50 +49,6 @@ const getCorrectCapitalizationLanguageName = (language: string) => {
|
||||
return language.charAt(0).toUpperCase() + language.substring(1)
|
||||
}
|
||||
|
||||
const preprocessLaTeX = (content: string) => {
|
||||
if (typeof content !== 'string')
|
||||
return content
|
||||
|
||||
const codeBlockRegex = /```[\s\S]*?```/g
|
||||
const codeBlocks = content.match(codeBlockRegex) || []
|
||||
let processedContent = content.replace(codeBlockRegex, 'CODE_BLOCK_PLACEHOLDER')
|
||||
|
||||
processedContent = flow([
|
||||
(str: string) => str.replace(/\\\[(.*?)\\\]/g, (_, equation) => `$$${equation}$$`),
|
||||
(str: string) => str.replace(/\\\[([\s\S]*?)\\\]/g, (_, equation) => `$$${equation}$$`),
|
||||
(str: string) => str.replace(/\\\((.*?)\\\)/g, (_, equation) => `$$${equation}$$`),
|
||||
(str: string) => str.replace(/(^|[^\\])\$(.+?)\$/g, (_, prefix, equation) => `${prefix}$${equation}$`),
|
||||
])(processedContent)
|
||||
|
||||
codeBlocks.forEach((block) => {
|
||||
processedContent = processedContent.replace('CODE_BLOCK_PLACEHOLDER', block)
|
||||
})
|
||||
|
||||
return processedContent
|
||||
}
|
||||
|
||||
const preprocessThinkTag = (content: string) => {
|
||||
const thinkOpenTagRegex = /<think>\n/g
|
||||
const thinkCloseTagRegex = /\n<\/think>/g
|
||||
return flow([
|
||||
(str: string) => str.replace(thinkOpenTagRegex, '<details data-think=true>\n'),
|
||||
(str: string) => str.replace(thinkCloseTagRegex, '\n[ENDTHINKFLAG]</details>'),
|
||||
])(content)
|
||||
}
|
||||
|
||||
export function PreCode(props: { children: any }) {
|
||||
const ref = useRef<HTMLPreElement>(null)
|
||||
|
||||
return (
|
||||
<pre ref={ref}>
|
||||
<span
|
||||
className="copy-code-button"
|
||||
></span>
|
||||
{props.children}
|
||||
</pre>
|
||||
)
|
||||
}
|
||||
|
||||
// **Add code block
|
||||
// Avoid error #185 (Maximum update depth exceeded.
|
||||
// This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate.
|
||||
@ -444,150 +385,4 @@ const CodeBlock: any = memo(({ inline, className, children = '', ...props }: any
|
||||
})
|
||||
CodeBlock.displayName = 'CodeBlock'
|
||||
|
||||
const VideoBlock: any = memo(({ node }: any) => {
|
||||
const srcs = node.children.filter((child: any) => 'properties' in child).map((child: any) => (child as any).properties.src)
|
||||
if (srcs.length === 0) {
|
||||
const src = node.properties?.src
|
||||
if (src)
|
||||
return <VideoGallery key={src} srcs={[src]} />
|
||||
return null
|
||||
}
|
||||
return <VideoGallery key={srcs.join()} srcs={srcs} />
|
||||
})
|
||||
VideoBlock.displayName = 'VideoBlock'
|
||||
|
||||
const AudioBlock: any = memo(({ node }: any) => {
|
||||
const srcs = node.children.filter((child: any) => 'properties' in child).map((child: any) => (child as any).properties.src)
|
||||
if (srcs.length === 0) {
|
||||
const src = node.properties?.src
|
||||
if (src)
|
||||
return <AudioGallery key={src} srcs={[src]} />
|
||||
return null
|
||||
}
|
||||
return <AudioGallery key={srcs.join()} srcs={srcs} />
|
||||
})
|
||||
AudioBlock.displayName = 'AudioBlock'
|
||||
|
||||
const ScriptBlock = memo(({ node }: any) => {
|
||||
const scriptContent = node.children[0]?.value || ''
|
||||
return `<script>${scriptContent}</script>`
|
||||
})
|
||||
ScriptBlock.displayName = 'ScriptBlock'
|
||||
|
||||
const Paragraph = (paragraph: any) => {
|
||||
const { node }: any = paragraph
|
||||
const children_node = node.children
|
||||
if (children_node && children_node[0] && 'tagName' in children_node[0] && children_node[0].tagName === 'img') {
|
||||
return (
|
||||
<div className="markdown-img-wrapper">
|
||||
<ImageGallery srcs={[children_node[0].properties.src]} />
|
||||
{
|
||||
Array.isArray(paragraph.children) && paragraph.children.length > 1 && (
|
||||
<div className="mt-2">{paragraph.children.slice(1)}</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return <p>{paragraph.children}</p>
|
||||
}
|
||||
|
||||
const Img = ({ src }: any) => {
|
||||
return <div className="markdown-img-wrapper"><ImageGallery srcs={[src]} /></div>
|
||||
}
|
||||
|
||||
const Link = ({ node, children, ...props }: any) => {
|
||||
if (node.properties?.href && node.properties.href?.toString().startsWith('abbr')) {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const { onSend } = useChatContext()
|
||||
const hidden_text = decodeURIComponent(node.properties.href.toString().split('abbr:')[1])
|
||||
|
||||
return <abbr className="cursor-pointer underline !decoration-primary-700 decoration-dashed" onClick={() => onSend?.(hidden_text)} title={node.children[0]?.value || ''}>{node.children[0]?.value || ''}</abbr>
|
||||
}
|
||||
else {
|
||||
return <a {...props} target="_blank" className="cursor-pointer underline !decoration-primary-700 decoration-dashed">{children || 'Download'}</a>
|
||||
}
|
||||
}
|
||||
|
||||
export function Markdown(props: { content: string; className?: string; customDisallowedElements?: string[] }) {
|
||||
const latexContent = flow([
|
||||
preprocessThinkTag,
|
||||
preprocessLaTeX,
|
||||
])(props.content)
|
||||
|
||||
return (
|
||||
<div className={cn('markdown-body', '!text-text-primary', props.className)}>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[
|
||||
RemarkGfm,
|
||||
[RemarkMath, { singleDollarTextMath: false }],
|
||||
RemarkBreaks,
|
||||
]}
|
||||
rehypePlugins={[
|
||||
RehypeKatex,
|
||||
RehypeRaw as any,
|
||||
// The Rehype plug-in is used to remove the ref attribute of an element
|
||||
() => {
|
||||
return (tree) => {
|
||||
const iterate = (node: any) => {
|
||||
if (node.type === 'element' && node.properties?.ref)
|
||||
delete node.properties.ref
|
||||
|
||||
if (node.type === 'element' && !/^[a-z][a-z0-9]*$/i.test(node.tagName)) {
|
||||
node.type = 'text'
|
||||
node.value = `<${node.tagName}`
|
||||
}
|
||||
|
||||
if (node.children)
|
||||
node.children.forEach(iterate)
|
||||
}
|
||||
tree.children.forEach(iterate)
|
||||
}
|
||||
},
|
||||
]}
|
||||
disallowedElements={['iframe', 'head', 'html', 'meta', 'link', 'style', 'body', ...(props.customDisallowedElements || [])]}
|
||||
components={{
|
||||
code: CodeBlock,
|
||||
img: Img,
|
||||
video: VideoBlock,
|
||||
audio: AudioBlock,
|
||||
a: Link,
|
||||
p: Paragraph,
|
||||
button: MarkdownButton,
|
||||
form: MarkdownForm,
|
||||
script: ScriptBlock as any,
|
||||
details: ThinkBlock,
|
||||
}}
|
||||
>
|
||||
{/* Markdown detect has problem. */}
|
||||
{latexContent}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// **Add an ECharts runtime error handler
|
||||
// Avoid error #7832 (Crash when ECharts accesses undefined objects)
|
||||
// This can happen when a component attempts to access an undefined object that references an unregistered map, causing the program to crash.
|
||||
|
||||
export default class ErrorBoundary extends Component {
|
||||
constructor(props: any) {
|
||||
super(props)
|
||||
this.state = { hasError: false }
|
||||
}
|
||||
|
||||
componentDidCatch(error: any, errorInfo: any) {
|
||||
this.setState({ hasError: true })
|
||||
console.error(error, errorInfo)
|
||||
}
|
||||
|
||||
render() {
|
||||
// eslint-disable-next-line ts/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
if (this.state.hasError)
|
||||
return <div>Oops! An error occurred. This could be due to an ECharts runtime error or invalid SVG content. <br />(see the browser console for more information)</div>
|
||||
// eslint-disable-next-line ts/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
export default CodeBlock
|
||||
13
web/app/components/base/markdown-blocks/img.tsx
Normal file
13
web/app/components/base/markdown-blocks/img.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @fileoverview Img component for rendering <img> tags in Markdown.
|
||||
* Extracted from the main markdown renderer for modularity.
|
||||
* Uses the ImageGallery component to display images.
|
||||
*/
|
||||
import React from 'react'
|
||||
import ImageGallery from '@/app/components/base/image-gallery'
|
||||
|
||||
const Img = ({ src }: any) => {
|
||||
return <div className="markdown-img-wrapper"><ImageGallery srcs={[src]} /></div>
|
||||
}
|
||||
|
||||
export default Img
|
||||
18
web/app/components/base/markdown-blocks/index.ts
Normal file
18
web/app/components/base/markdown-blocks/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @fileoverview Barrel file for all markdown block components.
|
||||
* This allows for cleaner imports in other parts of the application.
|
||||
*/
|
||||
|
||||
export { default as AudioBlock } from './audio-block'
|
||||
export { default as CodeBlock } from './code-block'
|
||||
export { default as Img } from './img'
|
||||
export { default as Link } from './link'
|
||||
export { default as Paragraph } from './paragraph'
|
||||
export { default as PreCode } from './pre-code'
|
||||
export { default as ScriptBlock } from './script-block'
|
||||
export { default as VideoBlock } from './video-block'
|
||||
|
||||
// Assuming these are also standalone components in this directory intended for Markdown rendering
|
||||
export { default as MarkdownButton } from './button'
|
||||
export { default as MarkdownForm } from './form'
|
||||
export { default as ThinkBlock } from './think-block'
|
||||
21
web/app/components/base/markdown-blocks/link.tsx
Normal file
21
web/app/components/base/markdown-blocks/link.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @fileoverview Link component for rendering <a> tags in Markdown.
|
||||
* Extracted from the main markdown renderer for modularity.
|
||||
* Handles special rendering for "abbr:" type links for interactive chat actions.
|
||||
*/
|
||||
import React from 'react'
|
||||
import { useChatContext } from '@/app/components/base/chat/chat/context'
|
||||
|
||||
const Link = ({ node, children, ...props }: any) => {
|
||||
const { onSend } = useChatContext()
|
||||
if (node.properties?.href && node.properties.href?.toString().startsWith('abbr')) {
|
||||
const hidden_text = decodeURIComponent(node.properties.href.toString().split('abbr:')[1])
|
||||
|
||||
return <abbr className="cursor-pointer underline !decoration-primary-700 decoration-dashed" onClick={() => onSend?.(hidden_text)} title={node.children[0]?.value || ''}>{node.children[0]?.value || ''}</abbr>
|
||||
}
|
||||
else {
|
||||
return <a {...props} target="_blank" className="cursor-pointer underline !decoration-primary-700 decoration-dashed">{children || 'Download'}</a>
|
||||
}
|
||||
}
|
||||
|
||||
export default Link
|
||||
27
web/app/components/base/markdown-blocks/paragraph.tsx
Normal file
27
web/app/components/base/markdown-blocks/paragraph.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @fileoverview Paragraph component for rendering <p> tags in Markdown.
|
||||
* Extracted from the main markdown renderer for modularity.
|
||||
* Handles special rendering for paragraphs that directly contain an image.
|
||||
*/
|
||||
import React from 'react'
|
||||
import ImageGallery from '@/app/components/base/image-gallery'
|
||||
|
||||
const Paragraph = (paragraph: any) => {
|
||||
const { node }: any = paragraph
|
||||
const children_node = node.children
|
||||
if (children_node && children_node[0] && 'tagName' in children_node[0] && children_node[0].tagName === 'img') {
|
||||
return (
|
||||
<div className="markdown-img-wrapper">
|
||||
<ImageGallery srcs={[children_node[0].properties.src]} />
|
||||
{
|
||||
Array.isArray(paragraph.children) && paragraph.children.length > 1 && (
|
||||
<div className="mt-2">{paragraph.children.slice(1)}</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return <p>{paragraph.children}</p>
|
||||
}
|
||||
|
||||
export default Paragraph
|
||||
21
web/app/components/base/markdown-blocks/pre-code.tsx
Normal file
21
web/app/components/base/markdown-blocks/pre-code.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @fileoverview PreCode component for rendering <pre> tags in Markdown.
|
||||
* Extracted from the main markdown renderer for modularity.
|
||||
* This is a simple wrapper around the HTML <pre> element.
|
||||
*/
|
||||
import React, { useRef } from 'react'
|
||||
|
||||
function PreCode(props: { children: any }) {
|
||||
const ref = useRef<HTMLPreElement>(null)
|
||||
|
||||
return (
|
||||
<pre ref={ref}>
|
||||
<span
|
||||
className="copy-code-button"
|
||||
></span>
|
||||
{props.children}
|
||||
</pre>
|
||||
)
|
||||
}
|
||||
|
||||
export default PreCode
|
||||
15
web/app/components/base/markdown-blocks/script-block.tsx
Normal file
15
web/app/components/base/markdown-blocks/script-block.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @fileoverview ScriptBlock component for handling <script> tags in Markdown.
|
||||
* Extracted from the main markdown renderer for modularity.
|
||||
* Note: Current implementation returns the script tag as a string, which might not execute as expected in React.
|
||||
* This behavior is preserved from the original implementation and may need review for security and functionality.
|
||||
*/
|
||||
import { memo } from 'react'
|
||||
|
||||
const ScriptBlock = memo(({ node }: any) => {
|
||||
const scriptContent = node.children[0]?.value || ''
|
||||
return `<script>${scriptContent}</script>`
|
||||
})
|
||||
ScriptBlock.displayName = 'ScriptBlock'
|
||||
|
||||
export default ScriptBlock
|
||||
21
web/app/components/base/markdown-blocks/video-block.tsx
Normal file
21
web/app/components/base/markdown-blocks/video-block.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @fileoverview VideoBlock component for rendering video elements in Markdown.
|
||||
* Extracted from the main markdown renderer for modularity.
|
||||
* Uses the VideoGallery component to display videos.
|
||||
*/
|
||||
import React, { memo } from 'react'
|
||||
import VideoGallery from '@/app/components/base/video-gallery'
|
||||
|
||||
const VideoBlock: any = memo(({ node }: any) => {
|
||||
const srcs = node.children.filter((child: any) => 'properties' in child).map((child: any) => (child as any).properties.src)
|
||||
if (srcs.length === 0) {
|
||||
const src = node.properties?.src
|
||||
if (src)
|
||||
return <VideoGallery key={src} srcs={[src]} />
|
||||
return null
|
||||
}
|
||||
return <VideoGallery key={srcs.join()} srcs={srcs} />
|
||||
})
|
||||
VideoBlock.displayName = 'VideoBlock'
|
||||
|
||||
export default VideoBlock
|
||||
33
web/app/components/base/markdown/error-boundary.tsx
Normal file
33
web/app/components/base/markdown/error-boundary.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @fileoverview ErrorBoundary component for React.
|
||||
* This component was extracted from the main markdown renderer.
|
||||
* It catches JavaScript errors anywhere in its child component tree,
|
||||
* logs those errors, and displays a fallback UI instead of the crashed component tree.
|
||||
* Primarily used around complex rendering logic like ECharts or SVG within Markdown.
|
||||
*/
|
||||
import React, { Component } from 'react'
|
||||
// **Add an ECharts runtime error handler
|
||||
// Avoid error #7832 (Crash when ECharts accesses undefined objects)
|
||||
// This can happen when a component attempts to access an undefined object that references an unregistered map, causing the program to crash.
|
||||
|
||||
export default class ErrorBoundary extends Component {
|
||||
constructor(props: any) {
|
||||
super(props)
|
||||
this.state = { hasError: false }
|
||||
}
|
||||
|
||||
componentDidCatch(error: any, errorInfo: any) {
|
||||
this.setState({ hasError: true })
|
||||
console.error(error, errorInfo)
|
||||
}
|
||||
|
||||
render() {
|
||||
// eslint-disable-next-line ts/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
if (this.state.hasError)
|
||||
return <div>Oops! An error occurred. This could be due to an ECharts runtime error or invalid SVG content. <br />(see the browser console for more information)</div>
|
||||
// eslint-disable-next-line ts/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
87
web/app/components/base/markdown/index.tsx
Normal file
87
web/app/components/base/markdown/index.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import 'katex/dist/katex.min.css'
|
||||
import RemarkMath from 'remark-math'
|
||||
import RemarkBreaks from 'remark-breaks'
|
||||
import RehypeKatex from 'rehype-katex'
|
||||
import RemarkGfm from 'remark-gfm'
|
||||
import RehypeRaw from 'rehype-raw'
|
||||
import { flow } from 'lodash-es'
|
||||
import cn from '@/utils/classnames'
|
||||
import { preprocessLaTeX, preprocessThinkTag } from './markdown-utils'
|
||||
import {
|
||||
AudioBlock,
|
||||
CodeBlock,
|
||||
Img,
|
||||
Link,
|
||||
MarkdownButton,
|
||||
MarkdownForm,
|
||||
Paragraph,
|
||||
ScriptBlock,
|
||||
ThinkBlock,
|
||||
VideoBlock,
|
||||
} from '@/app/components/base/markdown-blocks'
|
||||
|
||||
/**
|
||||
* @fileoverview Main Markdown rendering component.
|
||||
* This file was refactored to extract individual block renderers and utility functions
|
||||
* into separate modules for better organization and maintainability as of [Date of refactor].
|
||||
* Further refactoring candidates (custom block components not fitting general categories)
|
||||
* are noted in their respective files if applicable.
|
||||
*/
|
||||
|
||||
export function Markdown(props: { content: string; className?: string; customDisallowedElements?: string[] }) {
|
||||
const latexContent = flow([
|
||||
preprocessThinkTag,
|
||||
preprocessLaTeX,
|
||||
])(props.content)
|
||||
|
||||
return (
|
||||
<div className={cn('markdown-body', '!text-text-primary', props.className)}>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[
|
||||
RemarkGfm,
|
||||
[RemarkMath, { singleDollarTextMath: false }],
|
||||
RemarkBreaks,
|
||||
]}
|
||||
rehypePlugins={[
|
||||
RehypeKatex,
|
||||
RehypeRaw as any,
|
||||
// The Rehype plug-in is used to remove the ref attribute of an element
|
||||
() => {
|
||||
return (tree: any) => {
|
||||
const iterate = (node: any) => {
|
||||
if (node.type === 'element' && node.properties?.ref)
|
||||
delete node.properties.ref
|
||||
|
||||
if (node.type === 'element' && !/^[a-z][a-z0-9]*$/i.test(node.tagName)) {
|
||||
node.type = 'text'
|
||||
node.value = `<${node.tagName}`
|
||||
}
|
||||
|
||||
if (node.children)
|
||||
node.children.forEach(iterate)
|
||||
}
|
||||
tree.children.forEach(iterate)
|
||||
}
|
||||
},
|
||||
]}
|
||||
disallowedElements={['iframe', 'head', 'html', 'meta', 'link', 'style', 'body', ...(props.customDisallowedElements || [])]}
|
||||
components={{
|
||||
code: CodeBlock,
|
||||
img: Img,
|
||||
video: VideoBlock,
|
||||
audio: AudioBlock,
|
||||
a: Link,
|
||||
p: Paragraph,
|
||||
button: MarkdownButton,
|
||||
form: MarkdownForm,
|
||||
script: ScriptBlock as any,
|
||||
details: ThinkBlock,
|
||||
}}
|
||||
>
|
||||
{/* Markdown detect has problem. */}
|
||||
{latexContent}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
37
web/app/components/base/markdown/markdown-utils.ts
Normal file
37
web/app/components/base/markdown/markdown-utils.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @fileoverview Utility functions for preprocessing Markdown content.
|
||||
* These functions were extracted from the main markdown renderer for better separation of concerns.
|
||||
* Includes preprocessing for LaTeX and custom "think" tags.
|
||||
*/
|
||||
import { flow } from 'lodash-es'
|
||||
|
||||
export const preprocessLaTeX = (content: string) => {
|
||||
if (typeof content !== 'string')
|
||||
return content
|
||||
|
||||
const codeBlockRegex = /```[\s\S]*?```/g
|
||||
const codeBlocks = content.match(codeBlockRegex) || []
|
||||
let processedContent = content.replace(codeBlockRegex, 'CODE_BLOCK_PLACEHOLDER')
|
||||
|
||||
processedContent = flow([
|
||||
(str: string) => str.replace(/\\\[(.*?)\\\]/g, (_, equation) => `$$${equation}$$`),
|
||||
(str: string) => str.replace(/\\\[([\s\S]*?)\\\]/g, (_, equation) => `$$${equation}$$`),
|
||||
(str: string) => str.replace(/\\\((.*?)\\\)/g, (_, equation) => `$$${equation}$$`),
|
||||
(str: string) => str.replace(/(^|[^\\])\$(.+?)\$/g, (_, prefix, equation) => `${prefix}$${equation}$`),
|
||||
])(processedContent)
|
||||
|
||||
codeBlocks.forEach((block) => {
|
||||
processedContent = processedContent.replace('CODE_BLOCK_PLACEHOLDER', block)
|
||||
})
|
||||
|
||||
return processedContent
|
||||
}
|
||||
|
||||
export const preprocessThinkTag = (content: string) => {
|
||||
const thinkOpenTagRegex = /<think>\n/g
|
||||
const thinkCloseTagRegex = /\n<\/think>/g
|
||||
return flow([
|
||||
(str: string) => str.replace(thinkOpenTagRegex, '<details data-think=true>\n'),
|
||||
(str: string) => str.replace(thinkCloseTagRegex, '\n[ENDTHINKFLAG]</details>'),
|
||||
])(content)
|
||||
}
|
||||
@ -130,7 +130,7 @@ const CustomWebAppBrand = () => {
|
||||
<div className='system-xs-regular text-text-tertiary'>{t('custom.webapp.changeLogoTip')}</div>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
{(uploadDisabled || (!webappLogo && !webappBrandRemoved)) && (
|
||||
{(!uploadDisabled && webappLogo && !webappBrandRemoved) && (
|
||||
<>
|
||||
<Button
|
||||
variant='ghost'
|
||||
|
||||
@ -866,10 +866,10 @@ const StepTwo = ({
|
||||
<>
|
||||
<CustomDialog show={isQAConfirmDialogOpen} onClose={() => setIsQAConfirmDialogOpen(false)} className='w-[432px]'>
|
||||
<header className='mb-4 pt-6'>
|
||||
<h2 className='text-lg font-semibold'>
|
||||
<h2 className='text-lg font-semibold text-text-primary'>
|
||||
{t('datasetCreation.stepTwo.qaSwitchHighQualityTipTitle')}
|
||||
</h2>
|
||||
<p className='mt-2 text-sm font-normal'>
|
||||
<p className='mt-2 text-sm font-normal text-text-secondary'>
|
||||
{t('datasetCreation.stepTwo.qaSwitchHighQualityTipContent')}
|
||||
</p>
|
||||
</header>
|
||||
@ -929,7 +929,7 @@ const StepTwo = ({
|
||||
</div>
|
||||
)}
|
||||
{hasSetIndexType && indexType === IndexingType.ECONOMICAL && (
|
||||
<div className='system-xs-medium mt-2'>
|
||||
<div className='system-xs-medium mt-2 text-text-tertiary'>
|
||||
{t('datasetCreation.stepTwo.indexSettingTip')}
|
||||
<Link className='text-text-accent' href={`/datasets/${datasetId}/settings`}>{t('datasetCreation.stepTwo.datasetSettingLink')}</Link>
|
||||
</div>
|
||||
|
||||
@ -50,20 +50,20 @@ const CSVDownload: FC<{ docForm: ChunkingMode }> = ({ docForm }) => {
|
||||
|
||||
return (
|
||||
<div className='mt-6'>
|
||||
<div className='text-sm font-medium text-gray-900'>{t('share.generation.csvStructureTitle')}</div>
|
||||
<div className='text-sm font-medium text-text-primary'>{t('share.generation.csvStructureTitle')}</div>
|
||||
<div className='mt-2 max-h-[500px] overflow-auto'>
|
||||
{docForm === ChunkingMode.qa && (
|
||||
<table className='w-full table-fixed border-separate border-spacing-0 rounded-lg border border-gray-200 text-xs'>
|
||||
<thead className='text-gray-500'>
|
||||
<table className='w-full table-fixed border-separate border-spacing-0 rounded-lg border border-divider-subtle text-xs'>
|
||||
<thead className='text-text-secondary'>
|
||||
<tr>
|
||||
<td className='h-9 border-b border-gray-200 pl-3 pr-2'>{t('datasetDocuments.list.batchModal.question')}</td>
|
||||
<td className='h-9 border-b border-gray-200 pl-3 pr-2'>{t('datasetDocuments.list.batchModal.answer')}</td>
|
||||
<td className='h-9 border-b border-divider-subtle pl-3 pr-2'>{t('datasetDocuments.list.batchModal.question')}</td>
|
||||
<td className='h-9 border-b border-divider-subtle pl-3 pr-2'>{t('datasetDocuments.list.batchModal.answer')}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className='text-gray-700'>
|
||||
<tbody className='text-text-tertiary'>
|
||||
<tr>
|
||||
<td className='h-9 border-b border-gray-100 pl-3 pr-2 text-[13px]'>{t('datasetDocuments.list.batchModal.question')} 1</td>
|
||||
<td className='h-9 border-b border-gray-100 pl-3 pr-2 text-[13px]'>{t('datasetDocuments.list.batchModal.answer')} 1</td>
|
||||
<td className='h-9 border-b border-divider-subtle pl-3 pr-2 text-[13px]'>{t('datasetDocuments.list.batchModal.question')} 1</td>
|
||||
<td className='h-9 border-b border-divider-subtle pl-3 pr-2 text-[13px]'>{t('datasetDocuments.list.batchModal.answer')} 1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='h-9 pl-3 pr-2 text-[13px]'>{t('datasetDocuments.list.batchModal.question')} 2</td>
|
||||
@ -73,15 +73,15 @@ const CSVDownload: FC<{ docForm: ChunkingMode }> = ({ docForm }) => {
|
||||
</table>
|
||||
)}
|
||||
{docForm === ChunkingMode.text && (
|
||||
<table className='w-full table-fixed border-separate border-spacing-0 rounded-lg border border-gray-200 text-xs'>
|
||||
<thead className='text-gray-500'>
|
||||
<table className='w-full table-fixed border-separate border-spacing-0 rounded-lg border border-divider-subtle text-xs'>
|
||||
<thead className='text-text-secondary'>
|
||||
<tr>
|
||||
<td className='h-9 border-b border-gray-200 pl-3 pr-2'>{t('datasetDocuments.list.batchModal.contentTitle')}</td>
|
||||
<td className='h-9 border-b border-divider-subtle pl-3 pr-2'>{t('datasetDocuments.list.batchModal.contentTitle')}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className='text-gray-700'>
|
||||
<tbody className='text-text-tertiary'>
|
||||
<tr>
|
||||
<td className='h-9 border-b border-gray-100 pl-3 pr-2 text-[13px]'>{t('datasetDocuments.list.batchModal.content')} 1</td>
|
||||
<td className='h-9 border-b border-divider-subtle pl-3 pr-2 text-[13px]'>{t('datasetDocuments.list.batchModal.content')} 1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='h-9 pl-3 pr-2 text-[13px]'>{t('datasetDocuments.list.batchModal.content')} 2</td>
|
||||
|
||||
@ -93,29 +93,29 @@ const CSVUploader: FC<Props> = ({
|
||||
/>
|
||||
<div ref={dropRef}>
|
||||
{!file && (
|
||||
<div className={cn('flex h-20 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-20 items-center rounded-xl border border-dashed border-components-panel-border bg-components-panel-bg-blur text-sm font-normal', dragging && 'border border-divider-subtle bg-components-panel-on-panel-item-bg-hover')}>
|
||||
<div className='flex w-full items-center justify-center space-x-2'>
|
||||
<CSVIcon className="shrink-0" />
|
||||
<div className='text-gray-500'>
|
||||
<div className='text-text-secondary'>
|
||||
{t('datasetDocuments.list.batchModal.csvUploadTitle')}
|
||||
<span className='cursor-pointer text-primary-400' onClick={selectHandle}>{t('datasetDocuments.list.batchModal.browse')}</span>
|
||||
<span className='cursor-pointer 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 h-20 items-center rounded-xl border border-gray-200 bg-gray-50 px-6 text-sm font-normal', 'hover:border-[#B2CCFF] hover:bg-[#F5F8FF]')}>
|
||||
<div className={cn('group flex h-20 items-center rounded-xl border border-components-panel-border bg-components-panel-bg-blur px-6 text-sm font-normal', 'hover:border-divider-subtle hover:bg-components-panel-on-panel-item-bg-hover')}>
|
||||
<CSVIcon className="shrink-0" />
|
||||
<div className='ml-2 flex w-0 grow'>
|
||||
<span className='max-w-[calc(100%_-_30px)] overflow-hidden text-ellipsis whitespace-nowrap text-gray-800'>{file.name.replace(/.csv$/, '')}</span>
|
||||
<span className='shrink-0 text-gray-500'>.csv</span>
|
||||
<span className='max-w-[calc(100%_-_30px)] overflow-hidden text-ellipsis whitespace-nowrap text-text-primary'>{file.name.replace(/.csv$/, '')}</span>
|
||||
<span className='shrink-0 text-text-secondary'>.csv</span>
|
||||
</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='mx-2 h-4 w-px bg-text-secondary' />
|
||||
<div className='cursor-pointer p-2' onClick={removeFile}>
|
||||
<RiDeleteBinLine className='h-4 w-4 text-gray-500' />
|
||||
<RiDeleteBinLine className='h-4 w-4 text-text-secondary' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -41,9 +41,9 @@ const BatchModal: FC<IBatchModalProps> = ({
|
||||
|
||||
return (
|
||||
<Modal isShow={isShow} onClose={noop} className='!max-w-[520px] !rounded-xl px-8 py-6'>
|
||||
<div className='relative pb-1 text-xl font-medium leading-[30px] text-gray-900'>{t('datasetDocuments.list.batchModal.title')}</div>
|
||||
<div className='relative pb-1 text-xl font-medium leading-[30px] text-text-primary'>{t('datasetDocuments.list.batchModal.title')}</div>
|
||||
<div className='absolute right-4 top-4 cursor-pointer p-2' onClick={onCancel}>
|
||||
<RiCloseLine className='h-4 w-4 text-gray-500' />
|
||||
<RiCloseLine className='h-4 w-4 text-text-secondary' />
|
||||
</div>
|
||||
<CSVUploader
|
||||
file={currentCSV}
|
||||
|
||||
@ -1298,6 +1298,76 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}'
|
||||
method='GET'
|
||||
title='Get a Chunk Details in a Document'
|
||||
name='#view_document_chunk'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Get details of a specific document segment in the specified knowledge base
|
||||
|
||||
### Path
|
||||
<Properties>
|
||||
<Property name='dataset_id' type='string' key='dataset_id'>
|
||||
Knowledge Base ID
|
||||
</Property>
|
||||
<Property name='document_id' type='string' key='document_id'>
|
||||
Document ID
|
||||
</Property>
|
||||
<Property name='segment_id' type='string' key='segment_id'>
|
||||
Segment ID
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}"
|
||||
targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \
|
||||
--header 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": {
|
||||
"id": "chunk_id",
|
||||
"position": 2,
|
||||
"document_id": "document_id",
|
||||
"content": "Segment content text",
|
||||
"sign_content": "Signature content text",
|
||||
"answer": "Answer content (if in Q&A mode)",
|
||||
"word_count": 470,
|
||||
"tokens": 382,
|
||||
"keywords": ["keyword1", "keyword2"],
|
||||
"index_node_id": "index_node_id",
|
||||
"index_node_hash": "index_node_hash",
|
||||
"hit_count": 0,
|
||||
"enabled": true,
|
||||
"status": "completed",
|
||||
"created_by": "creator_id",
|
||||
"created_at": creation_timestamp,
|
||||
"updated_at": update_timestamp,
|
||||
"indexing_at": indexing_timestamp,
|
||||
"completed_at": completion_timestamp,
|
||||
"error": null,
|
||||
"child_chunks": []
|
||||
},
|
||||
"doc_form": "text_model"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}'
|
||||
method='DELETE'
|
||||
@ -1771,20 +1841,45 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
Query keyword
|
||||
</Property>
|
||||
<Property name='retrieval_model' type='object' key='retrieval_model'>
|
||||
Retrieval model (optional, if not filled, it will be recalled according to the default method)
|
||||
- <code>search_method</code> (text) Search method: One of the following four keywords is required
|
||||
- <code>keyword_search</code> Keyword search
|
||||
- <code>semantic_search</code> Semantic search
|
||||
- <code>full_text_search</code> Full-text search
|
||||
- <code>hybrid_search</code> Hybrid search
|
||||
- <code>reranking_enable</code> (bool) Whether to enable reranking, required if the search mode is semantic_search or hybrid_search (optional)
|
||||
- <code>reranking_mode</code> (object) Rerank model configuration, required if reranking is enabled
|
||||
- <code>reranking_provider_name</code> (string) Rerank model provider
|
||||
- <code>reranking_model_name</code> (string) Rerank model name
|
||||
- <code>weights</code> (float) Semantic search weight setting in hybrid search mode
|
||||
- <code>top_k</code> (integer) Number of results to return (optional)
|
||||
- <code>score_threshold_enabled</code> (bool) Whether to enable score threshold
|
||||
- <code>score_threshold</code> (float) Score threshold
|
||||
Retrieval parameters (optional, if not filled, it will be recalled according to the default method)
|
||||
- <code>search_method</code> (text) Search method: One of the following four keywords is required
|
||||
- <code>keyword_search</code> Keyword search
|
||||
- <code>semantic_search</code> Semantic search
|
||||
- <code>full_text_search</code> Full-text search
|
||||
- <code>hybrid_search</code> Hybrid search
|
||||
- <code>reranking_enable</code> (bool) Whether to enable reranking, required if the search mode is semantic_search or hybrid_search (optional)
|
||||
- <code>reranking_mode</code> (object) Rerank model configuration, required if reranking is enabled
|
||||
- <code>reranking_provider_name</code> (string) Rerank model provider
|
||||
- <code>reranking_model_name</code> (string) Rerank model name
|
||||
- <code>weights</code> (float) Semantic search weight setting in hybrid search mode
|
||||
- <code>top_k</code> (integer) Number of results to return (optional)
|
||||
- <code>score_threshold_enabled</code> (bool) Whether to enable score threshold
|
||||
- <code>score_threshold</code> (float) Score threshold
|
||||
- <code>metadata_filtering_conditions</code> (object) Metadata filtering conditions
|
||||
- <code>logical_operator</code> (string) Logical operator: <code>and</code> | <code>or</code>
|
||||
- <code>conditions</code> (array[object]) Conditions list
|
||||
- <code>name</code> (string) Metadata field name
|
||||
- <code>comparison_operator</code> (string) Comparison operator, allowed values:
|
||||
- String comparison:
|
||||
- <code>contains</code>: Contains
|
||||
- <code>not contains</code>: Does not contain
|
||||
- <code>start with</code>: Starts with
|
||||
- <code>end with</code>: Ends with
|
||||
- <code>is</code>: Equals
|
||||
- <code>is not</code>: Does not equal
|
||||
- <code>empty</code>: Is empty
|
||||
- <code>not empty</code>: Is not empty
|
||||
- Numeric comparison:
|
||||
- <code>=</code>: Equals
|
||||
- <code>≠</code>: Does not equal
|
||||
- <code>></code>: Greater than
|
||||
- <code>< </code>: Less than
|
||||
- <code>≥</code>: Greater than or equal
|
||||
- <code>≤</code>: Less than or equal
|
||||
- Time comparison:
|
||||
- <code>before</code>: Before
|
||||
- <code>after</code>: After
|
||||
- <code>value</code> (string|number|null) Comparison value
|
||||
</Property>
|
||||
<Property name='external_retrieval_model' type='object' key='external_retrieval_model'>
|
||||
Unused field
|
||||
@ -1809,7 +1904,17 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
"weights": null,
|
||||
"top_k": 1,
|
||||
"score_threshold_enabled": false,
|
||||
"score_threshold": null
|
||||
"score_threshold": null,
|
||||
"metadata_filtering_conditions": {
|
||||
"logical_operator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"name": "document_name",
|
||||
"comparison_operator": "contains",
|
||||
"value": "test"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}'`}
|
||||
>
|
||||
@ -2089,9 +2194,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
label="/datasets/{dataset_id}/documents/metadata"
|
||||
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/metadata' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'\\\n--data-raw '{"operation_data": [{"document_id": "document_id", "metadata_list": [{"id": "id", "value": "value", "name": "name"}]}]}'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
```
|
||||
</CodeGroup>
|
||||
```bash {{ title: 'cURL' }} </CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@ -2246,6 +2349,316 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
Okay, I will translate the Chinese text in your document while keeping all formatting and code content unchanged.
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags'
|
||||
method='POST'
|
||||
title='Create New Knowledge Base Type Tag'
|
||||
name='#create_new_knowledge_tag'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='name' type='string'>
|
||||
(text) New tag name, required, maximum length 50
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/datasets/tags"
|
||||
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "testtag1"}'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST '${props.apiBaseUrl}/datasets/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"name": "testtag1"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"id": "eddb66c2-04a1-4e3a-8cb2-75abd01e12a6",
|
||||
"name": "testtag1",
|
||||
"type": "knowledge",
|
||||
"binding_count": 0
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags'
|
||||
method='GET'
|
||||
title='Get Knowledge Base Type Tags'
|
||||
name='#get_knowledge_type_tags'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/datasets/tags"
|
||||
targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request GET '${props.apiBaseUrl}/datasets/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
[
|
||||
{
|
||||
"id": "39d6934c-ed36-463d-b4a7-377fa1503dc0",
|
||||
"name": "testtag1",
|
||||
"type": "knowledge",
|
||||
"binding_count": "0"
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags'
|
||||
method='PATCH'
|
||||
title='Modify Knowledge Base Type Tag Name'
|
||||
name='#modify_knowledge_tag_name'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='name' type='string'>
|
||||
(text) Modified tag name, required, maximum length 50
|
||||
</Property>
|
||||
<Property name='tag_id' type='string'>
|
||||
(text) Tag ID, required
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="PATCH"
|
||||
label="/datasets/tags"
|
||||
targetCode={`curl --location --request PATCH '${props.apiBaseUrl}/datasets/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "testtag2", "tag_id": "e1a0a3db-ee34-4e04-842a-81555d5316fd"}`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request PATCH '${props.apiBaseUrl}/datasets/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"name": "testtag2", "tag_id": "e1a0a3db-ee34-4e04-842a-81555d5316fd"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"id": "eddb66c2-04a1-4e3a-8cb2-75abd01e12a6",
|
||||
"name": "tag-renamed",
|
||||
"type": "knowledge",
|
||||
"binding_count": 0
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags'
|
||||
method='DELETE'
|
||||
title='Delete Knowledge Base Type Tag'
|
||||
name='#delete_knowledge_tag'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='tag_id' type='string'>
|
||||
(text) Tag ID, required
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="DELETE"
|
||||
label="/datasets/tags"
|
||||
targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ "tag_id": "e1a0a3db-ee34-4e04-842a-81555d5316fd"}`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request DELETE '${props.apiBaseUrl}/datasets/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"tag_id": "e1a0a3db-ee34-4e04-842a-81555d5316fd"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
|
||||
{"result": "success"}
|
||||
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags/binding'
|
||||
method='POST'
|
||||
title='Bind Dataset to Knowledge Base Type Tag'
|
||||
name='#bind_dataset_to_knowledge_tag'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='tag_ids' type='list'>
|
||||
(list) List of Tag IDs, required
|
||||
</Property>
|
||||
<Property name='target_id' type='string'>
|
||||
(text) Dataset ID, required
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/datasets/tags/binding"
|
||||
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/tags/binding' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"tag_ids": ["65cc29be-d072-4e26-adf4-2f727644da29","1e5348f3-d3ff-42b8-a1b7-0a86d518001a"], "target_id": "a932ea9f-fae1-4b2c-9b65-71c56e2cacd6"}'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST '${props.apiBaseUrl}/datasets/tags/binding' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"tag_ids": ["65cc29be-d072-4e26-adf4-2f727644da29","1e5348f3-d3ff-42b8-a1b7-0a86d518001a"], "target_id": "a932ea9f-fae1-4b2c-9b65-71c56e2cacd6"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{"result": "success"}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags/unbinding'
|
||||
method='POST'
|
||||
title='Unbind Dataset and Knowledge Base Type Tag'
|
||||
name='#unbind_dataset_and_knowledge_tag'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='tag_id' type='string'>
|
||||
(text) Tag ID, required
|
||||
</Property>
|
||||
<Property name='target_id' type='string'>
|
||||
(text) Dataset ID, required
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/datasets/tags/unbinding"
|
||||
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/tags/unbinding' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"tag_id": "1e5348f3-d3ff-42b8-a1b7-0a86d518001a", "target_id": "a932ea9f-fae1-4b2c-9b65-71c56e2cacd6"}'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST '${props.apiBaseUrl}/datasets/tags/unbinding' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"tag_id": "1e5348f3-d3ff-42b8-a1b7-0a86d518001a", "target_id": "a932ea9f-fae1-4b2c-9b65-71c56e2cacd6"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{"result": "success"}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/<uuid:dataset_id>/tags'
|
||||
method='POST'
|
||||
title='Query Tags Bound to a Dataset'
|
||||
name='#query_dataset_tags'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Path
|
||||
<Properties>
|
||||
<Property name='dataset_id' type='string'>
|
||||
(text) Dataset ID
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/datasets/<uuid:dataset_id>/tags"
|
||||
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/<uuid:dataset_id>/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST '${props.apiBaseUrl}/datasets/<uuid:dataset_id>/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data":
|
||||
[
|
||||
{"id": "4a601f4f-f8a2-4166-ae7c-58c3b252a524",
|
||||
"name": "123"
|
||||
},
|
||||
...
|
||||
],
|
||||
"total": 3
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
@ -1057,6 +1057,75 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
|
||||
<Heading
|
||||
url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}'
|
||||
method='GET'
|
||||
title='ドキュメントセグメントの詳細を表示'
|
||||
name='#view_document_segment'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
指定されたナレッジベース内の特定のドキュメントセグメントの詳細を表示します
|
||||
|
||||
### パス
|
||||
<Properties>
|
||||
<Property name='dataset_id' type='string' key='dataset_id'>
|
||||
ナレッジベースID
|
||||
</Property>
|
||||
<Property name='document_id' type='string' key='document_id'>
|
||||
ドキュメントID
|
||||
</Property>
|
||||
<Property name='segment_id' type='string' key='segment_id'>
|
||||
セグメントID
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="リクエスト"
|
||||
tag="GET"
|
||||
label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}"
|
||||
targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \
|
||||
--header 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="レスポンス">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": {
|
||||
"id": "セグメントID",
|
||||
"position": 2,
|
||||
"document_id": "ドキュメントID",
|
||||
"content": "セグメント内容テキスト",
|
||||
"sign_content": "署名内容テキスト",
|
||||
"answer": "回答内容(Q&Aモードの場合)",
|
||||
"word_count": 470,
|
||||
"tokens": 382,
|
||||
"keywords": ["キーワード1", "キーワード2"],
|
||||
"index_node_id": "インデックスノードID",
|
||||
"index_node_hash": "インデックスノードハッシュ",
|
||||
"hit_count": 0,
|
||||
"enabled": true,
|
||||
"status": "completed",
|
||||
"created_by": "作成者ID",
|
||||
"created_at": 作成タイムスタンプ,
|
||||
"updated_at": 更新タイムスタンプ,
|
||||
"indexing_at": インデックス作成タイムスタンプ,
|
||||
"completed_at": 完了タイムスタンプ,
|
||||
"error": null,
|
||||
"child_chunks": []
|
||||
},
|
||||
"doc_form": "text_model"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
method='DELETE'
|
||||
title='ドキュメント内のチャンクを削除'
|
||||
name='#delete_segment'
|
||||
@ -1100,7 +1169,6 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}'
|
||||
method='POST'
|
||||
title='ドキュメント内のチャンクを更新'
|
||||
name='#update_segment'
|
||||
@ -1528,20 +1596,45 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
クエリキーワード
|
||||
</Property>
|
||||
<Property name='retrieval_model' type='object' key='retrieval_model'>
|
||||
検索モデル (オプション、入力されない場合はデフォルトの方法でリコールされます)
|
||||
- <code>search_method</code> (text) 検索方法: 以下の 4 つのキーワードのいずれかが必要です
|
||||
- <code>keyword_search</code> キーワード検索
|
||||
- <code>semantic_search</code> セマンティック検索
|
||||
- <code>full_text_search</code> 全文検索
|
||||
- <code>hybrid_search</code> ハイブリッド検索
|
||||
- <code>reranking_enable</code> (bool) 再ランキングを有効にするかどうか、検索モードが semantic_search または hybrid_search の場合に必須 (オプション)
|
||||
- <code>reranking_mode</code> (object) 再ランキングモデル構成、再ランキングが有効な場合に必須
|
||||
- <code>reranking_provider_name</code> (string) 再ランキングモデルプロバイダー
|
||||
- <code>reranking_model_name</code> (string) 再ランキングモデル名
|
||||
- <code>weights</code> (float) ハイブリッド検索モードでのセマンティック検索の重み設定
|
||||
- <code>top_k</code> (integer) 返される結果の数 (オプション)
|
||||
- <code>score_threshold_enabled</code> (bool) スコア閾値を有効にするかどうか
|
||||
- <code>score_threshold</code> (float) スコア閾値
|
||||
検索パラメータ(オプション、入力されない場合はデフォルトの方法でリコールされます)
|
||||
- <code>search_method</code> (text) 検索方法: 以下の4つのキーワードのいずれかが必要です
|
||||
- <code>keyword_search</code> キーワード検索
|
||||
- <code>semantic_search</code> セマンティック検索
|
||||
- <code>full_text_search</code> 全文検索
|
||||
- <code>hybrid_search</code> ハイブリッド検索
|
||||
- <code>reranking_enable</code> (bool) 再ランキングを有効にするかどうか、検索モードがsemantic_searchまたはhybrid_searchの場合に必須(オプション)
|
||||
- <code>reranking_mode</code> (object) 再ランキングモデル構成、再ランキングが有効な場合に必須
|
||||
- <code>reranking_provider_name</code> (string) 再ランキングモデルプロバイダー
|
||||
- <code>reranking_model_name</code> (string) 再ランキングモデル名
|
||||
- <code>weights</code> (float) ハイブリッド検索モードでのセマンティック検索の重み設定
|
||||
- <code>top_k</code> (integer) 返される結果の数(オプション)
|
||||
- <code>score_threshold_enabled</code> (bool) スコア閾値を有効にするかどうか
|
||||
- <code>score_threshold</code> (float) スコア閾値
|
||||
- <code>metadata_filtering_conditions</code> (object) メタデータフィルタリング条件
|
||||
- <code>logical_operator</code> (string) 論理演算子: <code>and</code> | <code>or</code>
|
||||
- <code>conditions</code> (array[object]) 条件リスト
|
||||
- <code>name</code> (string) メタデータフィールド名
|
||||
- <code>comparison_operator</code> (string) 比較演算子、許可される値:
|
||||
- 文字列比較:
|
||||
- <code>contains</code>: 含む
|
||||
- <code>not contains</code>: 含まない
|
||||
- <code>start with</code>: で始まる
|
||||
- <code>end with</code>: で終わる
|
||||
- <code>is</code>: 等しい
|
||||
- <code>is not</code>: 等しくない
|
||||
- <code>empty</code>: 空
|
||||
- <code>not empty</code>: 空でない
|
||||
- 数値比較:
|
||||
- <code>=</code>: 等しい
|
||||
- <code>≠</code>: 等しくない
|
||||
- <code>></code>: より大きい
|
||||
- <code>< </code>: より小さい
|
||||
- <code>≥</code>: 以上
|
||||
- <code>≤</code>: 以下
|
||||
- 時間比較:
|
||||
- <code>before</code>: より前
|
||||
- <code>after</code>: より後
|
||||
- <code>value</code> (string|number|null) 比較値
|
||||
</Property>
|
||||
<Property name='external_retrieval_model' type='object' key='external_retrieval_model'>
|
||||
未使用フィールド
|
||||
@ -1566,7 +1659,17 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
"weights": null,
|
||||
"top_k": 1,
|
||||
"score_threshold_enabled": false,
|
||||
"score_threshold": null
|
||||
"score_threshold": null,
|
||||
"metadata_filtering_conditions": {
|
||||
"logical_operator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"name": "document_name",
|
||||
"comparison_operator": "contains",
|
||||
"value": "test"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}'`}
|
||||
>
|
||||
@ -1898,6 +2001,313 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
<Heading
|
||||
url='/datasets/tags'
|
||||
method='POST'
|
||||
title='ナレッジベースタイプタグの新規作成'
|
||||
name='#create_new_knowledge_tag'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='name' type='string'>
|
||||
(text) 新しいタグ名、必須、最大長50文字
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/datasets/tags"
|
||||
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "testtag1"}'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST '${props.apiBaseUrl}/datasets/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"name": "testtag1"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"id": "eddb66c2-04a1-4e3a-8cb2-75abd01e12a6",
|
||||
"name": "testtag1",
|
||||
"type": "knowledge",
|
||||
"binding_count": 0
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags'
|
||||
method='GET'
|
||||
title='ナレッジベースタイプタグの取得'
|
||||
name='#get_knowledge_type_tags'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/datasets/tags"
|
||||
targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request GET '${props.apiBaseUrl}/datasets/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
[
|
||||
{
|
||||
"id": "39d6934c-ed36-463d-b4a7-377fa1503dc0",
|
||||
"name": "testtag1",
|
||||
"type": "knowledge",
|
||||
"binding_count": "0"
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags'
|
||||
method='PATCH'
|
||||
title='ナレッジベースタイプタグ名の変更'
|
||||
name='#modify_knowledge_tag_name'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='name' type='string'>
|
||||
(text) 変更後のタグ名、必須、最大長50文字
|
||||
</Property>
|
||||
<Property name='tag_id' type='string'>
|
||||
(text) タグID、必須
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="PATCH"
|
||||
label="/datasets/tags"
|
||||
targetCode={`curl --location --request PATCH '${props.apiBaseUrl}/datasets/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "testtag2", "tag_id": "e1a0a3db-ee34-4e04-842a-81555d5316fd"}`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request PATCH '${props.apiBaseUrl}/datasets/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"name": "testtag2", "tag_id": "e1a0a3db-ee34-4e04-842a-81555d5316fd"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"id": "eddb66c2-04a1-4e3a-8cb2-75abd01e12a6",
|
||||
"name": "tag-renamed",
|
||||
"type": "knowledge",
|
||||
"binding_count": 0
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags'
|
||||
method='DELETE'
|
||||
title='ナレッジベースタイプタグの削除'
|
||||
name='#delete_knowledge_tag'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='tag_id' type='string'>
|
||||
(text) タグID、必須
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="DELETE"
|
||||
label="/datasets/tags"
|
||||
targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ "tag_id": "e1a0a3db-ee34-4e04-842a-81555d5316fd"}`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request DELETE '${props.apiBaseUrl}/datasets/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"tag_id": "e1a0a3db-ee34-4e04-842a-81555d5316fd"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
|
||||
{"result": "success"}
|
||||
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags/binding'
|
||||
method='POST'
|
||||
title='ナレッジベースをナレッジベースタイプタグに紐付け'
|
||||
name='#bind_dataset_to_knowledge_tag'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='tag_ids' type='list'>
|
||||
(list) タグIDリスト、必須
|
||||
</Property>
|
||||
<Property name='target_id' type='string'>
|
||||
(text) ナレッジベースID、必須
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/datasets/tags/binding"
|
||||
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/tags/binding' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"tag_ids": ["65cc29be-d072-4e26-adf4-2f727644da29","1e5348f3-d3ff-42b8-a1b7-0a86d518001a"], "target_id": "a932ea9f-fae1-4b2c-9b65-71c56e2cacd6"}'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST '${props.apiBaseUrl}/datasets/tags/binding' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"tag_ids": ["65cc29be-d072-4e26-adf4-2f727644da29","1e5348f3-d3ff-42b8-a1b7-0a86d518001a"], "target_id": "a932ea9f-fae1-4b2c-9b65-71c56e2cacd6"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{"result": "success"}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags/unbinding'
|
||||
method='POST'
|
||||
title='ナレッジベースとナレッジベースタイプタグの紐付け解除'
|
||||
name='#unbind_dataset_and_knowledge_tag'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='tag_id' type='string'>
|
||||
(text) タグID、必須
|
||||
</Property>
|
||||
<Property name='target_id' type='string'>
|
||||
(text) ナレッジベースID、必須
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/datasets/tags/unbinding"
|
||||
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/tags/unbinding' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"tag_id": "1e5348f3-d3ff-42b8-a1b7-0a86d518001a", "target_id": "a932ea9f-fae1-4b2c-9b65-71c56e2cacd6"}'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST '${props.apiBaseUrl}/datasets/tags/unbinding' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"tag_id": "1e5348f3-d3ff-42b8-a1b7-0a86d518001a", "target_id": "a932ea9f-fae1-4b2c-9b65-71c56e2cacd6"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{"result": "success"}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/<uuid:dataset_id>/tags'
|
||||
method='POST'
|
||||
title='ナレッジベースに紐付けられたタグの照会'
|
||||
name='#query_dataset_tags'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Path
|
||||
<Properties>
|
||||
<Property name='dataset_id' type='string'>
|
||||
(text) ナレッジベースID
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/datasets/<uuid:dataset_id>/tags"
|
||||
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/<uuid:dataset_id>/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST '${props.apiBaseUrl}/datasets/<uuid:dataset_id>/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data":
|
||||
[
|
||||
{"id": "4a601f4f-f8a2-4166-ae7c-58c3b252a524",
|
||||
"name": "123"
|
||||
},
|
||||
...
|
||||
],
|
||||
"total": 3
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Row>
|
||||
|
||||
@ -1351,6 +1351,75 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
|
||||
<Heading
|
||||
url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}'
|
||||
method='GET'
|
||||
title='查看文档分段详情'
|
||||
name='#view_document_segment'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
查看指定知识库中特定文档的分段详情
|
||||
|
||||
### Path
|
||||
<Properties>
|
||||
<Property name='dataset_id' type='string' key='dataset_id'>
|
||||
知识库 ID
|
||||
</Property>
|
||||
<Property name='document_id' type='string' key='document_id'>
|
||||
文档 ID
|
||||
</Property>
|
||||
<Property name='segment_id' type='string' key='segment_id'>
|
||||
分段 ID
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}"
|
||||
targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \
|
||||
--header 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": {
|
||||
"id": "分段唯一ID",
|
||||
"position": 2,
|
||||
"document_id": "所属文档ID",
|
||||
"content": "分段内容文本",
|
||||
"sign_content": "签名内容文本",
|
||||
"answer": "答案内容(如果有)",
|
||||
"word_count": 470,
|
||||
"tokens": 382,
|
||||
"keywords": ["关键词1", "关键词2"],
|
||||
"index_node_id": "索引节点ID",
|
||||
"index_node_hash": "索引节点哈希值",
|
||||
"hit_count": 0,
|
||||
"enabled": true,
|
||||
"status": "completed",
|
||||
"created_by": "创建者ID",
|
||||
"created_at": 创建时间戳,
|
||||
"updated_at": 更新时间戳,
|
||||
"indexing_at": 索引时间戳,
|
||||
"completed_at": 完成时间戳,
|
||||
"error": null,
|
||||
"child_chunks": []
|
||||
},
|
||||
"doc_form": "text_model"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
method='POST'
|
||||
title='更新文档分段'
|
||||
name='#update_segment'
|
||||
@ -1827,6 +1896,31 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
- <code>top_k</code> (integer) 返回结果数量,非必填
|
||||
- <code>score_threshold_enabled</code> (bool) 是否开启 score 阈值
|
||||
- <code>score_threshold</code> (float) Score 阈值
|
||||
- <code>metadata_filtering_conditions</code> (object) 元数据过滤条件
|
||||
- <code>logical_operator</code> (string) 逻辑运算符: <code>and</code> | <code>or</code>
|
||||
- <code>conditions</code> (array[object]) 条件列表
|
||||
- <code>name</code> (string) 元数据字段名
|
||||
- <code>comparison_operator</code> (string) 比较运算符,可选值:
|
||||
- 字符串比较:
|
||||
- <code>contains</code>: 包含
|
||||
- <code>not contains</code>: 不包含
|
||||
- <code>start with</code>: 以...开头
|
||||
- <code>end with</code>: 以...结尾
|
||||
- <code>is</code>: 等于
|
||||
- <code>is not</code>: 不等于
|
||||
- <code>empty</code>: 为空
|
||||
- <code>not empty</code>: 不为空
|
||||
- 数值比较:
|
||||
- <code>=</code>: 等于
|
||||
- <code>≠</code>: 不等于
|
||||
- <code>></code>: 大于
|
||||
- <code> < </code>: 小于
|
||||
- <code>≥</code>: 大于等于
|
||||
- <code>≤</code>: 小于等于
|
||||
- 时间比较:
|
||||
- <code>before</code>: 早于
|
||||
- <code>after</code>: 晚于
|
||||
- <code>value</code> (string|number|null) 比较值
|
||||
</Property>
|
||||
<Property name='external_retrieval_model' type='object' key='external_retrieval_model'>
|
||||
未启用字段
|
||||
@ -1851,7 +1945,17 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
"weights": null,
|
||||
"top_k": 1,
|
||||
"score_threshold_enabled": false,
|
||||
"score_threshold": null
|
||||
"score_threshold": null,
|
||||
"metadata_filtering_conditions": {
|
||||
"logical_operator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"name": "document_name",
|
||||
"comparison_operator": "contains",
|
||||
"value": "test"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}'`}
|
||||
>
|
||||
@ -2287,6 +2391,314 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags'
|
||||
method='POST'
|
||||
title='新增知识库类型标签'
|
||||
name='#create_new_knowledge_tag'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='name' type='string'>
|
||||
(text) 新标签名称,必填,最大长度为50
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/datasets/tags"
|
||||
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "testtag1"}'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST '${props.apiBaseUrl}/datasets/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"name": "testtag1"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"id": "eddb66c2-04a1-4e3a-8cb2-75abd01e12a6",
|
||||
"name": "testtag1",
|
||||
"type": "knowledge",
|
||||
"binding_count": 0
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags'
|
||||
method='GET'
|
||||
title='获取知识库类型标签'
|
||||
name='#get_knowledge_type_tags'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/datasets/tags"
|
||||
targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request GET '${props.apiBaseUrl}/datasets/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
[
|
||||
{
|
||||
"id": "39d6934c-ed36-463d-b4a7-377fa1503dc0",
|
||||
"name": "testtag1",
|
||||
"type": "knowledge",
|
||||
"binding_count": "0"
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags'
|
||||
method='PATCH'
|
||||
title='修改知识库类型标签名称'
|
||||
name='#modify_knowledge_tag_name'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='name' type='string'>
|
||||
(text) 修改后的标签名称,必填,最大长度为50
|
||||
</Property>
|
||||
<Property name='tag_id' type='string'>
|
||||
(text) 标签ID,必填
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="PATCH"
|
||||
label="/datasets/tags"
|
||||
targetCode={`curl --location --request PATCH '${props.apiBaseUrl}/datasets/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "testtag2", "tag_id": "e1a0a3db-ee34-4e04-842a-81555d5316fd"}`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request PATCH '${props.apiBaseUrl}/datasets/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"name": "testtag2", "tag_id": "e1a0a3db-ee34-4e04-842a-81555d5316fd"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"id": "eddb66c2-04a1-4e3a-8cb2-75abd01e12a6",
|
||||
"name": "tag-renamed",
|
||||
"type": "knowledge",
|
||||
"binding_count": 0
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags'
|
||||
method='DELETE'
|
||||
title='删除知识库类型标签'
|
||||
name='#delete_knowledge_tag'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='tag_id' type='string'>
|
||||
(text) 标签ID,必填
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="DELETE"
|
||||
label="/datasets/tags"
|
||||
targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ "tag_id": "e1a0a3db-ee34-4e04-842a-81555d5316fd"}`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request DELETE '${props.apiBaseUrl}/datasets/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"tag_id": "e1a0a3db-ee34-4e04-842a-81555d5316fd"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
|
||||
{"result": "success"}
|
||||
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags/binding'
|
||||
method='POST'
|
||||
title='绑定知识库到知识库类型标签'
|
||||
name='#bind_dataset_to_knowledge_tag'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='tag_ids' type='list'>
|
||||
(list) 标签ID列表,必填
|
||||
</Property>
|
||||
<Property name='target_id' type='string'>
|
||||
(text) 知识库ID,必填
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/datasets/tags/binding"
|
||||
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/tags/binding' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"tag_ids": ["65cc29be-d072-4e26-adf4-2f727644da29","1e5348f3-d3ff-42b8-a1b7-0a86d518001a"], "target_id": "a932ea9f-fae1-4b2c-9b65-71c56e2cacd6"}'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST '${props.apiBaseUrl}/datasets/tags/binding' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"tag_ids": ["65cc29be-d072-4e26-adf4-2f727644da29","1e5348f3-d3ff-42b8-a1b7-0a86d518001a"], "target_id": "a932ea9f-fae1-4b2c-9b65-71c56e2cacd6"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{"result": "success"}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/tags/unbinding'
|
||||
method='POST'
|
||||
title='解绑知识库和知识库类型标签'
|
||||
name='#unbind_dataset_and_knowledge_tag'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Request Body
|
||||
<Properties>
|
||||
<Property name='tag_id' type='string'>
|
||||
(text) 标签ID,必填
|
||||
</Property>
|
||||
<Property name='target_id' type='string'>
|
||||
(text) 知识库ID,必填
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/datasets/tags/unbinding"
|
||||
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/tags/unbinding' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"tag_id": "1e5348f3-d3ff-42b8-a1b7-0a86d518001a", "target_id": "a932ea9f-fae1-4b2c-9b65-71c56e2cacd6"}'`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST '${props.apiBaseUrl}/datasets/tags/unbinding' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"tag_id": "1e5348f3-d3ff-42b8-a1b7-0a86d518001a", "target_id": "a932ea9f-fae1-4b2c-9b65-71c56e2cacd6"}'
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{"result": "success"}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Heading
|
||||
url='/datasets/<uuid:dataset_id>/tags'
|
||||
method='POST'
|
||||
title='查询知识库已绑定的标签'
|
||||
name='#query_dataset_tags'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
### Path
|
||||
<Properties>
|
||||
<Property name='dataset_id' type='string'>
|
||||
(text) 知识库ID
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/datasets/<uuid:dataset_id>/tags"
|
||||
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/<uuid:dataset_id>/tags' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n`}
|
||||
>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST '${props.apiBaseUrl}/datasets/<uuid:dataset_id>/tags' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data":
|
||||
[
|
||||
{"id": "4a601f4f-f8a2-4166-ae7c-58c3b252a524",
|
||||
"name": "123"
|
||||
},
|
||||
...
|
||||
],
|
||||
"total": 3
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
<hr className='ml-0 mr-0' />
|
||||
|
||||
<Row>
|
||||
|
||||
@ -5,7 +5,7 @@ import type {
|
||||
} from '../declarations'
|
||||
import { useLanguage } from '../hooks'
|
||||
import { Group } from '@/app/components/base/icons/src/vender/other'
|
||||
import { OpenaiBlue, OpenaiViolet } from '@/app/components/base/icons/src/public/llm'
|
||||
import { OpenaiBlue, OpenaiTale, OpenaiViolet, OpenaiYellow } from '@/app/components/base/icons/src/public/llm'
|
||||
import cn from '@/utils/classnames'
|
||||
import { renderI18nObject } from '@/i18n'
|
||||
|
||||
@ -22,6 +22,10 @@ const ModelIcon: FC<ModelIconProps> = ({
|
||||
isDeprecated = false,
|
||||
}) => {
|
||||
const language = useLanguage()
|
||||
if (provider?.provider && ['openai', 'langgenius/openai/openai'].includes(provider.provider) && modelName?.startsWith('o'))
|
||||
return <div className='flex items-center justify-center'><OpenaiYellow className={cn('h-5 w-5', className)} /></div>
|
||||
if (provider?.provider && ['openai', 'langgenius/openai/openai'].includes(provider.provider) && modelName?.includes('gpt-4.1'))
|
||||
return <div className='flex items-center justify-center'><OpenaiTale className={cn('h-5 w-5', className)} /></div>
|
||||
if (provider?.provider && ['openai', 'langgenius/openai/openai'].includes(provider.provider) && modelName?.includes('gpt-4o'))
|
||||
return <div className='flex items-center justify-center'><OpenaiBlue className={cn('h-5 w-5', className)} /></div>
|
||||
if (provider?.provider && ['openai', 'langgenius/openai/openai'].includes(provider.provider) && modelName?.startsWith('gpt-4'))
|
||||
|
||||
@ -9,6 +9,7 @@ import InstallMulti from './install-multi'
|
||||
import { useInstallOrUpdate } from '@/service/use-plugins'
|
||||
import useRefreshPluginList from '../../hooks/use-refresh-plugin-list'
|
||||
import { useCanInstallPluginFromMarketplace } from '@/app/components/plugins/plugin-page/use-permission'
|
||||
import { useMittContextSelector } from '@/context/mitt-context'
|
||||
const i18nPrefix = 'plugin.installModal'
|
||||
|
||||
type Props = {
|
||||
@ -29,6 +30,7 @@ const Install: FC<Props> = ({
|
||||
isHideButton,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const emit = useMittContextSelector(s => s.emit)
|
||||
const [selectedPlugins, setSelectedPlugins] = React.useState<Plugin[]>([])
|
||||
const [selectedIndexes, setSelectedIndexes] = React.useState<number[]>([])
|
||||
const selectedPluginsNum = selectedPlugins.length
|
||||
@ -63,8 +65,12 @@ const Install: FC<Props> = ({
|
||||
})
|
||||
}))
|
||||
const hasInstallSuccess = res.some(r => r.success)
|
||||
if (hasInstallSuccess)
|
||||
if (hasInstallSuccess) {
|
||||
refreshPluginList(undefined, true)
|
||||
emit('plugin:install:success', selectedPlugins.map((p) => {
|
||||
return `${p.plugin_id}/${p.name}`
|
||||
}))
|
||||
}
|
||||
},
|
||||
})
|
||||
const handleInstall = () => {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { BodyType, type HttpNodeType, Method } from '../types'
|
||||
import { BodyPayloadValueType, BodyType, type HttpNodeType, Method } from '../types'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Textarea from '@/app/components/base/textarea'
|
||||
@ -51,11 +51,16 @@ const parseCurl = (curlCommand: string): { node: HttpNodeType | null; error: str
|
||||
case '-d':
|
||||
case '--data':
|
||||
case '--data-raw':
|
||||
case '--data-binary':
|
||||
case '--data-binary': {
|
||||
if (i + 1 >= args.length)
|
||||
return { node: null, error: 'Missing data value after -d, --data, --data-raw, or --data-binary.' }
|
||||
node.body = { type: BodyType.rawText, data: args[++i].replace(/^['"]|['"]$/g, '') }
|
||||
const bodyPayload = [{
|
||||
type: BodyPayloadValueType.text,
|
||||
value: args[++i].replace(/^['"]|['"]$/g, ''),
|
||||
}]
|
||||
node.body = { type: BodyType.rawText, data: bodyPayload }
|
||||
break
|
||||
}
|
||||
case '-F':
|
||||
case '--form': {
|
||||
if (i + 1 >= args.length)
|
||||
|
||||
@ -28,6 +28,7 @@ const CodeEditor: FC<CodeEditorProps> = ({
|
||||
const { theme } = useTheme()
|
||||
const monacoRef = useRef<any>(null)
|
||||
const editorRef = useRef<any>(null)
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (monacoRef.current) {
|
||||
@ -74,6 +75,19 @@ const CodeEditor: FC<CodeEditorProps> = ({
|
||||
onUpdate?.(value)
|
||||
}, [onUpdate])
|
||||
|
||||
useEffect(() => {
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
editorRef.current?.layout()
|
||||
})
|
||||
|
||||
if (containerRef.current)
|
||||
resizeObserver.observe(containerRef.current)
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={classNames('flex flex-col h-full bg-components-input-bg-normal overflow-hidden', className)}>
|
||||
<div className='flex items-center justify-between pl-2 pr-1 pt-1'>
|
||||
@ -102,9 +116,11 @@ const CodeEditor: FC<CodeEditorProps> = ({
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classNames('relative', editorWrapperClassName)}>
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={classNames('relative overflow-hidden', editorWrapperClassName)}
|
||||
>
|
||||
<Editor
|
||||
height='100%'
|
||||
defaultLanguage='json'
|
||||
value={value}
|
||||
onChange={handleEditorChange}
|
||||
@ -117,7 +133,6 @@ const CodeEditor: FC<CodeEditorProps> = ({
|
||||
scrollBeyondLastLine: false,
|
||||
wordWrap: 'on',
|
||||
wrappingIndent: 'same',
|
||||
// Add these options
|
||||
overviewRulerBorder: false,
|
||||
hideCursorInOverviewRuler: true,
|
||||
renderLineHighlightOnlyWhenFocus: false,
|
||||
|
||||
@ -21,7 +21,7 @@ import { MittProvider, VisualEditorContextProvider, useMittContext } from './vis
|
||||
import ErrorMessage from './error-message'
|
||||
import { useVisualEditorStore } from './visual-editor/store'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
import { useGetDocLanguage } from '@/context/i18n'
|
||||
import { JSON_SCHEMA_MAX_DEPTH } from '@/config'
|
||||
|
||||
type JsonSchemaConfigProps = {
|
||||
@ -47,21 +47,13 @@ const DEFAULT_SCHEMA: SchemaRoot = {
|
||||
additionalProperties: false,
|
||||
}
|
||||
|
||||
const HELP_DOC_URL = {
|
||||
zh_Hans: 'https://docs.dify.ai/zh-hans/guides/workflow/structured-outputs',
|
||||
en_US: 'https://docs.dify.ai/en/guides/workflow/structured-outputs',
|
||||
ja_JP: 'https://docs.dify.ai/ja-jp/guides/workflow/structured-outputs',
|
||||
}
|
||||
|
||||
type LocaleKey = keyof typeof HELP_DOC_URL
|
||||
|
||||
const JsonSchemaConfig: FC<JsonSchemaConfigProps> = ({
|
||||
defaultSchema,
|
||||
onSave,
|
||||
onClose,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const locale = useGetLanguage() as LocaleKey
|
||||
const docLanguage = useGetDocLanguage()
|
||||
const [currentTab, setCurrentTab] = useState(SchemaView.VisualEditor)
|
||||
const [jsonSchema, setJsonSchema] = useState(defaultSchema || DEFAULT_SCHEMA)
|
||||
const [json, setJson] = useState(JSON.stringify(jsonSchema, null, 2))
|
||||
@ -260,7 +252,7 @@ const JsonSchemaConfig: FC<JsonSchemaConfigProps> = ({
|
||||
<div className='flex items-center gap-x-2 p-6 pt-5'>
|
||||
<a
|
||||
className='flex grow items-center gap-x-1 text-text-accent'
|
||||
href={HELP_DOC_URL[locale]}
|
||||
href={`https://docs.dify.ai/${docLanguage}/guides/workflow/structured-outputs`}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
|
||||
@ -12,7 +12,7 @@ const SchemaEditor: FC<SchemaEditorProps> = ({
|
||||
}) => {
|
||||
return (
|
||||
<CodeEditor
|
||||
className='rounded-xl'
|
||||
className='grow rounded-xl'
|
||||
editorWrapperClassName='grow'
|
||||
value={schema}
|
||||
onUpdate={onUpdate}
|
||||
|
||||
@ -34,7 +34,7 @@ const UserInput = () => {
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className={cn('sticky top-0 z-[1] rounded-xl border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg shadow-xs')}>
|
||||
<div className={cn('relative z-[1] rounded-xl border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg shadow-xs')}>
|
||||
<div className='px-4 pb-4 pt-3'>
|
||||
{visibleVariables.map((variable, index) => (
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user