dify
36
dify/web/app/components/develop/ApiServer.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import CopyFeedback from '@/app/components/base/copy-feedback'
|
||||
import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-button'
|
||||
|
||||
type ApiServerProps = {
|
||||
apiBaseUrl: string
|
||||
appId?: string
|
||||
}
|
||||
const ApiServer: FC<ApiServerProps> = ({
|
||||
apiBaseUrl,
|
||||
appId,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className='flex flex-wrap items-center gap-y-2'>
|
||||
<div className='mr-2 flex h-8 items-center rounded-lg border-[0.5px] border-components-input-border-active bg-components-input-bg-normal pl-1.5 pr-1 leading-5'>
|
||||
<div className='mr-0.5 h-5 shrink-0 rounded-md border border-divider-subtle px-1.5 text-[11px] text-text-tertiary'>{t('appApi.apiServer')}</div>
|
||||
<div className='w-fit truncate px-1 text-[13px] font-medium text-text-secondary sm:w-[248px]'>{apiBaseUrl}</div>
|
||||
<div className='mx-1 h-[14px] w-[1px] bg-divider-regular'></div>
|
||||
<CopyFeedback content={apiBaseUrl}/>
|
||||
</div>
|
||||
<div className='mr-2 flex h-8 items-center rounded-lg border-[0.5px] border-[#D1FADF] bg-[#ECFDF3] px-3 text-xs font-semibold text-[#039855]'>
|
||||
{t('appApi.ok')}
|
||||
</div>
|
||||
<SecretKeyButton
|
||||
className='!h-8 shrink-0' appId={appId}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ApiServer
|
||||
315
dify/web/app/components/develop/code.tsx
Normal file
@@ -0,0 +1,315 @@
|
||||
'use client'
|
||||
import {
|
||||
Children,
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
|
||||
import { Tag } from './tag'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { writeTextToClipboard } from '@/utils/clipboard'
|
||||
import type { PropsWithChildren, ReactElement, ReactNode } from 'react'
|
||||
|
||||
type IChildrenProps = {
|
||||
children: React.ReactNode
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
function ClipboardIcon(props: any) {
|
||||
return (
|
||||
<svg viewBox="0 0 20 20" aria-hidden="true" {...props}>
|
||||
<path
|
||||
strokeWidth="0"
|
||||
d="M5.5 13.5v-5a2 2 0 0 1 2-2l.447-.894A2 2 0 0 1 9.737 4.5h.527a2 2 0 0 1 1.789 1.106l.447.894a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-5a2 2 0 0 1-2-2Z"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
strokeLinejoin="round"
|
||||
d="M12.5 6.5a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-5a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2m5 0-.447-.894a2 2 0 0 0-1.79-1.106h-.527a2 2 0 0 0-1.789 1.106L7.5 6.5m5 0-1 1h-3l-1-1"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function CopyButton({ code }: { code: string }) {
|
||||
const [copyCount, setCopyCount] = useState(0)
|
||||
const copied = copyCount > 0
|
||||
|
||||
useEffect(() => {
|
||||
if (copyCount > 0) {
|
||||
const timeout = setTimeout(() => setCopyCount(0), 1000)
|
||||
return () => {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}
|
||||
}, [copyCount])
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={classNames(
|
||||
'group/button absolute right-4 top-1.5 overflow-hidden rounded-full py-1 pl-2 pr-3 text-2xs font-medium opacity-0 backdrop-blur transition focus:opacity-100 group-hover:opacity-100',
|
||||
copied
|
||||
? 'bg-emerald-400/10 ring-1 ring-inset ring-emerald-400/20'
|
||||
: 'hover:bg-white/7.5 dark:bg-white/2.5 bg-white/5 dark:hover:bg-white/5',
|
||||
)}
|
||||
onClick={() => {
|
||||
writeTextToClipboard(code).then(() => {
|
||||
setCopyCount(count => count + 1)
|
||||
})
|
||||
}}
|
||||
>
|
||||
<span
|
||||
aria-hidden={copied}
|
||||
className={classNames(
|
||||
'pointer-events-none flex items-center gap-0.5 text-zinc-400 transition duration-300',
|
||||
copied && '-translate-y-1.5 opacity-0',
|
||||
)}
|
||||
>
|
||||
<ClipboardIcon className="h-5 w-5 fill-zinc-500/20 stroke-zinc-500 transition-colors group-hover/button:stroke-zinc-400" />
|
||||
Copy
|
||||
</span>
|
||||
<span
|
||||
aria-hidden={!copied}
|
||||
className={classNames(
|
||||
'pointer-events-none absolute inset-0 flex items-center justify-center text-emerald-400 transition duration-300',
|
||||
!copied && 'translate-y-1.5 opacity-0',
|
||||
)}
|
||||
>
|
||||
Copied!
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function CodePanelHeader({ tag, label }: { tag?: string; label?: string }) {
|
||||
if (!tag && !label)
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className="border-b-white/7.5 bg-white/2.5 dark:bg-white/1 flex h-9 items-center gap-2 border-y border-t-transparent bg-zinc-900 px-4 dark:border-b-white/5">
|
||||
{tag && (
|
||||
<div className="dark flex">
|
||||
<Tag variant="small">{tag}</Tag>
|
||||
</div>
|
||||
)}
|
||||
{tag && label && (
|
||||
<span className="h-0.5 w-0.5 rounded-full bg-zinc-500" />
|
||||
)}
|
||||
{label && (
|
||||
<span className="font-mono text-xs text-zinc-400">{label}</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type CodeExample = {
|
||||
title?: string
|
||||
tag?: string
|
||||
code: string
|
||||
}
|
||||
|
||||
type ICodePanelProps = {
|
||||
children?: React.ReactNode
|
||||
tag?: string
|
||||
label?: string
|
||||
code?: string
|
||||
title?: string
|
||||
targetCode?: CodeExample
|
||||
}
|
||||
|
||||
function CodePanel({ tag, label, children, targetCode }: ICodePanelProps) {
|
||||
const child = Children.toArray(children)[0] as ReactElement<any>
|
||||
|
||||
return (
|
||||
<div className="dark:bg-white/2.5 group">
|
||||
<CodePanelHeader
|
||||
tag={tag}
|
||||
label={label}
|
||||
/>
|
||||
<div className="relative">
|
||||
{/* <pre className="p-4 overflow-x-auto text-xs text-white">{children}</pre> */}
|
||||
{/* <CopyButton code={child.props.code ?? code} /> */}
|
||||
{/* <CopyButton code={child.props.children.props.children} /> */}
|
||||
<pre className="overflow-x-auto p-4 text-xs text-white">
|
||||
{targetCode?.code ? (
|
||||
<code>{targetCode?.code}</code>
|
||||
) : (
|
||||
child
|
||||
)}
|
||||
</pre>
|
||||
<CopyButton code={targetCode?.code ?? child.props.children.props.children} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type CodeGroupHeaderProps = {
|
||||
title?: string
|
||||
tabTitles?: string[]
|
||||
selectedIndex?: number
|
||||
}
|
||||
|
||||
function CodeGroupHeader({ title, tabTitles, selectedIndex }: CodeGroupHeaderProps) {
|
||||
const hasTabs = (tabTitles?.length ?? 0) > 1
|
||||
|
||||
return (
|
||||
<div className="flex min-h-[calc(theme(spacing.12)+1px)] flex-wrap items-start gap-x-4 border-b border-zinc-700 bg-zinc-800 px-4 dark:border-zinc-800 dark:bg-transparent">
|
||||
{title && (
|
||||
<h3 className="mr-auto pt-3 text-xs font-semibold text-white">
|
||||
{title}
|
||||
</h3>
|
||||
)}
|
||||
{hasTabs && (
|
||||
<TabList className="-mb-px flex gap-4 text-xs font-medium">
|
||||
{tabTitles!.map((tabTitle, tabIndex) => (
|
||||
<Tab
|
||||
key={tabIndex}
|
||||
className={classNames(
|
||||
'border-b py-3 transition focus:[&:not(:focus-visible)]:outline-none',
|
||||
tabIndex === selectedIndex
|
||||
? 'border-emerald-500 text-emerald-400'
|
||||
: 'border-transparent text-zinc-400 hover:text-zinc-300',
|
||||
)}
|
||||
>
|
||||
{tabTitle}
|
||||
</Tab>
|
||||
))}
|
||||
</TabList>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type ICodeGroupPanelsProps = PropsWithChildren<{
|
||||
targetCode?: CodeExample[]
|
||||
[key: string]: any
|
||||
}>
|
||||
|
||||
function CodeGroupPanels({ children, targetCode, ...props }: ICodeGroupPanelsProps) {
|
||||
if ((targetCode?.length ?? 0) > 1) {
|
||||
return (
|
||||
<TabPanels>
|
||||
{targetCode!.map((code, index) => (
|
||||
<TabPanel key={code.title || code.tag || index}>
|
||||
<CodePanel {...props} targetCode={code} />
|
||||
</TabPanel>
|
||||
))}
|
||||
</TabPanels>
|
||||
)
|
||||
}
|
||||
|
||||
return <CodePanel {...props} targetCode={targetCode?.[0]}>{children}</CodePanel>
|
||||
}
|
||||
|
||||
function usePreventLayoutShift() {
|
||||
const positionRef = useRef<any>(null)
|
||||
const rafRef = useRef<any>(null)
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
window.cancelAnimationFrame(rafRef.current)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return {
|
||||
positionRef,
|
||||
preventLayoutShift(callback: () => {}) {
|
||||
const initialTop = positionRef.current.getBoundingClientRect().top
|
||||
|
||||
callback()
|
||||
|
||||
rafRef.current = window.requestAnimationFrame(() => {
|
||||
const newTop = positionRef.current.getBoundingClientRect().top
|
||||
window.scrollBy(0, newTop - initialTop)
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function useTabGroupProps(availableLanguages: string[]) {
|
||||
const [preferredLanguages, addPreferredLanguage] = useState<any>([])
|
||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||
const activeLanguage = [...(availableLanguages || [])].sort(
|
||||
(a, z) => preferredLanguages.indexOf(z) - preferredLanguages.indexOf(a),
|
||||
)[0]
|
||||
const languageIndex = availableLanguages?.indexOf(activeLanguage) || 0
|
||||
const newSelectedIndex = languageIndex === -1 ? selectedIndex : languageIndex
|
||||
if (newSelectedIndex !== selectedIndex)
|
||||
setSelectedIndex(newSelectedIndex)
|
||||
|
||||
const { positionRef, preventLayoutShift } = usePreventLayoutShift()
|
||||
|
||||
return {
|
||||
as: 'div',
|
||||
ref: positionRef,
|
||||
selectedIndex,
|
||||
onChange: (newSelectedIndex: number) => {
|
||||
preventLayoutShift(() =>
|
||||
(addPreferredLanguage(availableLanguages[newSelectedIndex]) as any),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const CodeGroupContext = createContext(false)
|
||||
|
||||
type CodeGroupProps = PropsWithChildren<{
|
||||
/** Code example(s) to display */
|
||||
targetCode?: string | CodeExample[]
|
||||
/** Example block title */
|
||||
title?: string
|
||||
/** HTTP method tag, e.g. GET, POST */
|
||||
tag?: string
|
||||
/** API path */
|
||||
label?: string
|
||||
}>
|
||||
|
||||
export function CodeGroup({ children, title, targetCode, ...props }: CodeGroupProps) {
|
||||
const examples = typeof targetCode === 'string' ? [{ code: targetCode }] as CodeExample[] : targetCode
|
||||
const tabTitles = examples?.map(({ title }) => title || 'Code') || []
|
||||
const tabGroupProps = useTabGroupProps(tabTitles)
|
||||
const hasTabs = tabTitles.length > 1
|
||||
const Container = hasTabs ? TabGroup : 'div'
|
||||
const containerProps = hasTabs ? tabGroupProps : {}
|
||||
const headerProps = hasTabs
|
||||
? { selectedIndex: tabGroupProps.selectedIndex, tabTitles }
|
||||
: {}
|
||||
|
||||
return (
|
||||
<CodeGroupContext.Provider value={true}>
|
||||
<Container
|
||||
{...containerProps}
|
||||
className="not-prose my-6 overflow-hidden rounded-2xl bg-zinc-900 shadow-md dark:ring-1 dark:ring-white/10"
|
||||
>
|
||||
<CodeGroupHeader title={title} {...headerProps} />
|
||||
<CodeGroupPanels {...props} targetCode={examples}>{children}</CodeGroupPanels>
|
||||
</Container>
|
||||
</CodeGroupContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
type IChildProps = {
|
||||
children: ReactNode
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export function Code({ children, ...props }: IChildProps) {
|
||||
return <code {...props}>{children}</code>
|
||||
}
|
||||
|
||||
export function Pre({ children, ...props }: IChildrenProps) {
|
||||
const isGrouped = useContext(CodeGroupContext)
|
||||
|
||||
if (isGrouped)
|
||||
return children
|
||||
|
||||
return <CodeGroup {...props}>{children}</CodeGroup>
|
||||
}
|
||||
|
||||
export function Embed({ value, ...props }: IChildrenProps) {
|
||||
return <span {...props}>{value}</span>
|
||||
}
|
||||
243
dify/web/app/components/develop/doc.tsx
Normal file
@@ -0,0 +1,243 @@
|
||||
'use client'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiCloseLine, RiListUnordered } from '@remixicon/react'
|
||||
import TemplateEn from './template/template.en.mdx'
|
||||
import TemplateZh from './template/template.zh.mdx'
|
||||
import TemplateJa from './template/template.ja.mdx'
|
||||
import TemplateAdvancedChatEn from './template/template_advanced_chat.en.mdx'
|
||||
import TemplateAdvancedChatZh from './template/template_advanced_chat.zh.mdx'
|
||||
import TemplateAdvancedChatJa from './template/template_advanced_chat.ja.mdx'
|
||||
import TemplateWorkflowEn from './template/template_workflow.en.mdx'
|
||||
import TemplateWorkflowZh from './template/template_workflow.zh.mdx'
|
||||
import TemplateWorkflowJa from './template/template_workflow.ja.mdx'
|
||||
import TemplateChatEn from './template/template_chat.en.mdx'
|
||||
import TemplateChatZh from './template/template_chat.zh.mdx'
|
||||
import TemplateChatJa from './template/template_chat.ja.mdx'
|
||||
import I18n from '@/context/i18n'
|
||||
import { LanguagesSupported } from '@/i18n-config/language'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import { AppModeEnum, Theme } from '@/types/app'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type IDocProps = {
|
||||
appDetail: any
|
||||
}
|
||||
|
||||
const Doc = ({ appDetail }: IDocProps) => {
|
||||
const { locale } = useContext(I18n)
|
||||
const { t } = useTranslation()
|
||||
const [toc, setToc] = useState<Array<{ href: string; text: string }>>([])
|
||||
const [isTocExpanded, setIsTocExpanded] = useState(false)
|
||||
const [activeSection, setActiveSection] = useState<string>('')
|
||||
const { theme } = useTheme()
|
||||
|
||||
const variables = appDetail?.model_config?.configs?.prompt_variables || []
|
||||
const inputs = variables.reduce((res: any, variable: any) => {
|
||||
res[variable.key] = variable.name || ''
|
||||
return res
|
||||
}, {})
|
||||
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia('(min-width: 1280px)')
|
||||
setIsTocExpanded(mediaQuery.matches)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const extractTOC = () => {
|
||||
const article = document.querySelector('article')
|
||||
if (article) {
|
||||
const headings = article.querySelectorAll('h2')
|
||||
const tocItems = Array.from(headings).map((heading) => {
|
||||
const anchor = heading.querySelector('a')
|
||||
if (anchor) {
|
||||
return {
|
||||
href: anchor.getAttribute('href') || '',
|
||||
text: anchor.textContent || '',
|
||||
}
|
||||
}
|
||||
return null
|
||||
}).filter((item): item is { href: string; text: string } => item !== null)
|
||||
setToc(tocItems)
|
||||
if (tocItems.length > 0)
|
||||
setActiveSection(tocItems[0].href.replace('#', ''))
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(extractTOC, 0)
|
||||
}, [appDetail, locale])
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const scrollContainer = document.querySelector('.overflow-auto')
|
||||
if (!scrollContainer || toc.length === 0)
|
||||
return
|
||||
|
||||
let currentSection = ''
|
||||
toc.forEach((item) => {
|
||||
const targetId = item.href.replace('#', '')
|
||||
const element = document.getElementById(targetId)
|
||||
if (element) {
|
||||
const rect = element.getBoundingClientRect()
|
||||
if (rect.top <= window.innerHeight / 2)
|
||||
currentSection = targetId
|
||||
}
|
||||
})
|
||||
|
||||
if (currentSection && currentSection !== activeSection)
|
||||
setActiveSection(currentSection)
|
||||
}
|
||||
|
||||
const scrollContainer = document.querySelector('.overflow-auto')
|
||||
if (scrollContainer) {
|
||||
scrollContainer.addEventListener('scroll', handleScroll)
|
||||
handleScroll()
|
||||
return () => scrollContainer.removeEventListener('scroll', handleScroll)
|
||||
}
|
||||
}, [toc, activeSection])
|
||||
|
||||
const handleTocClick = (e: React.MouseEvent<HTMLAnchorElement>, item: { href: string; text: string }) => {
|
||||
e.preventDefault()
|
||||
const targetId = item.href.replace('#', '')
|
||||
const element = document.getElementById(targetId)
|
||||
if (element) {
|
||||
const scrollContainer = document.querySelector('.overflow-auto')
|
||||
if (scrollContainer) {
|
||||
const headerOffset = 80
|
||||
const elementTop = element.offsetTop - headerOffset
|
||||
scrollContainer.scrollTo({
|
||||
top: elementTop,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Template = useMemo(() => {
|
||||
if (appDetail?.mode === AppModeEnum.CHAT || appDetail?.mode === AppModeEnum.AGENT_CHAT) {
|
||||
switch (locale) {
|
||||
case LanguagesSupported[1]:
|
||||
return <TemplateChatZh appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
case LanguagesSupported[7]:
|
||||
return <TemplateChatJa appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
default:
|
||||
return <TemplateChatEn appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
}
|
||||
}
|
||||
if (appDetail?.mode === AppModeEnum.ADVANCED_CHAT) {
|
||||
switch (locale) {
|
||||
case LanguagesSupported[1]:
|
||||
return <TemplateAdvancedChatZh appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
case LanguagesSupported[7]:
|
||||
return <TemplateAdvancedChatJa appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
default:
|
||||
return <TemplateAdvancedChatEn appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
}
|
||||
}
|
||||
if (appDetail?.mode === AppModeEnum.WORKFLOW) {
|
||||
switch (locale) {
|
||||
case LanguagesSupported[1]:
|
||||
return <TemplateWorkflowZh appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
case LanguagesSupported[7]:
|
||||
return <TemplateWorkflowJa appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
default:
|
||||
return <TemplateWorkflowEn appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
}
|
||||
}
|
||||
if (appDetail?.mode === AppModeEnum.COMPLETION) {
|
||||
switch (locale) {
|
||||
case LanguagesSupported[1]:
|
||||
return <TemplateZh appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
case LanguagesSupported[7]:
|
||||
return <TemplateJa appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
default:
|
||||
return <TemplateEn appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
}
|
||||
}
|
||||
return null
|
||||
}, [appDetail, locale, variables, inputs])
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
<div className={`fixed right-20 top-32 z-10 transition-all duration-150 ease-out ${isTocExpanded ? 'w-[280px]' : 'w-11'}`}>
|
||||
{isTocExpanded
|
||||
? (
|
||||
<nav className="toc flex max-h-[calc(100vh-150px)] w-full flex-col overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-background-default-hover shadow-xl">
|
||||
<div className="relative z-10 flex items-center justify-between border-b border-components-panel-border-subtle bg-background-default-hover px-4 py-2.5">
|
||||
<span className="text-xs font-medium uppercase tracking-wide text-text-tertiary">
|
||||
{t('appApi.develop.toc')}
|
||||
</span>
|
||||
<button type="button"
|
||||
onClick={() => setIsTocExpanded(false)}
|
||||
className="group flex h-6 w-6 items-center justify-center rounded-md transition-colors hover:bg-state-base-hover"
|
||||
aria-label="Close"
|
||||
>
|
||||
<RiCloseLine className="h-3 w-3 text-text-quaternary transition-colors group-hover:text-text-secondary" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="from-components-panel-border-subtle/20 pointer-events-none absolute left-0 right-0 top-[41px] z-10 h-2 bg-gradient-to-b to-transparent"></div>
|
||||
<div className="pointer-events-none absolute left-0 right-0 top-[43px] z-10 h-3 bg-gradient-to-b from-background-default-hover to-transparent"></div>
|
||||
|
||||
<div className="relative flex-1 overflow-y-auto px-3 py-3 pt-1">
|
||||
{toc.length === 0 ? (
|
||||
<div className="px-2 py-8 text-center text-xs text-text-quaternary">
|
||||
{t('appApi.develop.noContent')}
|
||||
</div>
|
||||
) : (
|
||||
<ul className="space-y-0.5">
|
||||
{toc.map((item, index) => {
|
||||
const isActive = activeSection === item.href.replace('#', '')
|
||||
return (
|
||||
<li key={index}>
|
||||
<a
|
||||
href={item.href}
|
||||
onClick={e => handleTocClick(e, item)}
|
||||
className={cn(
|
||||
'group relative flex items-center rounded-md px-3 py-2 text-[13px] transition-all duration-200',
|
||||
isActive
|
||||
? 'bg-state-base-hover font-medium text-text-primary'
|
||||
: 'text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
'mr-2 h-1.5 w-1.5 rounded-full transition-all duration-200',
|
||||
isActive
|
||||
? 'scale-100 bg-text-accent'
|
||||
: 'scale-75 bg-components-panel-border',
|
||||
)}
|
||||
/>
|
||||
<span className="flex-1 truncate">
|
||||
{item.text}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="pointer-events-none absolute bottom-0 left-0 right-0 z-10 h-4 rounded-b-xl bg-gradient-to-t from-background-default-hover to-transparent"></div>
|
||||
</nav>
|
||||
)
|
||||
: (
|
||||
<button type="button"
|
||||
onClick={() => setIsTocExpanded(true)}
|
||||
className="group flex h-11 w-11 items-center justify-center rounded-full border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg transition-all duration-150 hover:bg-background-default-hover hover:shadow-xl"
|
||||
aria-label="Open table of contents"
|
||||
>
|
||||
<RiListUnordered className="h-5 w-5 text-text-tertiary transition-colors group-hover:text-text-secondary" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<article className={cn('prose-xl prose', theme === Theme.dark && 'prose-invert')}>
|
||||
{Template}
|
||||
</article>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Doc
|
||||
35
dify/web/app/components/develop/index.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
'use client'
|
||||
import Doc from '@/app/components/develop/doc'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import ApiServer from '@/app/components/develop/ApiServer'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
|
||||
type IDevelopMainProps = {
|
||||
appId: string
|
||||
}
|
||||
|
||||
const DevelopMain = ({ appId }: IDevelopMainProps) => {
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
|
||||
if (!appDetail) {
|
||||
return (
|
||||
<div className='flex h-full items-center justify-center bg-background-default'>
|
||||
<Loading />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='relative flex h-full flex-col overflow-hidden'>
|
||||
<div className='flex shrink-0 items-center justify-between border-b border-solid border-b-divider-regular px-6 py-2'>
|
||||
<div className='text-lg font-medium text-text-primary'></div>
|
||||
<ApiServer apiBaseUrl={appDetail.api_base_url} appId={appId} />
|
||||
</div>
|
||||
<div className='grow overflow-auto px-4 py-4 sm:px-10'>
|
||||
<Doc appDetail={appDetail} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DevelopMain
|
||||
151
dify/web/app/components/develop/md.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
'use client'
|
||||
import type { PropsWithChildren } from 'react'
|
||||
import classNames from '@/utils/classnames'
|
||||
|
||||
type IChildrenProps = {
|
||||
children: React.ReactNode
|
||||
id?: string
|
||||
tag?: any
|
||||
label?: any
|
||||
anchor: boolean
|
||||
}
|
||||
|
||||
type IHeaderingProps = {
|
||||
url: string
|
||||
method: 'PUT' | 'DELETE' | 'GET' | 'POST' | 'PATCH'
|
||||
title: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export const Heading = function H2({
|
||||
url,
|
||||
method,
|
||||
title,
|
||||
name,
|
||||
}: IHeaderingProps) {
|
||||
let style = ''
|
||||
switch (method) {
|
||||
case 'PUT':
|
||||
style = 'ring-amber-300 bg-amber-400/10 text-amber-500 dark:ring-amber-400/30 dark:bg-amber-400/10 dark:text-amber-400'
|
||||
break
|
||||
case 'DELETE':
|
||||
style = 'ring-rose-200 bg-rose-50 text-red-500 dark:ring-rose-500/20 dark:bg-rose-400/10 dark:text-rose-400'
|
||||
break
|
||||
case 'POST':
|
||||
style = 'ring-sky-300 bg-sky-400/10 text-sky-500 dark:ring-sky-400/30 dark:bg-sky-400/10 dark:text-sky-400'
|
||||
break
|
||||
case 'PATCH':
|
||||
style = 'ring-violet-300 bg-violet-400/10 text-violet-500 dark:ring-violet-400/30 dark:bg-violet-400/10 dark:text-violet-400'
|
||||
break
|
||||
default:
|
||||
style = 'ring-emerald-300 dark:ring-emerald-400/30 bg-emerald-400/10 text-emerald-500 dark:text-emerald-400'
|
||||
break
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<span id={name?.replace(/^#/, '')} className='relative -top-28' />
|
||||
<div className="flex items-center gap-x-3" >
|
||||
<span className={`rounded-lg px-1.5 font-mono text-[0.625rem] font-semibold leading-6 ring-1 ring-inset ${style}`}>{method}</span>
|
||||
{/* <span className="h-0.5 w-0.5 rounded-full bg-zinc-300 dark:bg-zinc-600"></span> */}
|
||||
<span className="font-mono text-xs text-zinc-400">{url}</span>
|
||||
</div>
|
||||
<h2 className='mt-2 scroll-mt-32'>
|
||||
<a href={name} className='group text-inherit no-underline hover:text-inherit'>{title}</a>
|
||||
</h2>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export function Row({ children }: IChildrenProps) {
|
||||
return (
|
||||
<div className="grid grid-cols-1 items-start gap-x-16 gap-y-10 xl:!max-w-none xl:grid-cols-2">
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type IColProps = IChildrenProps & {
|
||||
sticky: boolean
|
||||
}
|
||||
export function Col({ children, sticky = false }: IColProps) {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'[&>:first-child]:mt-0 [&>:last-child]:mb-0',
|
||||
sticky && 'xl:sticky xl:top-24',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Properties({ children }: IChildrenProps) {
|
||||
return (
|
||||
<div className="my-6">
|
||||
<ul
|
||||
role="list"
|
||||
className="m-0 max-w-[calc(theme(maxWidth.lg)-theme(spacing.8))] list-none divide-y divide-zinc-900/5 p-0 dark:divide-white/5"
|
||||
>
|
||||
{children}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type IProperty = IChildrenProps & {
|
||||
name: string
|
||||
type: string
|
||||
}
|
||||
export function Property({ name, type, children }: IProperty) {
|
||||
return (
|
||||
<li className="m-0 px-0 py-4 first:pt-0 last:pb-0">
|
||||
<dl className="m-0 flex flex-wrap items-center gap-x-3 gap-y-2">
|
||||
<dt className="sr-only">Name</dt>
|
||||
<dd>
|
||||
<code>{name}</code>
|
||||
</dd>
|
||||
<dt className="sr-only">Type</dt>
|
||||
<dd className="font-mono text-xs text-zinc-400 dark:text-zinc-500">
|
||||
{type}
|
||||
</dd>
|
||||
<dt className="sr-only">Description</dt>
|
||||
<dd className="w-full flex-none [&>:first-child]:mt-0 [&>:last-child]:mb-0">
|
||||
{children}
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
type ISubProperty = IChildrenProps & {
|
||||
name: string
|
||||
type: string
|
||||
}
|
||||
export function SubProperty({ name, type, children }: ISubProperty) {
|
||||
return (
|
||||
<li className="m-0 px-0 py-1 last:pb-0">
|
||||
<dl className="m-0 flex flex-wrap items-center gap-x-3">
|
||||
<dt className="sr-only">Name</dt>
|
||||
<dd>
|
||||
<code>{name}</code>
|
||||
</dd>
|
||||
<dt className="sr-only">Type</dt>
|
||||
<dd className="font-mono text-xs text-zinc-400 dark:text-zinc-500">
|
||||
{type}
|
||||
</dd>
|
||||
<dt className="sr-only">Description</dt>
|
||||
<dd className="w-full flex-none [&>:first-child]:mt-0 [&>:last-child]:mb-0">
|
||||
{children}
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export function PropertyInstruction({ children }: PropsWithChildren<{ }>) {
|
||||
return (
|
||||
<li className="m-0 px-0 py-4 italic first:pt-0">{children}</li>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.6665 2.66683C11.2865 2.66683 11.5965 2.66683 11.8508 2.73498C12.541 2.91991 13.0801 3.45901 13.265 4.14919C13.3332 4.40352 13.3332 4.71352 13.3332 5.3335V11.4668C13.3332 12.5869 13.3332 13.147 13.1152 13.5748C12.9234 13.9511 12.6175 14.2571 12.2412 14.4488C11.8133 14.6668 11.2533 14.6668 10.1332 14.6668H5.8665C4.7464 14.6668 4.18635 14.6668 3.75852 14.4488C3.3822 14.2571 3.07624 13.9511 2.88449 13.5748C2.6665 13.147 2.6665 12.5869 2.6665 11.4668V5.3335C2.6665 4.71352 2.6665 4.40352 2.73465 4.14919C2.91959 3.45901 3.45868 2.91991 4.14887 2.73498C4.4032 2.66683 4.71319 2.66683 5.33317 2.66683M5.99984 10.0002L7.33317 11.3335L10.3332 8.3335M6.39984 4.00016H9.59984C9.9732 4.00016 10.1599 4.00016 10.3025 3.9275C10.4279 3.86359 10.5299 3.7616 10.5938 3.63616C10.6665 3.49355 10.6665 3.30686 10.6665 2.9335V2.40016C10.6665 2.02679 10.6665 1.84011 10.5938 1.6975C10.5299 1.57206 10.4279 1.47007 10.3025 1.40616C10.1599 1.3335 9.97321 1.3335 9.59984 1.3335H6.39984C6.02647 1.3335 5.83978 1.3335 5.69718 1.40616C5.57174 1.47007 5.46975 1.57206 5.40583 1.6975C5.33317 1.84011 5.33317 2.02679 5.33317 2.40016V2.9335C5.33317 3.30686 5.33317 3.49355 5.40583 3.63616C5.46975 3.7616 5.57174 3.86359 5.69718 3.9275C5.83978 4.00016 6.02647 4.00016 6.39984 4.00016Z" stroke="#1D2939" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.6665 2.66634H11.9998C12.3535 2.66634 12.6926 2.80682 12.9426 3.05687C13.1927 3.30691 13.3332 3.64605 13.3332 3.99967V13.333C13.3332 13.6866 13.1927 14.0258 12.9426 14.2758C12.6926 14.5259 12.3535 14.6663 11.9998 14.6663H3.99984C3.64622 14.6663 3.30708 14.5259 3.05703 14.2758C2.80698 14.0258 2.6665 13.6866 2.6665 13.333V3.99967C2.6665 3.64605 2.80698 3.30691 3.05703 3.05687C3.30708 2.80682 3.64622 2.66634 3.99984 2.66634H5.33317M5.99984 1.33301H9.99984C10.368 1.33301 10.6665 1.63148 10.6665 1.99967V3.33301C10.6665 3.7012 10.368 3.99967 9.99984 3.99967H5.99984C5.63165 3.99967 5.33317 3.7012 5.33317 3.33301V1.99967C5.33317 1.63148 5.63165 1.33301 5.99984 1.33301Z" stroke="#1D2939" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 875 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.6665 2.66634H11.9998C12.3535 2.66634 12.6926 2.80682 12.9426 3.05687C13.1927 3.30691 13.3332 3.64605 13.3332 3.99967V13.333C13.3332 13.6866 13.1927 14.0258 12.9426 14.2758C12.6926 14.5259 12.3535 14.6663 11.9998 14.6663H3.99984C3.64622 14.6663 3.30708 14.5259 3.05703 14.2758C2.80698 14.0258 2.6665 13.6866 2.6665 13.333V3.99967C2.6665 3.64605 2.80698 3.30691 3.05703 3.05687C3.30708 2.80682 3.64622 2.66634 3.99984 2.66634H5.33317M5.99984 1.33301H9.99984C10.368 1.33301 10.6665 1.63148 10.6665 1.99967V3.33301C10.6665 3.7012 10.368 3.99967 9.99984 3.99967H5.99984C5.63165 3.99967 5.33317 3.7012 5.33317 3.33301V1.99967C5.33317 1.63148 5.63165 1.33301 5.99984 1.33301Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 875 B |
10
dify/web/app/components/develop/secret-key/assets/pause.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_129_2189)">
|
||||
<path d="M10.6666 14V10M13.3333 14V10M18.6666 12C18.6666 15.6819 15.6819 18.6667 12 18.6667C8.31808 18.6667 5.33331 15.6819 5.33331 12C5.33331 8.3181 8.31808 5.33333 12 5.33333C15.6819 5.33333 18.6666 8.3181 18.6666 12Z" stroke="#155EEF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_129_2189">
|
||||
<rect width="16" height="16" fill="white" transform="translate(4 4)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 580 B |
11
dify/web/app/components/develop/secret-key/assets/play.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_129_107)">
|
||||
<path d="M7.99998 14.6666C11.6819 14.6666 14.6666 11.6819 14.6666 7.99998C14.6666 4.31808 11.6819 1.33331 7.99998 1.33331C4.31808 1.33331 1.33331 4.31808 1.33331 7.99998C1.33331 11.6819 4.31808 14.6666 7.99998 14.6666Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.66665 5.33331L10.6666 7.99998L6.66665 10.6666V5.33331Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_129_107">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 703 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.33333 4.33333H4.34M11.6667 4.33333H11.6733M4.33333 11.6667H4.34M8.66667 8.66667H8.67333M11.6667 11.6667H11.6733M11.3333 14H14V11.3333M9.33333 11V14M14 9.33333H11M10.4 6.66667H12.9333C13.3067 6.66667 13.4934 6.66667 13.636 6.594C13.7614 6.53009 13.8634 6.4281 13.9273 6.30266C14 6.16005 14 5.97337 14 5.6V3.06667C14 2.6933 14 2.50661 13.9273 2.36401C13.8634 2.23856 13.7614 2.13658 13.636 2.07266C13.4934 2 13.3067 2 12.9333 2H10.4C10.0266 2 9.83995 2 9.69734 2.07266C9.5719 2.13658 9.46991 2.23856 9.406 2.36401C9.33333 2.50661 9.33333 2.6933 9.33333 3.06667V5.6C9.33333 5.97337 9.33333 6.16005 9.406 6.30266C9.46991 6.4281 9.5719 6.53009 9.69734 6.594C9.83995 6.66667 10.0266 6.66667 10.4 6.66667ZM3.06667 6.66667H5.6C5.97337 6.66667 6.16005 6.66667 6.30266 6.594C6.4281 6.53009 6.53009 6.4281 6.594 6.30266C6.66667 6.16005 6.66667 5.97337 6.66667 5.6V3.06667C6.66667 2.6933 6.66667 2.50661 6.594 2.36401C6.53009 2.23856 6.4281 2.13658 6.30266 2.07266C6.16005 2 5.97337 2 5.6 2H3.06667C2.6933 2 2.50661 2 2.36401 2.07266C2.23856 2.13658 2.13658 2.23856 2.07266 2.36401C2 2.50661 2 2.6933 2 3.06667V5.6C2 5.97337 2 6.16005 2.07266 6.30266C2.13658 6.4281 2.23856 6.53009 2.36401 6.594C2.50661 6.66667 2.6933 6.66667 3.06667 6.66667ZM3.06667 14H5.6C5.97337 14 6.16005 14 6.30266 13.9273C6.4281 13.8634 6.53009 13.7614 6.594 13.636C6.66667 13.4934 6.66667 13.3067 6.66667 12.9333V10.4C6.66667 10.0266 6.66667 9.83995 6.594 9.69734C6.53009 9.5719 6.4281 9.46991 6.30266 9.406C6.16005 9.33333 5.97337 9.33333 5.6 9.33333H3.06667C2.6933 9.33333 2.50661 9.33333 2.36401 9.406C2.23856 9.46991 2.13658 9.5719 2.07266 9.69734C2 9.83995 2 10.0266 2 10.4V12.9333C2 13.3067 2 13.4934 2.07266 13.636C2.13658 13.7614 2.23856 13.8634 2.36401 13.9273C2.50661 14 2.6933 14 3.06667 14Z" stroke="#1D2939" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.33333 4.33333H4.34M11.6667 4.33333H11.6733M4.33333 11.6667H4.34M8.66667 8.66667H8.67333M11.6667 11.6667H11.6733M11.3333 14H14V11.3333M9.33333 11V14M14 9.33333H11M10.4 6.66667H12.9333C13.3067 6.66667 13.4934 6.66667 13.636 6.594C13.7614 6.53009 13.8634 6.4281 13.9273 6.30266C14 6.16005 14 5.97337 14 5.6V3.06667C14 2.6933 14 2.50661 13.9273 2.36401C13.8634 2.23856 13.7614 2.13658 13.636 2.07266C13.4934 2 13.3067 2 12.9333 2H10.4C10.0266 2 9.83995 2 9.69734 2.07266C9.5719 2.13658 9.46991 2.23856 9.406 2.36401C9.33333 2.50661 9.33333 2.6933 9.33333 3.06667V5.6C9.33333 5.97337 9.33333 6.16005 9.406 6.30266C9.46991 6.4281 9.5719 6.53009 9.69734 6.594C9.83995 6.66667 10.0266 6.66667 10.4 6.66667ZM3.06667 6.66667H5.6C5.97337 6.66667 6.16005 6.66667 6.30266 6.594C6.4281 6.53009 6.53009 6.4281 6.594 6.30266C6.66667 6.16005 6.66667 5.97337 6.66667 5.6V3.06667C6.66667 2.6933 6.66667 2.50661 6.594 2.36401C6.53009 2.23856 6.4281 2.13658 6.30266 2.07266C6.16005 2 5.97337 2 5.6 2H3.06667C2.6933 2 2.50661 2 2.36401 2.07266C2.23856 2.13658 2.13658 2.23856 2.07266 2.36401C2 2.50661 2 2.6933 2 3.06667V5.6C2 5.97337 2 6.16005 2.07266 6.30266C2.13658 6.4281 2.23856 6.53009 2.36401 6.594C2.50661 6.66667 2.6933 6.66667 3.06667 6.66667ZM3.06667 14H5.6C5.97337 14 6.16005 14 6.30266 13.9273C6.4281 13.8634 6.53009 13.7614 6.594 13.636C6.66667 13.4934 6.66667 13.3067 6.66667 12.9333V10.4C6.66667 10.0266 6.66667 9.83995 6.594 9.69734C6.53009 9.5719 6.4281 9.46991 6.30266 9.406C6.16005 9.33333 5.97337 9.33333 5.6 9.33333H3.06667C2.6933 9.33333 2.50661 9.33333 2.36401 9.406C2.23856 9.46991 2.13658 9.5719 2.07266 9.69734C2 9.83995 2 10.0266 2 10.4V12.9333C2 13.3067 2 13.4934 2.07266 13.636C2.13658 13.7614 2.23856 13.8634 2.36401 13.9273C2.50661 14 2.6933 14 3.06667 14Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1694177685288" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4415" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M192 384h640a42.666667 42.666667 0 0 1 42.666667 42.666667v362.666666a42.666667 42.666667 0 0 1-42.666667 42.666667H192v106.666667a21.333333 21.333333 0 0 0 21.333333 21.333333h725.333334a21.333333 21.333333 0 0 0 21.333333-21.333333V308.821333L949.909333 298.666667h-126.528A98.048 98.048 0 0 1 725.333333 200.618667V72.661333L716.714667 64H213.333333a21.333333 21.333333 0 0 0-21.333333 21.333333v298.666667zM128 832H42.666667a42.666667 42.666667 0 0 1-42.666667-42.666667V426.666667a42.666667 42.666667 0 0 1 42.666667-42.666667h85.333333V85.333333a85.333333 85.333333 0 0 1 85.333333-85.333333h530.026667L1024 282.453333V938.666667a85.333333 85.333333 0 0 1-85.333333 85.333333H213.333333a85.333333 85.333333 0 0 1-85.333333-85.333333v-106.666667z m61.376-364.885333c-27.434667 0-49.898667 6.528-67.712 19.968-19.221333 13.824-28.501333 33.024-28.501333 57.216s9.621333 42.624 29.226666 55.296c7.466667 4.608 27.093333 12.288 58.432 23.04 28.138667 9.216 44.522667 15.36 49.514667 18.048 15.68 8.448 23.872 19.968 23.872 34.56 0 11.52-5.696 20.352-16.384 27.264-10.688 6.528-25.664 9.984-44.181333 9.984-21.013333 0-36.352-4.224-46.314667-11.904-11.050667-8.832-17.813333-23.808-20.672-44.544H85.333333c1.792 34.944 13.546667 60.288 34.922667 76.416 17.450667 13.056 42.026667 19.584 73.386667 19.584 32.426667 0 57.706667-7.296 75.52-21.12 17.813333-14.208 26.730667-33.792 26.730666-58.368 0-25.344-11.050667-44.928-33.130666-59.136-9.984-6.144-32.064-15.36-66.624-26.88-23.509333-8.064-38.122667-13.824-43.477334-16.896-12.096-6.912-17.813333-16.512-17.813333-28.032 0-13.056 4.992-22.656 15.68-28.416 8.554667-4.992 20.672-7.296 36.693333-7.296 18.538667 0 32.789333 3.456 42.048 11.136 9.258667 7.296 16.021333 19.584 19.584 36.48h41.344c-2.496-29.952-12.821333-52.224-30.656-66.432-16.725333-13.44-40.256-19.968-70.186666-19.968z m118.976 5.376L398.848 746.666667h50.24l90.496-274.176h-45.226667l-69.845333 223.488h-1.066667l-69.845333-223.488h-45.226667z m368.405333-5.376c-37.76 0-67.690667 13.824-89.792 42.24-21.013333 26.496-31.36 60.288-31.36 101.376 0 40.704 10.346667 74.112 31.36 99.84 22.442667 27.648 53.802667 41.472 94.421334 41.472 22.805333 0 43.093333-3.072 61.632-9.216A143.829333 143.829333 0 0 0 789.333333 716.714667V600.746667h-109.013333v38.4h67.328v56.448c-8.533333 5.376-17.450667 9.6-27.434667 12.672a123.285333 123.285333 0 0 1-34.197333 4.608c-30.997333 0-53.802667-9.216-68.416-27.648-13.525333-17.28-20.309333-42.24-20.309333-74.496 0-33.792 7.488-59.52 22.826666-77.952 13.866667-17.664 32.768-26.112 56.64-26.112 19.221333 0 34.901333 4.224 46.656 13.056 11.413333 8.832 19.242667 21.888 22.826667 39.552h42.026667c-4.629333-30.72-16.042667-53.376-34.197334-68.736-18.88-15.744-44.544-23.424-77.312-23.424z" fill="#8a8a8a" p-id="4416"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1694177378730" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4206" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M192 384h640a42.666667 42.666667 0 0 1 42.666667 42.666667v362.666666a42.666667 42.666667 0 0 1-42.666667 42.666667H192v106.666667a21.333333 21.333333 0 0 0 21.333333 21.333333h725.333334a21.333333 21.333333 0 0 0 21.333333-21.333333V308.821333L949.909333 298.666667h-126.528A98.048 98.048 0 0 1 725.333333 200.618667V72.661333L716.714667 64H213.333333a21.333333 21.333333 0 0 0-21.333333 21.333333v298.666667zM128 832H42.666667a42.666667 42.666667 0 0 1-42.666667-42.666667V426.666667a42.666667 42.666667 0 0 1 42.666667-42.666667h85.333333V85.333333a85.333333 85.333333 0 0 1 85.333333-85.333333h530.026667L1024 282.453333V938.666667a85.333333 85.333333 0 0 1-85.333333 85.333333H213.333333a85.333333 85.333333 0 0 1-85.333333-85.333333v-106.666667z m61.376-364.885333c-27.434667 0-49.898667 6.528-67.712 19.968-19.221333 13.824-28.501333 33.024-28.501333 57.216s9.621333 42.624 29.226666 55.296c7.466667 4.608 27.093333 12.288 58.432 23.04 28.138667 9.216 44.522667 15.36 49.514667 18.048 15.68 8.448 23.872 19.968 23.872 34.56 0 11.52-5.696 20.352-16.384 27.264-10.688 6.528-25.664 9.984-44.181333 9.984-21.013333 0-36.352-4.224-46.314667-11.904-11.050667-8.832-17.813333-23.808-20.672-44.544H85.333333c1.792 34.944 13.546667 60.288 34.922667 76.416 17.450667 13.056 42.026667 19.584 73.386667 19.584 32.426667 0 57.706667-7.296 75.52-21.12 17.813333-14.208 26.730667-33.792 26.730666-58.368 0-25.344-11.050667-44.928-33.130666-59.136-9.984-6.144-32.064-15.36-66.624-26.88-23.509333-8.064-38.122667-13.824-43.477334-16.896-12.096-6.912-17.813333-16.512-17.813333-28.032 0-13.056 4.992-22.656 15.68-28.416 8.554667-4.992 20.672-7.296 36.693333-7.296 18.538667 0 32.789333 3.456 42.048 11.136 9.258667 7.296 16.021333 19.584 19.584 36.48h41.344c-2.496-29.952-12.821333-52.224-30.656-66.432-16.725333-13.44-40.256-19.968-70.186666-19.968z m118.976 5.376L398.848 746.666667h50.24l90.496-274.176h-45.226667l-69.845333 223.488h-1.066667l-69.845333-223.488h-45.226667z m368.405333-5.376c-37.76 0-67.690667 13.824-89.792 42.24-21.013333 26.496-31.36 60.288-31.36 101.376 0 40.704 10.346667 74.112 31.36 99.84 22.442667 27.648 53.802667 41.472 94.421334 41.472 22.805333 0 43.093333-3.072 61.632-9.216A143.829333 143.829333 0 0 0 789.333333 716.714667V600.746667h-109.013333v38.4h67.328v56.448c-8.533333 5.376-17.450667 9.6-27.434667 12.672a123.285333 123.285333 0 0 1-34.197333 4.608c-30.997333 0-53.802667-9.216-68.416-27.648-13.525333-17.28-20.309333-42.24-20.309333-74.496 0-33.792 7.488-59.52 22.826666-77.952 13.866667-17.664 32.768-26.112 56.64-26.112 19.221333 0 34.901333 4.224 46.656 13.056 11.413333 8.832 19.242667 21.888 22.826667 39.552h42.026667c-4.629333-30.72-16.042667-53.376-34.197334-68.736-18.88-15.744-44.544-23.424-77.312-23.424z" fill="#1A8EF7" p-id="4207"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 2H10M2 4H14M12.6667 4L12.1991 11.0129C12.129 12.065 12.0939 12.5911 11.8667 12.99C11.6666 13.3412 11.3648 13.6235 11.0011 13.7998C10.588 14 10.0607 14 9.00623 14H6.99377C5.93927 14 5.41202 14 4.99889 13.7998C4.63517 13.6235 4.33339 13.3412 4.13332 12.99C3.90607 12.5911 3.871 12.065 3.80086 11.0129L3.33333 4M6.66667 7V10.3333M9.33333 7V10.3333" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 548 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 2H10M2 4H14M12.6667 4L12.1991 11.0129C12.129 12.065 12.0939 12.5911 11.8667 12.99C11.6666 13.3412 11.3648 13.6235 11.0011 13.7998C10.588 14 10.0607 14 9.00623 14H6.99377C5.93927 14 5.41202 14 4.99889 13.7998C4.63517 13.6235 4.33339 13.3412 4.13332 12.99C3.90607 12.5911 3.871 12.065 3.80086 11.0129L3.33333 4M6.66667 7V10.3333M9.33333 7V10.3333" stroke="#D92D20" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 550 B |
57
dify/web/app/components/develop/secret-key/input-copy.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
'use client'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import { t } from 'i18next'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import CopyFeedback from '@/app/components/base/copy-feedback'
|
||||
|
||||
type IInputCopyProps = {
|
||||
value?: string
|
||||
className?: string
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
const InputCopy = ({
|
||||
value = '',
|
||||
className,
|
||||
children,
|
||||
}: IInputCopyProps) => {
|
||||
const [isCopied, setIsCopied] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (isCopied) {
|
||||
const timeout = setTimeout(() => {
|
||||
setIsCopied(false)
|
||||
}, 1000)
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}
|
||||
}, [isCopied])
|
||||
|
||||
return (
|
||||
<div className={`flex items-center rounded-lg bg-components-input-bg-normal py-2 hover:bg-state-base-hover ${className}`}>
|
||||
<div className="flex h-5 grow items-center">
|
||||
{children}
|
||||
<div className='relative h-full grow text-[13px]'>
|
||||
<div className='r-0 absolute left-0 top-0 w-full cursor-pointer truncate pl-2 pr-2' onClick={() => {
|
||||
copy(value)
|
||||
setIsCopied(true)
|
||||
}}>
|
||||
<Tooltip
|
||||
popupContent={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
|
||||
position='bottom'
|
||||
>
|
||||
<span className='text-text-secondary'>{value}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-4 w-px shrink-0 bg-divider-regular" />
|
||||
<div className='mx-1'><CopyFeedback content={value} /></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default InputCopy
|
||||
@@ -0,0 +1,35 @@
|
||||
'use client'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiKey2Line } from '@remixicon/react'
|
||||
import Button from '@/app/components/base/button'
|
||||
import SecretKeyModal from '@/app/components/develop/secret-key/secret-key-modal'
|
||||
|
||||
type ISecretKeyButtonProps = {
|
||||
className?: string
|
||||
appId?: string
|
||||
textCls?: string
|
||||
}
|
||||
|
||||
const SecretKeyButton = ({ className, appId, textCls }: ISecretKeyButtonProps) => {
|
||||
const [isVisible, setVisible] = useState(false)
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
className={`px-3 ${className}`}
|
||||
onClick={() => setVisible(true)}
|
||||
size='small'
|
||||
variant='ghost'
|
||||
>
|
||||
<div className={'flex h-3.5 w-3.5 items-center justify-center'}>
|
||||
<RiKey2Line className='h-3.5 w-3.5 text-text-tertiary' />
|
||||
</div>
|
||||
<div className={`system-xs-medium px-[3px] text-text-tertiary ${textCls}`}>{t('appApi.apiKey')}</div>
|
||||
</Button>
|
||||
<SecretKeyModal isShow={isVisible} onClose={() => setVisible(false)} appId={appId} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SecretKeyButton
|
||||
@@ -0,0 +1,43 @@
|
||||
'use client'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import InputCopy from './input-copy'
|
||||
import s from './style.module.css'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import type { CreateApiKeyResponse } from '@/models/app'
|
||||
|
||||
type ISecretKeyGenerateModalProps = {
|
||||
isShow: boolean
|
||||
onClose: () => void
|
||||
newKey?: CreateApiKeyResponse
|
||||
className?: string
|
||||
}
|
||||
|
||||
const SecretKeyGenerateModal = ({
|
||||
isShow = false,
|
||||
onClose,
|
||||
newKey,
|
||||
className,
|
||||
}: ISecretKeyGenerateModalProps) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Modal isShow={isShow} onClose={onClose} title={`${t('appApi.apiKeyModal.apiSecretKey')}`} className={`px-8 ${className}`}>
|
||||
<div className="-mr-2 -mt-6 mb-4 flex justify-end">
|
||||
<XMarkIcon className="h-6 w-6 cursor-pointer text-text-tertiary" onClick={onClose} />
|
||||
</div>
|
||||
<p className='mt-1 text-[13px] font-normal leading-5 text-text-tertiary'>{t('appApi.apiKeyModal.generateTips')}</p>
|
||||
<div className='my-4'>
|
||||
<InputCopy className='w-full' value={newKey?.token} />
|
||||
</div>
|
||||
<div className='my-4 flex justify-end'>
|
||||
<Button className={`shrink-0 ${s.w64}`} onClick={onClose}>
|
||||
<span className='text-xs font-medium text-text-secondary'>{t('appApi.actionMsg.ok')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</Modal >
|
||||
)
|
||||
}
|
||||
|
||||
export default SecretKeyGenerateModal
|
||||
149
dify/web/app/components/develop/secret-key/secret-key-modal.tsx
Normal file
@@ -0,0 +1,149 @@
|
||||
'use client'
|
||||
import {
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiDeleteBinLine } from '@remixicon/react'
|
||||
import { PlusIcon, XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import useSWR, { useSWRConfig } from 'swr'
|
||||
import SecretKeyGenerateModal from './secret-key-generate'
|
||||
import s from './style.module.css'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import CopyFeedback from '@/app/components/base/copy-feedback'
|
||||
import {
|
||||
createApikey as createAppApikey,
|
||||
delApikey as delAppApikey,
|
||||
fetchApiKeysList as fetchAppApiKeysList,
|
||||
} from '@/service/apps'
|
||||
import {
|
||||
createApikey as createDatasetApikey,
|
||||
delApikey as delDatasetApikey,
|
||||
fetchApiKeysList as fetchDatasetApiKeysList,
|
||||
} from '@/service/datasets'
|
||||
import type { CreateApiKeyResponse } from '@/models/app'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import useTimestamp from '@/hooks/use-timestamp'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
|
||||
type ISecretKeyModalProps = {
|
||||
isShow: boolean
|
||||
appId?: string
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const SecretKeyModal = ({
|
||||
isShow = false,
|
||||
appId,
|
||||
onClose,
|
||||
}: ISecretKeyModalProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { formatTime } = useTimestamp()
|
||||
const { currentWorkspace, isCurrentWorkspaceManager, isCurrentWorkspaceEditor } = useAppContext()
|
||||
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
||||
const [isVisible, setVisible] = useState(false)
|
||||
const [newKey, setNewKey] = useState<CreateApiKeyResponse | undefined>(undefined)
|
||||
const { mutate } = useSWRConfig()
|
||||
const commonParams = appId
|
||||
? { url: `/apps/${appId}/api-keys`, params: {} }
|
||||
: { url: '/datasets/api-keys', params: {} }
|
||||
const fetchApiKeysList = appId ? fetchAppApiKeysList : fetchDatasetApiKeysList
|
||||
const { data: apiKeysList } = useSWR(commonParams, fetchApiKeysList)
|
||||
|
||||
const [delKeyID, setDelKeyId] = useState('')
|
||||
|
||||
const onDel = async () => {
|
||||
setShowConfirmDelete(false)
|
||||
if (!delKeyID)
|
||||
return
|
||||
|
||||
const delApikey = appId ? delAppApikey : delDatasetApikey
|
||||
const params = appId
|
||||
? { url: `/apps/${appId}/api-keys/${delKeyID}`, params: {} }
|
||||
: { url: `/datasets/api-keys/${delKeyID}`, params: {} }
|
||||
await delApikey(params)
|
||||
mutate(commonParams)
|
||||
}
|
||||
|
||||
const onCreate = async () => {
|
||||
const params = appId
|
||||
? { url: `/apps/${appId}/api-keys`, body: {} }
|
||||
: { url: '/datasets/api-keys', body: {} }
|
||||
const createApikey = appId ? createAppApikey : createDatasetApikey
|
||||
const res = await createApikey(params)
|
||||
setVisible(true)
|
||||
setNewKey(res)
|
||||
mutate(commonParams)
|
||||
}
|
||||
|
||||
const generateToken = (token: string) => {
|
||||
return `${token.slice(0, 3)}...${token.slice(-20)}`
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isShow={isShow} onClose={onClose} title={`${t('appApi.apiKeyModal.apiSecretKey')}`} className={`${s.customModal} flex flex-col px-8`}>
|
||||
<div className="-mr-2 -mt-6 mb-4 flex justify-end">
|
||||
<XMarkIcon className="h-6 w-6 cursor-pointer text-text-tertiary" onClick={onClose} />
|
||||
</div>
|
||||
<p className='mt-1 shrink-0 text-[13px] font-normal leading-5 text-text-tertiary'>{t('appApi.apiKeyModal.apiSecretKeyTips')}</p>
|
||||
{!apiKeysList && <div className='mt-4'><Loading /></div>}
|
||||
{
|
||||
!!apiKeysList?.data?.length && (
|
||||
<div className='mt-4 flex grow flex-col overflow-hidden'>
|
||||
<div className='flex h-9 shrink-0 items-center border-b border-divider-regular text-xs font-semibold text-text-tertiary'>
|
||||
<div className='w-64 shrink-0 px-3'>{t('appApi.apiKeyModal.secretKey')}</div>
|
||||
<div className='w-[200px] shrink-0 px-3'>{t('appApi.apiKeyModal.created')}</div>
|
||||
<div className='w-[200px] shrink-0 px-3'>{t('appApi.apiKeyModal.lastUsed')}</div>
|
||||
<div className='grow px-3'></div>
|
||||
</div>
|
||||
<div className='grow overflow-auto'>
|
||||
{apiKeysList.data.map(api => (
|
||||
<div className='flex h-9 items-center border-b border-divider-regular text-sm font-normal text-text-secondary' key={api.id}>
|
||||
<div className='w-64 shrink-0 truncate px-3 font-mono'>{generateToken(api.token)}</div>
|
||||
<div className='w-[200px] shrink-0 truncate px-3'>{formatTime(Number(api.created_at), t('appLog.dateTimeFormat') as string)}</div>
|
||||
<div className='w-[200px] shrink-0 truncate px-3'>{api.last_used_at ? formatTime(Number(api.last_used_at), t('appLog.dateTimeFormat') as string) : t('appApi.never')}</div>
|
||||
<div className='flex grow space-x-2 px-3'>
|
||||
<CopyFeedback content={api.token} />
|
||||
{isCurrentWorkspaceManager && (
|
||||
<ActionButton
|
||||
onClick={() => {
|
||||
setDelKeyId(api.id)
|
||||
setShowConfirmDelete(true)
|
||||
}}
|
||||
>
|
||||
<RiDeleteBinLine className='h-4 w-4' />
|
||||
</ActionButton>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className='flex'>
|
||||
<Button className={`mt-4 flex shrink-0 ${s.autoWidth}`} onClick={onCreate} disabled={!currentWorkspace || !isCurrentWorkspaceEditor}>
|
||||
<PlusIcon className='mr-1 flex h-4 w-4 shrink-0' />
|
||||
<div className='text-xs font-medium text-text-secondary'>{t('appApi.apiKeyModal.createNewSecretKey')}</div>
|
||||
</Button>
|
||||
</div>
|
||||
<SecretKeyGenerateModal className='shrink-0' isShow={isVisible} onClose={() => setVisible(false)} newKey={newKey} />
|
||||
{showConfirmDelete && (
|
||||
<Confirm
|
||||
title={`${t('appApi.actionMsg.deleteConfirmTitle')}`}
|
||||
content={`${t('appApi.actionMsg.deleteConfirmTips')}`}
|
||||
isShow={showConfirmDelete}
|
||||
onConfirm={onDel}
|
||||
onCancel={() => {
|
||||
setDelKeyId('')
|
||||
setShowConfirmDelete(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Modal >
|
||||
)
|
||||
}
|
||||
|
||||
export default SecretKeyModal
|
||||
57
dify/web/app/components/develop/secret-key/style.module.css
Normal file
@@ -0,0 +1,57 @@
|
||||
.customModal {
|
||||
max-width: 800px !important;
|
||||
max-height: calc(100vh - 80px);
|
||||
}
|
||||
|
||||
.close {
|
||||
top: 1.5rem;
|
||||
right: 1.5rem;
|
||||
}
|
||||
|
||||
.trash {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.w64 {
|
||||
width: 4rem;
|
||||
}
|
||||
|
||||
.customApi {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.autoWidth {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.trashIcon {
|
||||
background-color: transparent;
|
||||
background-image: url(./assets/trash-gray.svg);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
|
||||
.trashIcon:hover {
|
||||
background-color: rgba(254, 228, 226, 1);
|
||||
background-image: url(./assets/trash-red.svg);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
|
||||
.copyIcon {
|
||||
background-image: url(./assets/copy.svg);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.copyIcon:hover {
|
||||
background-image: url(./assets/copy-hover.svg);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.copyIcon.copied {
|
||||
background-image: url(./assets/copied.svg);
|
||||
}
|
||||
65
dify/web/app/components/develop/tag.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
'use client'
|
||||
import classNames from '@/utils/classnames'
|
||||
|
||||
const variantStyles = {
|
||||
medium: 'rounded-lg px-1.5 ring-1 ring-inset',
|
||||
} as { [key: string]: string }
|
||||
|
||||
const colorStyles = {
|
||||
emerald: {
|
||||
small: 'text-emerald-500 dark:text-emerald-400',
|
||||
medium:
|
||||
'ring-emerald-300 dark:ring-emerald-400/30 bg-emerald-400/10 text-emerald-500 dark:text-emerald-400',
|
||||
},
|
||||
sky: {
|
||||
small: 'text-sky-500',
|
||||
medium:
|
||||
'ring-sky-300 bg-sky-400/10 text-sky-500 dark:ring-sky-400/30 dark:bg-sky-400/10 dark:text-sky-400',
|
||||
},
|
||||
amber: {
|
||||
small: 'text-amber-500',
|
||||
medium:
|
||||
'ring-amber-300 bg-amber-400/10 text-amber-500 dark:ring-amber-400/30 dark:bg-amber-400/10 dark:text-amber-400',
|
||||
},
|
||||
rose: {
|
||||
small: 'text-red-500 dark:text-rose-500',
|
||||
medium:
|
||||
'ring-rose-200 bg-rose-50 text-red-500 dark:ring-rose-500/20 dark:bg-rose-400/10 dark:text-rose-400',
|
||||
},
|
||||
zinc: {
|
||||
small: 'text-zinc-400 dark:text-zinc-500',
|
||||
medium:
|
||||
'ring-zinc-200 bg-zinc-50 text-zinc-500 dark:ring-zinc-500/20 dark:bg-zinc-400/10 dark:text-zinc-400',
|
||||
},
|
||||
} as { [key: string]: { [key: string]: string } }
|
||||
|
||||
const valueColorMap = {
|
||||
get: 'emerald',
|
||||
post: 'sky',
|
||||
put: 'amber',
|
||||
delete: 'rose',
|
||||
} as { [key: string]: string }
|
||||
|
||||
type ITagProps = {
|
||||
children: string
|
||||
color?: string
|
||||
variant?: string
|
||||
}
|
||||
|
||||
export function Tag({
|
||||
children,
|
||||
variant = 'medium',
|
||||
color = valueColorMap[children.toLowerCase()] ?? 'emerald',
|
||||
}: ITagProps) {
|
||||
return (
|
||||
<span
|
||||
className={classNames(
|
||||
'font-mono text-[0.625rem] font-semibold leading-6',
|
||||
variantStyles[variant],
|
||||
colorStyles[color][variant],
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
798
dify/web/app/components/develop/template/template.en.mdx
Normal file
@@ -0,0 +1,798 @@
|
||||
import { CodeGroup } from '../code.tsx'
|
||||
import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from '../md.tsx'
|
||||
|
||||
# Completion App API
|
||||
|
||||
The text generation application offers non-session support and is ideal for translation, article writing, summarization AI, and more.
|
||||
|
||||
<div>
|
||||
### Base URL
|
||||
<CodeGroup title="Code" targetCode={props.appDetail.api_base_url} />
|
||||
|
||||
### Authentication
|
||||
|
||||
The Service API uses `API-Key` authentication.
|
||||
<i>**Strongly recommend storing your API Key on the server-side, not shared or stored on the client-side, to avoid possible API-Key leakage that can lead to serious consequences.**</i>
|
||||
|
||||
For all API requests, include your API Key in the `Authorization` HTTP Header, as shown below:
|
||||
|
||||
<CodeGroup title="Code" targetCode='Authorization: Bearer {API_KEY}' />
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/completion-messages'
|
||||
method='POST'
|
||||
title='Create Completion Message'
|
||||
name='#Create-Completion-Message'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Send a request to the text generation application.
|
||||
|
||||
### Request Body
|
||||
|
||||
<Properties>
|
||||
|
||||
<Property name='inputs' type='object' key='inputs'>
|
||||
Allows the entry of various variable values defined by the App.
|
||||
The `inputs` parameter contains multiple key/value pairs, with each key corresponding to a specific variable and each value being the specific value for that variable.
|
||||
The text generation application requires at least one key/value pair to be inputted.
|
||||
- `query` (string) Required
|
||||
The input text, the content to be processed.
|
||||
</Property>
|
||||
<Property name='response_mode' type='string' key='response_mode'>
|
||||
The mode of response return, supporting:
|
||||
- `streaming` Streaming mode (recommended), implements a typewriter-like output through SSE ([Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)).
|
||||
- `blocking` Blocking mode, returns result after execution is complete. (Requests may be interrupted if the process is long)
|
||||
<i>Due to Cloudflare restrictions, the request will be interrupted without a return after 100 seconds.</i>
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
User identifier, used to define the identity of the end-user, convenient for retrieval and statistics.
|
||||
The rules are defined by the developer and need to ensure that the user identifier is unique within the application. The Service API does not share conversations created by the WebApp.
|
||||
</Property>
|
||||
<Property name='files' type='array[object]' key='files'>
|
||||
File list, suitable for inputting files combined with text understanding and answering questions, available only when the model supports Vision/Video capability.
|
||||
- `type` (string) Supported type:
|
||||
- `document` Supported types include: 'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` Supported types include: 'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` Supported types include: 'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` Supported types include: 'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` Supported types include: other file types
|
||||
- `transfer_method` (string) Transfer method:
|
||||
- `remote_url`: File URL.
|
||||
- `local_file`: Upload file.
|
||||
- `url` File URL. (Only when transfer method is `remote_url`).
|
||||
- `upload_file_id` Upload file ID. (Only when transfer method is `local_file`).
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Response
|
||||
When `response_mode` is `blocking`, return a CompletionResponse object.
|
||||
When `response_mode` is `streaming`, return a ChunkCompletionResponse stream.
|
||||
|
||||
### ChatCompletionResponse
|
||||
Returns the complete App result, `Content-Type` is `application/json`.
|
||||
- `message_id` (string) Unique message ID
|
||||
- `mode` (string) App mode, fixed as `chat`
|
||||
- `answer` (string) Complete response content
|
||||
- `metadata` (object) Metadata
|
||||
- `usage` (Usage) Model usage information
|
||||
- `retriever_resources` (array[RetrieverResource]) Citation and Attribution List
|
||||
- `created_at` (int) Message creation timestamp, e.g., 1705395332
|
||||
|
||||
### ChunkChatCompletionResponse
|
||||
Returns the stream chunks outputted by the App, `Content-Type` is `text/event-stream`.
|
||||
Each streaming chunk starts with `data:`, separated by two newline characters `\n\n`, as shown below:
|
||||
<CodeGroup>
|
||||
```streaming {{ title: 'Response' }}
|
||||
data: {"event": "message", "task_id": "900bbd43-dc0b-4383-a372-aa6e6c414227", "id": "663c5084-a254-4040-8ad3-51f2a3c1a77c", "answer": "Hi", "created_at": 1705398420}\n\n
|
||||
```
|
||||
</CodeGroup>
|
||||
The structure of the streaming chunks varies depending on the `event`:
|
||||
- `event: message` LLM returns text chunk event, i.e., the complete text is output in a chunked fashion.
|
||||
- `task_id` (string) Task ID, used for request tracking and the below Stop Generate API
|
||||
- `message_id` (string) Unique message ID
|
||||
- `answer` (string) LLM returned text chunk content
|
||||
- `created_at` (int) Creation timestamp, e.g., 1705395332
|
||||
- `event: message_end` Message end event, receiving this event means streaming has ended.
|
||||
- `task_id` (string) Task ID, used for request tracking and the below Stop Generate API
|
||||
- `message_id` (string) Unique message ID
|
||||
- `metadata` (object) Metadata
|
||||
- `usage` (Usage) Model usage information
|
||||
- `retriever_resources` (array[RetrieverResource]) Citation and Attribution List
|
||||
- `event: tts_message` TTS audio stream event, that is, speech synthesis output. The content is an audio block in Mp3 format, encoded as a base64 string. When playing, simply decode the base64 and feed it into the player. (This message is available only when auto-play is enabled)
|
||||
- `task_id` (string) Task ID, used for request tracking and the stop response interface below
|
||||
- `message_id` (string) Unique message ID
|
||||
- `audio` (string) The audio after speech synthesis, encoded in base64 text content, when playing, simply decode the base64 and feed it into the player
|
||||
- `created_at` (int) Creation timestamp, e.g.: 1705395332
|
||||
- `event: tts_message_end` TTS audio stream end event, receiving this event indicates the end of the audio stream.
|
||||
- `task_id` (string) Task ID, used for request tracking and the stop response interface below
|
||||
- `message_id` (string) Unique message ID
|
||||
- `audio` (string) The end event has no audio, so this is an empty string
|
||||
- `created_at` (int) Creation timestamp, e.g.: 1705395332
|
||||
- `event: message_replace` Message content replacement event.
|
||||
When output content moderation is enabled, if the content is flagged, then the message content will be replaced with a preset reply through this event.
|
||||
- `task_id` (string) Task ID, used for request tracking and the below Stop Generate API
|
||||
- `message_id` (string) Unique message ID
|
||||
- `answer` (string) Replacement content (directly replaces all LLM reply text)
|
||||
- `created_at` (int) Creation timestamp, e.g., 1705395332
|
||||
- `event: error`
|
||||
Exceptions that occur during the streaming process will be output in the form of stream events, and reception of an error event will end the stream.
|
||||
- `task_id` (string) Task ID, used for request tracking and the below Stop Generate API
|
||||
- `message_id` (string) Unique message ID
|
||||
- `status` (int) HTTP status code
|
||||
- `code` (string) Error code
|
||||
- `message` (string) Error message
|
||||
- `event: ping` Ping event every 10 seconds to keep the connection alive.
|
||||
|
||||
### Errors
|
||||
- 404, Conversation does not exists
|
||||
- 400, `invalid_param`, abnormal parameter input
|
||||
- 400, `app_unavailable`, App configuration unavailable
|
||||
- 400, `provider_not_initialize`, no available model credential configuration
|
||||
- 400, `provider_quota_exceeded`, model invocation quota insufficient
|
||||
- 400, `model_currently_not_support`, current model unavailable
|
||||
- 400, `completion_request_error`, text generation failed
|
||||
- 500, internal server error
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/completion-messages"
|
||||
targetCode={`curl -X POST '${props.appDetail.api_base_url}/completion-messages' \\
|
||||
--header 'Authorization: Bearer {api_key}' \\
|
||||
--header 'Content-Type: application/json' \\
|
||||
--data-raw '{
|
||||
"inputs": {"query": "Hello, world!"},
|
||||
"response_mode": "streaming",
|
||||
"user": "abc-123"
|
||||
}'`}
|
||||
/>
|
||||
### Blocking Mode
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"event": "message",
|
||||
"message_id": "9da23599-e713-473b-982c-4328d4f5c78a",
|
||||
"mode": "completion",
|
||||
"answer": "Hello World!...",
|
||||
"metadata": {
|
||||
"usage": {
|
||||
"prompt_tokens": 1033,
|
||||
"prompt_unit_price": "0.001",
|
||||
"prompt_price_unit": "0.001",
|
||||
"prompt_price": "0.0010330",
|
||||
"completion_tokens": 128,
|
||||
"completion_unit_price": "0.002",
|
||||
"completion_price_unit": "0.001",
|
||||
"completion_price": "0.0002560",
|
||||
"total_tokens": 1161,
|
||||
"total_price": "0.0012890",
|
||||
"currency": "USD",
|
||||
"latency": 0.7682376249867957
|
||||
}
|
||||
},
|
||||
"created_at": 1705407629
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
### Streaming Mode
|
||||
<CodeGroup title="Response">
|
||||
```streaming {{ title: 'Response' }}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " I", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": "'m", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " glad", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " to", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " meet", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " you", "created_at": 1679586595}
|
||||
data: {"event": "message_end", "id": "5e52ce04-874b-4d27-9045-b3bc80def685", "metadata": {"usage": {"prompt_tokens": 1033, "prompt_unit_price": "0.001", "prompt_price_unit": "0.001", "prompt_price": "0.0010330", "completion_tokens": 135, "completion_unit_price": "0.002", "completion_price_unit": "0.001", "completion_price": "0.0002700", "total_tokens": 1168, "total_price": "0.0013030", "currency": "USD", "latency": 1.381760165997548}}}
|
||||
data: {"event": "tts_message", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"}
|
||||
data: {"event": "tts_message_end", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": ""}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
<Heading
|
||||
url='/files/upload'
|
||||
method='POST'
|
||||
title='File Upload'
|
||||
name='#file-upload'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Upload a file (currently only images are supported) for use when sending messages, enabling multimodal understanding of images and text.
|
||||
Supports png, jpg, jpeg, webp, gif formats.
|
||||
<i>Uploaded files are for use by the current end-user only.</i>
|
||||
|
||||
### Request Body
|
||||
This interface requires a `multipart/form-data` request.
|
||||
- `file` (File) Required
|
||||
The file to be uploaded.
|
||||
- `user` (string) Required
|
||||
User identifier, defined by the developer's rules, must be unique within the application. The Service API does not share conversations created by the WebApp.
|
||||
|
||||
### Response
|
||||
After a successful upload, the server will return the file's ID and related information.
|
||||
- `id` (uuid) ID
|
||||
- `name` (string) File name
|
||||
- `size` (int) File size (bytes)
|
||||
- `extension` (string) File extension
|
||||
- `mime_type` (string) File mime-type
|
||||
- `created_by` (uuid) End-user ID
|
||||
- `created_at` (timestamp) Creation timestamp, e.g., 1705395332
|
||||
|
||||
### Errors
|
||||
- 400, `no_file_uploaded`, a file must be provided
|
||||
- 400, `too_many_files`, currently only one file is accepted
|
||||
- 400, `unsupported_preview`, the file does not support preview
|
||||
- 400, `unsupported_estimate`, the file does not support estimation
|
||||
- 413, `file_too_large`, the file is too large
|
||||
- 415, `unsupported_file_type`, unsupported extension, currently only document files are accepted
|
||||
- 503, `s3_connection_failed`, unable to connect to S3 service
|
||||
- 503, `s3_permission_denied`, no permission to upload files to S3
|
||||
- 503, `s3_file_too_large`, file exceeds S3 size limit
|
||||
- 500, internal server error
|
||||
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
### Request Example
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/files/upload"
|
||||
targetCode={`curl -X POST '${props.appDetail.api_base_url}/files/upload' \\
|
||||
--header 'Authorization: Bearer {api_key}' \\
|
||||
--form 'file=@localfile;type=image/[png|jpeg|jpg|webp|gif]' \\
|
||||
--form 'user=abc-123'`}
|
||||
/>
|
||||
|
||||
|
||||
### Response Example
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"id": "72fa9618-8f89-4a37-9b33-7e1178a24a67",
|
||||
"name": "example.png",
|
||||
"size": 1024,
|
||||
"extension": "png",
|
||||
"mime_type": "image/png",
|
||||
"created_by": "6ad1ab0a-73ff-4ac1-b9e4-cdb312f71f13",
|
||||
"created_at": 1577836800,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/files/:file_id/preview'
|
||||
method='GET'
|
||||
title='File Preview'
|
||||
name='#file-preview'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Preview or download uploaded files. This endpoint allows you to access files that have been previously uploaded via the File Upload API.
|
||||
|
||||
<i>Files can only be accessed if they belong to messages within the requesting application.</i>
|
||||
|
||||
### Path Parameters
|
||||
- `file_id` (string) Required
|
||||
The unique identifier of the file to preview, obtained from the File Upload API response.
|
||||
|
||||
### Query Parameters
|
||||
- `as_attachment` (boolean) Optional
|
||||
Whether to force download the file as an attachment. Default is `false` (preview in browser).
|
||||
|
||||
### Response
|
||||
Returns the file content with appropriate headers for browser display or download.
|
||||
- `Content-Type` Set based on file mime type
|
||||
- `Content-Length` File size in bytes (if available)
|
||||
- `Content-Disposition` Set to "attachment" if `as_attachment=true`
|
||||
- `Cache-Control` Caching headers for performance
|
||||
- `Accept-Ranges` Set to "bytes" for audio/video files
|
||||
|
||||
### Errors
|
||||
- 400, `invalid_param`, abnormal parameter input
|
||||
- 403, `file_access_denied`, file access denied or file does not belong to current application
|
||||
- 404, `file_not_found`, file not found or has been deleted
|
||||
- 500, internal server error
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
### Request Example
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/files/:file_id/preview"
|
||||
targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\
|
||||
--header 'Authorization: Bearer {api_key}'`}
|
||||
/>
|
||||
|
||||
### Download as Attachment
|
||||
<CodeGroup
|
||||
title="Download
|
||||
Request"
|
||||
tag="GET"
|
||||
label="/files/:file_id/preview?as_attachment=true"
|
||||
targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\
|
||||
--header 'Authorization: Bearer {api_key}' \\
|
||||
--output downloaded_file.png`}
|
||||
/>
|
||||
|
||||
### Response Headers Example
|
||||
<CodeGroup title="Response Headers">
|
||||
```http {{ title: 'Headers - Image Preview' }}
|
||||
Content-Type: image/png
|
||||
Content-Length: 1024
|
||||
Cache-Control: public, max-age=3600
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Download Response Headers
|
||||
<CodeGroup title="Download Response Headers">
|
||||
```http {{ title: 'Headers - File Download' }}
|
||||
Content-Type: image/png
|
||||
Content-Length: 1024
|
||||
Content-Disposition: attachment; filename*=UTF-8''example.png
|
||||
Cache-Control: public, max-age=3600
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/completion-messages/:task_id/stop'
|
||||
method='POST'
|
||||
title='Stop Generate'
|
||||
name='#stop-generatebacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Only supported in streaming mode.
|
||||
### Path
|
||||
- `task_id` (string) Task ID, can be obtained from the streaming chunk return
|
||||
Request Body
|
||||
- `user` (string) Required
|
||||
User identifier, used to define the identity of the end-user, must be consistent with the user passed in the send message interface. The Service API does not share conversations created by the WebApp.
|
||||
### Response
|
||||
- `result` (string) Always returns "success"
|
||||
</Col>
|
||||
<Col sticky>
|
||||
### Request Example
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/completion-messages/:task_id/stop"
|
||||
targetCode={`curl -X POST '${props.appDetail.api_base_url}/completion-messages/:task_id/stop' \\
|
||||
-H 'Authorization: Bearer {api_key}' \\
|
||||
-H 'Content-Type: application/json' \\
|
||||
--data-raw '{ "user": "abc-123"}'`}
|
||||
/>
|
||||
|
||||
### Response Example
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/messages/:message_id/feedbacks'
|
||||
method='POST'
|
||||
title='Message Feedback'
|
||||
name='#feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
End-users can provide feedback messages, facilitating application developers to optimize expected outputs.
|
||||
|
||||
### Path
|
||||
<Properties>
|
||||
<Property name='message_id' type='string' key='message_id'>
|
||||
Message ID
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Request Body
|
||||
|
||||
<Properties>
|
||||
<Property name='rating' type='string' key='rating'>
|
||||
Upvote as `like`, downvote as `dislike`, revoke upvote as `null`
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
User identifier, defined by the developer's rules, must be unique within the application.
|
||||
</Property>
|
||||
<Property name='content' type='string' key='content'>
|
||||
The specific content of message feedback.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Response
|
||||
- `result` (string) Always returns "success"
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/messages/:message_id/feedbacks"
|
||||
targetCode={`curl -X POST '${props.appDetail.api_base_url}/messages/:message_id/feedbacks \\
|
||||
--header 'Authorization: Bearer {api_key}' \\
|
||||
--header 'Content-Type: application/json' \\
|
||||
--data-raw '{
|
||||
"rating": "like",
|
||||
"user": "abc-123",
|
||||
"content": "message feedback information"
|
||||
}'`}
|
||||
/>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/app/feedbacks'
|
||||
method='GET'
|
||||
title='Get feedbacks of application'
|
||||
name='#app-feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Get application's feedbacks.
|
||||
|
||||
### Query
|
||||
<Properties>
|
||||
<Property name='page' type='string' key='page'>
|
||||
(optional)pagination,default:1
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Properties>
|
||||
<Property name='limit' type='string' key='limit'>
|
||||
(optional) records per page default:20
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Response
|
||||
- `data` (List) return apps feedback list.
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/app/feedbacks"
|
||||
targetCode={`curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20'`}
|
||||
/>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "8c0fbed8-e2f9-49ff-9f0e-15a35bdd0e25",
|
||||
"app_id": "f252d396-fe48-450e-94ec-e184218e7346",
|
||||
"conversation_id": "2397604b-9deb-430e-b285-4726e51fd62d",
|
||||
"message_id": "709c0b0f-0a96-4a4e-91a4-ec0889937b11",
|
||||
"rating": "like",
|
||||
"content": "message feedback information-3",
|
||||
"from_source": "user",
|
||||
"from_end_user_id": "74286412-9a1a-42c1-929c-01edb1d381d5",
|
||||
"from_account_id": null,
|
||||
"created_at": "2025-04-24T09:24:38",
|
||||
"updated_at": "2025-04-24T09:24:38"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/text-to-audio'
|
||||
method='POST'
|
||||
title='Text to Audio'
|
||||
name='#audio'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Text to speech.
|
||||
|
||||
### Request Body
|
||||
|
||||
<Properties>
|
||||
<Property name='message_id' type='str' key='message_id'>
|
||||
For text messages generated by Dify, simply pass the generated message-id directly. The backend will use the message-id to look up the corresponding content and synthesize the voice information directly. If both message_id and text are provided simultaneously, the message_id is given priority.
|
||||
</Property>
|
||||
<Property name='text' type='str' key='text'>
|
||||
Speech generated content.
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
The user identifier, defined by the developer, must ensure uniqueness within the app.
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/text-to-audio"
|
||||
targetCode={`curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \\
|
||||
--header 'Authorization: Bearer {api_key}' \\
|
||||
--header 'Content-Type: application/json' \\
|
||||
--data-raw '{
|
||||
"message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",
|
||||
"text": "Hello Dify",
|
||||
"user": "abc-123"
|
||||
}'`}
|
||||
/>
|
||||
|
||||
<CodeGroup title="headers">
|
||||
```json {{ title: 'headers' }}
|
||||
{
|
||||
"Content-Type": "audio/wav"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/info'
|
||||
method='GET'
|
||||
title='Get Application Basic Information'
|
||||
name='#info'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Used to get basic information about this application
|
||||
|
||||
### Response
|
||||
- `name` (string) application name
|
||||
- `description` (string) application description
|
||||
- `tags` (array[string]) application tags
|
||||
- `mode` (string) application mode
|
||||
- `author_name` (string) author name
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/info"
|
||||
targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\
|
||||
-H 'Authorization: Bearer {api_key}'`}
|
||||
/>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"name": "My App",
|
||||
"description": "This is my app.",
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
],
|
||||
"mode": "chat",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/parameters'
|
||||
method='GET'
|
||||
title='Get Application Parameters Information'
|
||||
name='#parameters'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Used at the start of entering the page to obtain information such as features, input parameter names, types, and default values.
|
||||
|
||||
### Response
|
||||
- `opening_statement` (string) Opening statement
|
||||
- `suggested_questions` (array[string]) List of suggested questions for the opening
|
||||
- `suggested_questions_after_answer` (object) Suggest questions after enabling the answer.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `speech_to_text` (object) Speech to text
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `retriever_resource` (object) Citation and Attribution
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `annotation_reply` (object) Annotation reply
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `user_input_form` (array[object]) User input form configuration
|
||||
- `text-input` (object) Text input control
|
||||
- `label` (string) Variable display label name
|
||||
- `variable` (string) Variable ID
|
||||
- `required` (bool) Whether it is required
|
||||
- `default` (string) Default value
|
||||
- `paragraph` (object) Paragraph text input control
|
||||
- `label` (string) Variable display label name
|
||||
- `variable` (string) Variable ID
|
||||
- `required` (bool) Whether it is required
|
||||
- `default` (string) Default value
|
||||
- `select` (object) Dropdown control
|
||||
- `label` (string) Variable display label name
|
||||
- `variable` (string) Variable ID
|
||||
- `required` (bool) Whether it is required
|
||||
- `default` (string) Default value
|
||||
- `options` (array[string]) Option values
|
||||
- `file_upload` (object) File upload configuration
|
||||
- `document` (object) Document settings
|
||||
Currently only supports document types: `txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Document number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `image` (object) Image settings
|
||||
Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Image number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `audio` (object) Audio settings
|
||||
Currently only supports audio types: `mp3`, `m4a`, `wav`, `webm`, `amr`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Audio number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `video` (object) Video settings
|
||||
Currently only supports video types: `mp4`, `mov`, `mpeg`, `mpga`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Video number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `custom` (object) Custom settings
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Custom number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `system_parameters` (object) System parameters
|
||||
- `file_size_limit` (int) Document upload size limit (MB)
|
||||
- `image_file_size_limit` (int) Image file upload size limit (MB)
|
||||
- `audio_file_size_limit` (int) Audio file upload size limit (MB)
|
||||
- `video_file_size_limit` (int) Video file upload size limit (MB)
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/parameters"
|
||||
targetCode={` curl -X GET '${props.appDetail.api_base_url}/parameters'`}
|
||||
/>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"opening_statement": "Hello!",
|
||||
"suggested_questions_after_answer": {
|
||||
"enabled": true
|
||||
},
|
||||
"speech_to_text": {
|
||||
"enabled": true
|
||||
},
|
||||
"retriever_resource": {
|
||||
"enabled": true
|
||||
},
|
||||
"annotation_reply": {
|
||||
"enabled": true
|
||||
},
|
||||
"user_input_form": [
|
||||
{
|
||||
"paragraph": {
|
||||
"label": "Query",
|
||||
"variable": "query",
|
||||
"required": true,
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"file_upload": {
|
||||
"image": {
|
||||
"enabled": false,
|
||||
"number_limits": 3,
|
||||
"detail": "high",
|
||||
"transfer_methods": [
|
||||
"remote_url",
|
||||
"local_file"
|
||||
]
|
||||
}
|
||||
},
|
||||
"system_parameters": {
|
||||
"file_size_limit": 15,
|
||||
"image_file_size_limit": 10,
|
||||
"audio_file_size_limit": 50,
|
||||
"video_file_size_limit": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='Get Application WebApp Settings'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Used to get the WebApp settings of the application.
|
||||
### Response
|
||||
- `title` (string) WebApp name
|
||||
- `chat_color_theme` (string) Chat color theme, in hex format
|
||||
- `chat_color_theme_inverted` (bool) Whether the chat color theme is inverted
|
||||
- `icon_type` (string) Icon type, `emoji` - emoji, `image` - picture
|
||||
- `icon` (string) Icon. If it's `emoji` type, it's an emoji symbol; if it's `image` type, it's an image URL.
|
||||
- `icon_background` (string) Background color in hex format
|
||||
- `icon_url` (string) Icon URL
|
||||
- `description` (string) Description
|
||||
- `copyright` (string) Copyright information
|
||||
- `privacy_policy` (string) Privacy policy link
|
||||
- `custom_disclaimer` (string) Custom disclaimer
|
||||
- `default_language` (string) Default language
|
||||
- `show_workflow_steps` (bool) Whether to show workflow details
|
||||
- `use_icon_as_answer_icon` (bool) Whether to replace 🤖 in chat with the WebApp icon
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/site"
|
||||
targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\
|
||||
-H 'Authorization: Bearer {api_key}'`}
|
||||
/>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"chat_color_theme": "#ff4a4a",
|
||||
"chat_color_theme_inverted": false,
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
"use_icon_as_answer_icon": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||
795
dify/web/app/components/develop/template/template.ja.mdx
Normal file
@@ -0,0 +1,795 @@
|
||||
import { CodeGroup } from '../code.tsx'
|
||||
import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from '../md.tsx'
|
||||
|
||||
# Completion アプリ API
|
||||
|
||||
テキスト生成アプリケーションはセッションレスをサポートし、翻訳、記事作成、要約 AI 等に最適です。
|
||||
|
||||
<div>
|
||||
### ベース URL
|
||||
<CodeGroup title="コード" targetCode={props.appDetail.api_base_url} />
|
||||
|
||||
### 認証
|
||||
|
||||
サービス API は `API-Key` 認証を使用します。
|
||||
<i>**API キーの漏洩による重大な結果を避けるため、API キーはサーバーサイドに保存し、クライアントサイドでは共有や保存しないことを強く推奨します。**</i>
|
||||
|
||||
すべての API リクエストで、以下のように `Authorization` HTTP ヘッダーに API キーを含めてください:
|
||||
|
||||
<CodeGroup title="コード" targetCode='Authorization: Bearer {API_KEY}' />
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/completion-messages'
|
||||
method='POST'
|
||||
title='完了メッセージの作成'
|
||||
name='#Create-Completion-Message'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
テキスト生成アプリケーションにリクエストを送信します。
|
||||
|
||||
### リクエストボディ
|
||||
|
||||
<Properties>
|
||||
|
||||
<Property name='inputs' type='object' key='inputs'>
|
||||
アプリで定義された各種変数値を入力できます。
|
||||
`inputs`パラメータには複数のキー/値ペアが含まれ、各キーは特定の変数に対応し、各値はその変数の具体的な値となります。
|
||||
テキスト生成アプリケーションでは、少なくとも1つのキー/値ペアの入力が必要です。
|
||||
- `query` (string) 必須
|
||||
入力テキスト、処理される内容。
|
||||
</Property>
|
||||
<Property name='response_mode' type='string' key='response_mode'>
|
||||
レスポンス返却モード、以下をサポート:
|
||||
- `streaming` ストリーミングモード(推奨)、SSE([Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events))によるタイプライター風の出力を実装。
|
||||
- `blocking` ブロッキングモード、実行完了後に結果を返却。(処理が長い場合はリクエストが中断される可能性があります)
|
||||
<i>Cloudflareの制限により、100秒後に返却なしで中断されます。</i>
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
ユーザー識別子、エンドユーザーの身元を定義し、取得や統計に使用します。
|
||||
アプリケーション内で開発者が一意に定義する必要があります。
|
||||
</Property>
|
||||
<Property name='files' type='array[object]' key='files'>
|
||||
ファイルリスト、モデルが Vision/Video 機能をサポートしている場合に限り、ファイルをテキスト理解および質問応答に組み合わせて入力するのに適しています。
|
||||
- `type` (string) サポートされるタイプ:
|
||||
- `document` サポートされるタイプには以下が含まれます:'TXT', 'MD', 'MARKDOWN', 'MDX', 'PDF', 'HTML', 'XLSX', 'XLS', 'VTT', 'PROPERTIES', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` サポートされるタイプには以下が含まれます:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
- `audio` サポートされるタイプには以下が含まれます:'MP3', 'M4A', 'WAV', 'WEBM', 'MPGA'
|
||||
- `video` サポートされるタイプには以下が含まれます:'MP4', 'MOV', 'MPEG', 'WEBM'
|
||||
- `custom` サポートされるタイプには以下が含まれます:その他のファイルタイプ
|
||||
- `transfer_method` (string) 転送方法:
|
||||
- `remote_url`: ファイルのURL。
|
||||
- `local_file`: ファイルをアップロード。
|
||||
- `url` ファイルのURL。(転送方法が `remote_url` の場合のみ)。
|
||||
- `upload_file_id` アップロードされたファイルID。(転送方法が `local_file` の場合のみ)。
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### レスポンス
|
||||
`response_mode`が`blocking`の場合、CompletionResponseオブジェクトを返却します。
|
||||
`response_mode`が`streaming`の場合、ChunkCompletionResponseストリームを返却します。
|
||||
|
||||
### ChatCompletionResponse
|
||||
アプリの完全な結果を返却、`Content-Type`は`application/json`です。
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `mode` (string) アプリモード、固定で`chat`
|
||||
- `answer` (string) 完全な応答内容
|
||||
- `metadata` (object) メタデータ
|
||||
- `usage` (Usage) モデル使用情報
|
||||
- `retriever_resources` (array[RetrieverResource]) 引用と帰属のリスト
|
||||
- `created_at` (int) メッセージ作成タイムスタンプ、例:1705395332
|
||||
|
||||
### ChunkChatCompletionResponse
|
||||
アプリが出力するストリームチャンクを返却、`Content-Type`は`text/event-stream`です。
|
||||
各ストリーミングチャンクは`data:`で始まり、2つの改行文字`\n\n`で区切られます:
|
||||
<CodeGroup>
|
||||
```streaming {{ title: 'Response' }}
|
||||
data: {"event": "message", "task_id": "900bbd43-dc0b-4383-a372-aa6e6c414227", "id": "663c5084-a254-4040-8ad3-51f2a3c1a77c", "answer": "Hi", "created_at": 1705398420}\n\n
|
||||
```
|
||||
</CodeGroup>
|
||||
ストリーミングチャンクの構造は`event`によって異なります:
|
||||
- `event: message` LLMがテキストチャンクを返すイベント、つまり完全なテキストがチャンク形式で出力されます。
|
||||
- `task_id` (string) タスクID、リクエストの追跡と以下の生成停止APIに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `answer` (string) LLMが返したテキストチャンクの内容
|
||||
- `created_at` (int) 作成タイムスタンプ、例:1705395332
|
||||
- `event: message_end` メッセージ終了イベント、このイベントを受信するとストリーミングが終了したことを意味します。
|
||||
- `task_id` (string) タスクID、リクエストの追跡と以下の生成停止APIに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `metadata` (object) メタデータ
|
||||
- `usage` (Usage) モデル使用情報
|
||||
- `retriever_resources` (array[RetrieverResource]) 引用と帰属のリスト
|
||||
- `event: tts_message` TTS音声ストリームイベント、つまり音声合成出力。内容はMp3形式の音声ブロックで、base64文字列としてエンコードされています。再生時は単にbase64をデコードしてプレーヤーに供給するだけです。(このメッセージは自動再生が有効な場合のみ利用可能)
|
||||
- `task_id` (string) タスクID、リクエストの追跡と以下の応答停止インターフェースに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `audio` (string) 音声合成後の音声、base64テキストコンテンツとしてエンコード、再生時は単にbase64をデコードしてプレーヤーに供給
|
||||
- `created_at` (int) 作成タイムスタンプ、例:1705395332
|
||||
- `event: tts_message_end` TTS音声ストリーム終了イベント、このイベントを受信すると音声ストリームが終了したことを示します。
|
||||
- `task_id` (string) タスクID、リクエストの追跡と以下の応答停止インターフェースに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `audio` (string) 終了イベントには音声がないため、空文字列
|
||||
- `created_at` (int) 作成タイムスタンプ、例:1705395332
|
||||
- `event: message_replace` メッセージ内容置換イベント。
|
||||
出力内容のモデレーションが有効な場合、コンテンツがフラグ付けされると、このイベントを通じてメッセージ内容が事前設定された返信に置き換えられます。
|
||||
- `task_id` (string) タスクID、リクエストの追跡と以下の生成停止APIに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `answer` (string) 置換内容(LLMの返信テキストすべてを直接置換)
|
||||
- `created_at` (int) 作成タイムスタンプ、例:1705395332
|
||||
- `event: error`
|
||||
ストリーミング処理中に発生した例外は、ストリームイベントの形式で出力され、エラーイベントを受信するとストリームが終了します。
|
||||
- `task_id` (string) タスクID、リクエストの追跡と以下の生成停止APIに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `status` (int) HTTPステータスコード
|
||||
- `code` (string) エラーコード
|
||||
- `message` (string) エラーメッセージ
|
||||
- `event: ping` 接続を維持するため10秒ごとのPingイベント。
|
||||
|
||||
### エラー
|
||||
- 404, 会話が存在しません
|
||||
- 400, `invalid_param`, パラメータ入力異常
|
||||
- 400, `app_unavailable`, アプリ設定が利用できません
|
||||
- 400, `provider_not_initialize`, 利用可能なモデル認証情報設定がありません
|
||||
- 400, `provider_quota_exceeded`, モデル呼び出しクォータ不足
|
||||
- 400, `model_currently_not_support`, 現在のモデルは利用できません
|
||||
- 400, `completion_request_error`, テキスト生成に失敗しました
|
||||
- 500, 内部サーバーエラー
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/completion-messages"
|
||||
targetCode={`curl -X POST '${props.appDetail.api_base_url}/completion-messages' \\
|
||||
--header 'Authorization: Bearer {api_key}' \\
|
||||
--header 'Content-Type: application/json' \\
|
||||
--data-raw '{
|
||||
"inputs": {"query": "Hello, world!"},
|
||||
"response_mode": "streaming",
|
||||
"user": "abc-123"
|
||||
}'`}
|
||||
/>
|
||||
### ブロッキングモード
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"event": "message",
|
||||
"message_id": "9da23599-e713-473b-982c-4328d4f5c78a",
|
||||
"mode": "completion",
|
||||
"answer": "Hello World!...",
|
||||
"metadata": {
|
||||
"usage": {
|
||||
"prompt_tokens": 1033,
|
||||
"prompt_unit_price": "0.001",
|
||||
"prompt_price_unit": "0.001",
|
||||
"prompt_price": "0.0010330",
|
||||
"completion_tokens": 128,
|
||||
"completion_unit_price": "0.002",
|
||||
"completion_price_unit": "0.001",
|
||||
"completion_price": "0.0002560",
|
||||
"total_tokens": 1161,
|
||||
"total_price": "0.0012890",
|
||||
"currency": "USD",
|
||||
"latency": 0.7682376249867957
|
||||
}
|
||||
},
|
||||
"created_at": 1705407629
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
### ストリーミングモード
|
||||
<CodeGroup title="Response">
|
||||
```streaming {{ title: 'Response' }}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " I", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": "'m", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " glad", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " to", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id" : "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " meet", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id" : "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " you", "created_at": 1679586595}
|
||||
data: {"event": "message_end", "id": "5e52ce04-874b-4d27-9045-b3bc80def685", "metadata": {"usage": {"prompt_tokens": 1033, "prompt_unit_price": "0.001", "prompt_price_unit": "0.001", "prompt_price": "0.0010330", "completion_tokens": 135, "completion_unit_price": "0.002", "completion_price_unit": "0.001", "completion_price": "0.0002700", "total_tokens": 1168, "total_price": "0.0013030", "currency": "USD", "latency": 1.381760165997548}}}
|
||||
data: {"event": "tts_message", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"}
|
||||
data: {"event": "tts_message_end", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": ""}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
<Heading
|
||||
url='/files/upload'
|
||||
method='POST'
|
||||
title='ファイルアップロード'
|
||||
name='#file-upload'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
メッセージ送信時に使用するファイル(現在は画像のみ対応)をアップロードし、画像とテキストのマルチモーダルな理解を可能にします。
|
||||
png、jpg、jpeg、webp、gif 形式に対応しています。
|
||||
<i>アップロードされたファイルは、現在のエンドユーザーのみが使用できます。</i>
|
||||
|
||||
### リクエストボディ
|
||||
このインターフェースは`multipart/form-data`リクエストが必要です。
|
||||
- `file` (File) 必須
|
||||
アップロードするファイル。
|
||||
- `user` (string) 必須
|
||||
開発者のルールで定義されたユーザー識別子。アプリケーション内で一意である必要があります。サービス API は WebApp によって作成された会話を共有しません。
|
||||
|
||||
### レスポンス
|
||||
アップロードが成功すると、サーバーはファイルの ID と関連情報を返します。
|
||||
- `id` (uuid) ID
|
||||
- `name` (string) ファイル名
|
||||
- `size` (int) ファイルサイズ(バイト)
|
||||
- `extension` (string) ファイル拡張子
|
||||
- `mime_type` (string) ファイルの MIME タイプ
|
||||
- `created_by` (uuid) エンドユーザーID
|
||||
- `created_at` (timestamp) 作成タイムスタンプ、例:1705395332
|
||||
|
||||
### エラー
|
||||
- 400, `no_file_uploaded`, ファイルを提供する必要があります
|
||||
- 400, `too_many_files`, 現在は 1 つのファイルのみ受け付けています
|
||||
- 400, `unsupported_preview`, ファイルがプレビューに対応していません
|
||||
- 400, `unsupported_estimate`, ファイルが推定に対応していません
|
||||
- 413, `file_too_large`, ファイルが大きすぎます
|
||||
- 415, `unsupported_file_type`, サポートされていない拡張子です。現在はドキュメントファイルのみ受け付けています
|
||||
- 503, `s3_connection_failed`, S3 サービスに接続できません
|
||||
- 503, `s3_permission_denied`, S3 へのファイルアップロード権限がありません
|
||||
- 503, `s3_file_too_large`, ファイルが S3 のサイズ制限を超えています
|
||||
- 500, 内部サーバーエラー
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
### リクエスト例
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/files/upload"
|
||||
targetCode={`curl -X POST '${props.appDetail.api_base_url}/files/upload' \\
|
||||
--header 'Authorization: Bearer {api_key}' \\
|
||||
--form 'file=@localfile;type=image/[png|jpeg|jpg|webp|gif]' \\
|
||||
--form 'user=abc-123'`}
|
||||
/>
|
||||
|
||||
|
||||
### レスポンス例
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"id": "72fa9618-8f89-4a37-9b33-7e1178a24a67",
|
||||
"name": "example.png",
|
||||
"size": 1024,
|
||||
"extension": "png",
|
||||
"mime_type": "image/png",
|
||||
"created_by": "6ad1ab0a-73ff-4ac1-b9e4-cdb312f71f13",
|
||||
"created_at": 1577836800,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/files/:file_id/preview'
|
||||
method='GET'
|
||||
title='ファイルプレビュー'
|
||||
name='#file-preview'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
アップロードされたファイルをプレビューまたはダウンロードします。このエンドポイントを使用すると、以前にファイルアップロード API でアップロードされたファイルにアクセスできます。
|
||||
|
||||
<i>ファイルは、リクエストしているアプリケーションのメッセージ範囲内にある場合のみアクセス可能です。</i>
|
||||
|
||||
### パスパラメータ
|
||||
- `file_id` (string) 必須
|
||||
プレビューするファイルの一意識別子。ファイルアップロード API レスポンスから取得します。
|
||||
|
||||
### クエリパラメータ
|
||||
- `as_attachment` (boolean) オプション
|
||||
ファイルを添付ファイルとして強制ダウンロードするかどうか。デフォルトは `false`(ブラウザでプレビュー)。
|
||||
|
||||
### レスポンス
|
||||
ブラウザ表示またはダウンロード用の適切なヘッダー付きでファイル内容を返します。
|
||||
- `Content-Type` ファイル MIME タイプに基づいて設定
|
||||
- `Content-Length` ファイルサイズ(バイト、利用可能な場合)
|
||||
- `Content-Disposition` `as_attachment=true` の場合は "attachment" に設定
|
||||
- `Cache-Control` パフォーマンス向上のためのキャッシュヘッダー
|
||||
- `Accept-Ranges` 音声/動画ファイルの場合は "bytes" に設定
|
||||
|
||||
### エラー
|
||||
- 400, `invalid_param`, パラメータ入力異常
|
||||
- 403, `file_access_denied`, ファイルアクセス拒否またはファイルが現在のアプリケーションに属していません
|
||||
- 404, `file_not_found`, ファイルが見つからないか削除されています
|
||||
- 500, サーバー内部エラー
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
### リクエスト例
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/files/:file_id/preview"
|
||||
targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\
|
||||
--header 'Authorization: Bearer {api_key}'`}
|
||||
/>
|
||||
|
||||
### 添付ファイルとしてダウンロード
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/files/:file_id/preview"
|
||||
targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\
|
||||
--header 'Authorization: Bearer {api_key}' \\
|
||||
--output downloaded_file.png`}
|
||||
/>
|
||||
|
||||
### レスポンスヘッダー例
|
||||
<CodeGroup title="Response Headers">
|
||||
```http {{ title: 'ヘッダー - 画像プレビュー' }}
|
||||
Content-Type: image/png
|
||||
Content-Length: 1024
|
||||
Cache-Control: public, max-age=3600
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### ファイルダウンロードレスポンスヘッダー
|
||||
<CodeGroup title="Response Headers">
|
||||
```http {{ title: 'ヘッダー - ファイルダウンロード' }}
|
||||
Content-Type: image/png
|
||||
Content-Length: 1024
|
||||
Content-Disposition: attachment; filename*=UTF-8''example.png
|
||||
Cache-Control: public, max-age=3600
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/completion-messages/:task_id/stop'
|
||||
method='POST'
|
||||
title='生成の停止'
|
||||
name='#stop-generatebacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
ストリーミングモードでのみサポートされています。
|
||||
### パス
|
||||
- `task_id` (string) タスク ID、ストリーミングチャンクの返信から取得可能
|
||||
リクエストボディ
|
||||
- `user` (string) 必須
|
||||
ユーザー識別子。エンドユーザーの身元を定義するために使用され、メッセージ送信インターフェースで渡されたユーザーと一致する必要があります。サービス API は WebApp によって作成された会話を共有しません。
|
||||
### レスポンス
|
||||
- `result` (string) 常に"success"を返します
|
||||
</Col>
|
||||
<Col sticky>
|
||||
### リクエスト例
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/completion-messages/:task_id/stop"
|
||||
targetCode={`curl -X POST '${props.appDetail.api_base_url}/completion-messages/:task_id/stop' \\
|
||||
-H 'Authorization: Bearer {api_key}' \\
|
||||
-H 'Content-Type: application/json' \\
|
||||
--data-raw '{ "user": "abc-123"}'`}
|
||||
/>
|
||||
|
||||
### レスポンス例
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/messages/:message_id/feedbacks'
|
||||
method='POST'
|
||||
title='メッセージフィードバック'
|
||||
name='#feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
エンドユーザーはフィードバックメッセージを提供でき、アプリケーション開発者が期待される出力を最適化するのに役立ちます。
|
||||
|
||||
### パス
|
||||
<Properties>
|
||||
<Property name='message_id' type='string' key='message_id'>
|
||||
メッセージID
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### リクエストボディ
|
||||
|
||||
<Properties>
|
||||
<Property name='rating' type='string' key='rating'>
|
||||
高評価は`like`、低評価は`dislike`、高評価の取り消しは`null`
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
開発者のルールで定義されたユーザー識別子。アプリケーション内で一意である必要があります。
|
||||
</Property>
|
||||
<Property name='content' type='string' key='content'>
|
||||
メッセージのフィードバックです。
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### レスポンス
|
||||
- `result` (string) 常に"success"を返します
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/messages/:message_id/feedbacks"
|
||||
targetCode={`curl -X POST '${props.appDetail.api_base_url}/messages/:message_id/feedbacks \\
|
||||
--header 'Authorization: Bearer {api_key}' \\
|
||||
--header 'Content-Type: application/json' \\
|
||||
--data-raw '{
|
||||
"rating": "like",
|
||||
"user": "abc-123",
|
||||
"content": "message feedback information"
|
||||
}'`}
|
||||
/>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/app/feedbacks'
|
||||
method='GET'
|
||||
title='アプリのメッセージの「いいね」とフィードバックを取得'
|
||||
name='#app-feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
アプリのエンドユーザーからのフィードバックや「いいね」を取得します。
|
||||
|
||||
### クエリ
|
||||
<Properties>
|
||||
<Property name='page' type='string' key='page'>
|
||||
(任意)ページ番号。デフォルト値:1
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Properties>
|
||||
<Property name='limit' type='string' key='limit'>
|
||||
(任意)1ページあたりの件数。デフォルト値:20
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### レスポンス
|
||||
- `data` (リスト) このアプリの「いいね」とフィードバックの一覧を返します。
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/app/feedbacks"
|
||||
targetCode={`curl -X GET '${props.appDetail.api_base_url}/app/feedbacks?page=1&limit=20'`}
|
||||
/>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "8c0fbed8-e2f9-49ff-9f0e-15a35bdd0e25",
|
||||
"app_id": "f252d396-fe48-450e-94ec-e184218e7346",
|
||||
"conversation_id": "2397604b-9deb-430e-b285-4726e51fd62d",
|
||||
"message_id": "709c0b0f-0a96-4a4e-91a4-ec0889937b11",
|
||||
"rating": "like",
|
||||
"content": "message feedback information-3",
|
||||
"from_source": "user",
|
||||
"from_end_user_id": "74286412-9a1a-42c1-929c-01edb1d381d5",
|
||||
"from_account_id": null,
|
||||
"created_at": "2025-04-24T09:24:38",
|
||||
"updated_at": "2025-04-24T09:24:38"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/text-to-audio'
|
||||
method='POST'
|
||||
title='テキストから音声'
|
||||
name='#text-to-audio'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
テキストを音声に変換します。
|
||||
|
||||
### リクエストボディ
|
||||
|
||||
<Properties>
|
||||
<Property name='message_id' type='str' key='message_id'>
|
||||
Difyが生成したテキストメッセージの場合、生成されたmessage-idを直接渡すだけです。バックエンドはmessage-idを使用して対応するコンテンツを検索し、音声情報を直接合成します。message_idとtextの両方が同時に提供された場合、message_idが優先されます。
|
||||
</Property>
|
||||
<Property name='text' type='str' key='text'>
|
||||
音声生成コンテンツ。
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
開発者が定義したユーザー識別子。アプリ内で一意性を確保する必要があります。
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="POST"
|
||||
label="/text-to-audio"
|
||||
targetCode={`curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \\
|
||||
--header 'Authorization: Bearer {api_key}' \\
|
||||
--header 'Content-Type: application/json' \\
|
||||
--data-raw '{
|
||||
"message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",
|
||||
"text": "Hello Dify",
|
||||
"user": "abc-123"
|
||||
}'`}
|
||||
/>
|
||||
|
||||
<CodeGroup title="headers">
|
||||
```json {{ title: 'headers' }}
|
||||
{
|
||||
"Content-Type": "audio/wav"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/info'
|
||||
method='GET'
|
||||
title='アプリケーションの基本情報を取得'
|
||||
name='#info'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
このアプリケーションの基本情報を取得するために使用されます
|
||||
|
||||
### Response
|
||||
- `name` (string) アプリケーションの名前
|
||||
- `description` (string) アプリケーションの説明
|
||||
- `tags` (array[string]) アプリケーションのタグ
|
||||
- `mode` (string) アプリケーションのモード
|
||||
- `author_name` (string) 作者の名前
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/info"
|
||||
targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\
|
||||
-H 'Authorization: Bearer {api_key}'`}
|
||||
/>
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"name": "My App",
|
||||
"description": "This is my app.",
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
],
|
||||
"mode": "chat",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/parameters'
|
||||
method='GET'
|
||||
title='アプリケーションのパラメータ情報を取得'
|
||||
name='#parameters'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
ページ開始時に、機能、入力パラメータ名、タイプ、デフォルト値などの情報を取得するために使用されます。
|
||||
|
||||
### レスポンス
|
||||
- `opening_statement` (string) 開始文
|
||||
- `suggested_questions` (array[string]) 開始時の提案質問リスト
|
||||
- `suggested_questions_after_answer` (object) 回答後の提案質問を有効にします。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `speech_to_text` (object) 音声からテキスト
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `retriever_resource` (object) 引用と帰属
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `annotation_reply` (object) 注釈付き返信
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `user_input_form` (array[object]) ユーザー入力フォーム設定
|
||||
- `text-input` (object) テキスト入力コントロール
|
||||
- `label` (string) 変数表示ラベル名
|
||||
- `variable` (string) 変数ID
|
||||
- `required` (bool) 必須かどうか
|
||||
- `default` (string) デフォルト値
|
||||
- `paragraph` (object) 段落テキスト入力コントロール
|
||||
- `label` (string) 変数表示ラベル名
|
||||
- `variable` (string) 変数ID
|
||||
- `required` (bool) 必須かどうか
|
||||
- `default` (string) デフォルト値
|
||||
- `select` (object) ドロップダウンコントロール
|
||||
- `label` (string) 変数表示ラベル名
|
||||
- `variable` (string) 変数ID
|
||||
- `required` (bool) 必須かどうか
|
||||
- `default` (string) デフォルト値
|
||||
- `options` (array[string]) オプション値
|
||||
- `file_upload` (object) ファイルアップロード設定
|
||||
- `document` (object) ドキュメント設定
|
||||
現在サポートされているドキュメントタイプ:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) ドキュメント数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `image` (object) 画像設定
|
||||
現在サポートされている画像タイプ:`png`, `jpg`, `jpeg`, `webp`, `gif`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) 画像数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `audio` (object) オーディオ設定
|
||||
現在サポートされているオーディオタイプ:`mp3`, `m4a`, `wav`, `webm`, `amr`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) オーディオ数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `video` (object) ビデオ設定
|
||||
現在サポートされているビデオタイプ:`mp4`, `mov`, `mpeg`, `mpga`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) ビデオ数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `custom` (object) カスタム設定
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) カスタム数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `system_parameters` (object) システムパラメータ
|
||||
- `file_size_limit` (int) ドキュメントアップロードサイズ制限(MB)
|
||||
- `image_file_size_limit` (int) 画像ファイルアップロードサイズ制限(MB)
|
||||
- `audio_file_size_limit` (int) 音声ファイルアップロードサイズ制限(MB)
|
||||
- `video_file_size_limit` (int) 動画ファイルアップロードサイズ制限(MB)
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/parameters"
|
||||
targetCode={` curl -X GET '${props.appDetail.api_base_url}/parameters'`}
|
||||
/>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"opening_statement": "Hello!",
|
||||
"suggested_questions_after_answer": {
|
||||
"enabled": true
|
||||
},
|
||||
"speech_to_text": {
|
||||
"enabled": true
|
||||
},
|
||||
"retriever_resource": {
|
||||
"enabled": true
|
||||
},
|
||||
"annotation_reply": {
|
||||
"enabled": true
|
||||
},
|
||||
"user_input_form": [
|
||||
{
|
||||
"paragraph": {
|
||||
"label": "Query",
|
||||
"variable": "query",
|
||||
"required": true,
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"file_upload": {
|
||||
"image": {
|
||||
"enabled": false,
|
||||
"number_limits": 3,
|
||||
"detail": "high",
|
||||
"transfer_methods": [
|
||||
"remote_url",
|
||||
"local_file"
|
||||
]
|
||||
}
|
||||
},
|
||||
"system_parameters": {
|
||||
"file_size_limit": 15,
|
||||
"image_file_size_limit": 10,
|
||||
"audio_file_size_limit": 50,
|
||||
"video_file_size_limit": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/site'
|
||||
method='GET'
|
||||
title='アプリのWebApp設定を取得'
|
||||
name='#site'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
アプリの WebApp 設定を取得するために使用します。
|
||||
### レスポンス
|
||||
- `title` (string) WebApp 名
|
||||
- `chat_color_theme` (string) チャットの色テーマ、16 進数形式
|
||||
- `chat_color_theme_inverted` (bool) チャットの色テーマを反転するかどうか
|
||||
- `icon_type` (string) アイコンタイプ、`emoji`-絵文字、`image`-画像
|
||||
- `icon` (string) アイコン。`emoji`タイプの場合は絵文字、`image`タイプの場合は画像 URL
|
||||
- `icon_background` (string) 16 進数形式の背景色
|
||||
- `icon_url` (string) アイコンの URL
|
||||
- `description` (string) 説明
|
||||
- `copyright` (string) 著作権情報
|
||||
- `privacy_policy` (string) プライバシーポリシーのリンク
|
||||
- `custom_disclaimer` (string) カスタム免責事項
|
||||
- `default_language` (string) デフォルト言語
|
||||
- `show_workflow_steps` (bool) ワークフローの詳細を表示するかどうか
|
||||
- `use_icon_as_answer_icon` (bool) WebApp のアイコンをチャット内の🤖に置き換えるかどうか
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup
|
||||
title="Request"
|
||||
tag="GET"
|
||||
label="/site"
|
||||
targetCode={`curl -X GET '${props.appDetail.api_base_url}/site' \\
|
||||
-H 'Authorization: Bearer {api_key}'`}
|
||||
/>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"title": "My App",
|
||||
"chat_color_theme": "#ff4a4a",
|
||||
"chat_color_theme_inverted": false,
|
||||
"icon_type": "emoji",
|
||||
"icon": "😄",
|
||||
"icon_background": "#FFEAD5",
|
||||
"icon_url": null,
|
||||
"description": "This is my app.",
|
||||
"copyright": "all rights reserved",
|
||||
"privacy_policy": "",
|
||||
"custom_disclaimer": "All generated by AI",
|
||||
"default_language": "en-US",
|
||||
"show_workflow_steps": false,
|
||||
"use_icon_as_answer_icon": false,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
___
|
||||