Initial commit

This commit is contained in:
2025-10-14 14:17:21 +08:00
commit ac715a8b88
35011 changed files with 3834178 additions and 0 deletions

View File

@@ -0,0 +1,305 @@
'use client'
import {
Children,
createContext,
useContext,
useEffect,
useRef,
useState,
} from 'react'
import { Tab } from '@headlessui/react'
import { Tag } from './tag'
import classNames from '@/utils/classnames'
import { writeTextToClipboard } from '@/utils/clipboard'
const languageNames = {
js: 'JavaScript',
ts: 'TypeScript',
javascript: 'JavaScript',
typescript: 'TypeScript',
php: 'PHP',
python: 'Python',
ruby: 'Ruby',
go: 'Go',
} as { [key: string]: string }
type IChildrenProps = {
children: React.ReactElement
[key: string]: any
}
function getPanelTitle({ className }: { className: string }) {
const language = className.split('-')[1]
return languageNames[language] ?? 'Code'
}
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 top-3.5 right-4 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'
: 'bg-white/5 hover:bg-white/7.5 dark:bg-white/2.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="w-5 h-5 transition-colors fill-zinc-500/20 stroke-zinc-500 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="flex h-9 items-center gap-2 border-y border-t-transparent border-b-white/7.5 bg-zinc-900 bg-white/2.5 px-4 dark:border-b-white/5 dark:bg-white/1">
{tag && (
<div className="flex dark">
<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 ICodePanelProps = {
children: React.ReactElement
tag?: string
code?: string
label?: string
targetCode?: string
}
function CodePanel({ tag, label, code, children, targetCode }: ICodePanelProps) {
const child = Children.only(children)
return (
<div className="group dark:bg-white/2.5">
<CodePanelHeader
tag={child.props.tag ?? tag}
label={child.props.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="p-4 overflow-x-auto text-xs text-white">{targetCode || children}</pre>
<CopyButton code={targetCode || child.props.children.props.children} />
</div>
</div>
)
}
function CodeGroupHeader({ title, children, selectedIndex }: IChildrenProps) {
const hasTabs = Children.count(children) > 1
if (!title && !hasTabs)
return null
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="pt-3 mr-auto text-xs font-semibold text-white">
{title}
</h3>
)}
{hasTabs && (
<Tab.List className="flex gap-4 -mb-px text-xs font-medium">
{Children.map(children, (child, childIndex) => (
<Tab
className={classNames(
'border-b py-3 transition focus:[&:not(:focus-visible)]:outline-none',
childIndex === selectedIndex
? 'border-emerald-500 text-emerald-400'
: 'border-transparent text-zinc-400 hover:text-zinc-300',
)}
>
{getPanelTitle(child.props.children.props)}
</Tab>
))}
</Tab.List>
)}
</div>
)
}
type ICodeGroupPanelsProps = {
children: React.ReactElement
[key: string]: any
}
function CodeGroupPanels({ children, targetCode, ...props }: ICodeGroupPanelsProps) {
const hasTabs = Children.count(children) > 1
if (hasTabs) {
return (
<Tab.Panels>
{Children.map(children, child => (
<Tab.Panel>
<CodePanel {...props}>{child}</CodePanel>
</Tab.Panel>
))}
</Tab.Panels>
)
}
return <CodePanel {...props} targetCode={targetCode}>{children}</CodePanel>
}
function usePreventLayoutShift() {
const positionRef = useRef<any>()
const rafRef = useRef<any>()
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)
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)
export function CodeGroup({ children, title, inputs, targetCode, ...props }: IChildrenProps) {
const languages = Children.map(children, child =>
getPanelTitle(child.props.children.props),
)
const tabGroupProps = useTabGroupProps(languages)
const hasTabs = Children.count(children) > 1
const Container = hasTabs ? Tab.Group : 'div'
const containerProps = hasTabs ? tabGroupProps : {}
const headerProps = hasTabs
? { selectedIndex: tabGroupProps.selectedIndex }
: {}
return (
<CodeGroupContext.Provider value={true}>
<Container
{...containerProps}
className="my-6 overflow-hidden shadow-md not-prose rounded-2xl bg-zinc-900 dark:ring-1 dark:ring-white/10"
>
<CodeGroupHeader title={title} {...headerProps}>
{children}
</CodeGroupHeader>
<CodeGroupPanels {...props} targetCode={targetCode}>{children}</CodeGroupPanels>
</Container>
</CodeGroupContext.Provider>
)
}
type IChildProps = {
children: string
[key: string]: any
}
export function Code({ children, ...props }: IChildProps) {
const isGrouped = useContext(CodeGroupContext)
if (isGrouped)
return <code {...props} dangerouslySetInnerHTML={{ __html: children }} />
return <code {...props}>{children}</code>
}
export function Pre({ children, ...props }: IChildrenProps) {
const isGrouped = useContext(CodeGroupContext)
if (isGrouped)
return children
return <CodeGroup {...props}>{children}</CodeGroup>
}

View File

@@ -0,0 +1,174 @@
'use client'
import { useEffect, useState } from 'react'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import { 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/language'
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 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)
}
}
// Run after component has rendered
setTimeout(extractTOC, 0)
}, [appDetail, locale])
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',
})
}
}
}
return (
<div className="flex">
<div className={`fixed right-8 top-32 z-10 transition-all ${isTocExpanded ? 'w-64' : 'w-10'}`}>
{isTocExpanded
? (
<nav className="toc w-full bg-gray-50 p-4 rounded-lg shadow-md">
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-semibold">{t('appApi.develop.toc')}</h3>
<button
onClick={() => setIsTocExpanded(false)}
className="text-gray-500 hover:text-gray-700"
>
</button>
</div>
<ul className="space-y-2">
{toc.map((item, index) => (
<li key={index}>
<a
href={item.href}
className="text-gray-600 hover:text-gray-900 hover:underline transition-colors duration-200"
onClick={e => handleTocClick(e, item)}
>
{item.text}
</a>
</li>
))}
</ul>
</nav>
)
: (
<button
onClick={() => setIsTocExpanded(true)}
className="w-10 h-10 bg-gray-50 rounded-full shadow-md flex items-center justify-center hover:bg-gray-100 transition-colors duration-200"
>
<RiListUnordered className="w-6 h-6" />
</button>
)}
</div>
<article className="prose prose-xl" >
{(appDetail?.mode === 'chat' || appDetail?.mode === '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} />
}
})()
)}
{appDetail?.mode === '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} />
}
})()
)}
{appDetail?.mode === '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} />
}
})()
)}
{appDetail?.mode === '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} />
}
})()
)}
</article>
</div>
)
}
export default Doc

View File

@@ -0,0 +1,51 @@
'use client'
import { useTranslation } from 'react-i18next'
import s from './secret-key/style.module.css'
import Doc from '@/app/components/develop/doc'
import Loading from '@/app/components/base/loading'
import InputCopy from '@/app/components/develop/secret-key/input-copy'
import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-button'
import { useStore as useAppStore } from '@/app/components/app/store'
type IDevelopMainProps = {
appId: string
}
const DevelopMain = ({ appId }: IDevelopMainProps) => {
const appDetail = useAppStore(state => state.appDetail)
const { t } = useTranslation()
if (!appDetail) {
return (
<div className='flex h-full items-center justify-center bg-white'>
<Loading />
</div>
)
}
return (
<div className='relative flex flex-col h-full overflow-hidden'>
<div className='flex items-center justify-between flex-shrink-0 px-6 border-b border-solid py-2 border-b-gray-100'>
<div className='text-lg font-medium text-gray-900'></div>
<div className='flex items-center flex-wrap gap-y-1'>
<InputCopy className='flex-shrink-0 mr-1 w-52 sm:w-80' value={appDetail.api_base_url}>
<div className={`ml-2 border border-gray-200 border-solid flex-shrink-0 px-2 py-0.5 rounded-[6px] text-gray-500 text-[0.625rem] ${s.customApi}`}>
{t('appApi.apiServer')}
</div>
</InputCopy>
<div className={`flex items-center h-9 px-3 rounded-lg
text-[13px] font-normal mr-2 ${appDetail.enable_api ? 'text-green-500 bg-green-50' : 'text-yellow-500 bg-yellow-50'}`}>
<div className='mr-1'>{t('appApi.status')}</div>
<div className='font-semibold'>{appDetail.enable_api ? `${t('appApi.ok')}` : `${t('appApi.disabled')}`}</div>
</div>
<SecretKeyButton className='flex-shrink-0' appId={appId} />
</div>
</div>
<div className='px-4 sm:px-10 py-4 overflow-auto grow'>
<Doc appDetail={appDetail} />
</div>
</div>
)
}
export default DevelopMain

View File

@@ -0,0 +1,148 @@
'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'
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
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={`font-mono text-[0.625rem] font-semibold leading-6 rounded-lg px-1.5 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='no-underline group text-inherit hover:text-inherit'>{title}</a>
</h2>
</>
)
}
export function Row({ children }: IChildrenProps) {
return (
<div className="grid items-start grid-cols-1 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="px-0 py-4 m-0 first:pt-0 last:pb-0">
<dl className="flex flex-wrap items-center m-0 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="px-0 py-1 m-0 last:pb-0">
<dl className="flex flex-wrap items-center m-0 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 first:pt-0 italic">{children}</li>
)
}

View File

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

View File

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

View File

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

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,70 @@
'use client'
import React, { useEffect, useState } from 'react'
import copy from 'copy-to-clipboard'
import { t } from 'i18next'
import s from './style.module.css'
import Tooltip from '@/app/components/base/tooltip'
type IInputCopyProps = {
value?: string
className?: string
readOnly?: boolean
children?: React.ReactNode
}
const InputCopy = ({
value = '',
className,
readOnly = true,
children,
}: IInputCopyProps) => {
const [isCopied, setIsCopied] = useState(false)
useEffect(() => {
if (isCopied) {
const timeout = setTimeout(() => {
setIsCopied(false)
}, 1000)
return () => {
clearTimeout(timeout)
}
}
}, [isCopied])
return (
<div className={`flex rounded-lg bg-components-input-bg-normal hover:bg-state-base-hover py-2 items-center ${className}`}>
<div className="flex items-center grow h-5">
{children}
<div className='grow text-[13px] relative h-full'>
<div className='absolute top-0 left-0 w-full pl-2 pr-2 truncate cursor-pointer r-0' onClick={() => {
copy(value)
setIsCopied(true)
}}>
<Tooltip
popupContent={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
position='bottom'
>
{value}
</Tooltip>
</div>
</div>
<div className="shrink-0 h-4 bg-divider-regular border" />
<Tooltip
popupContent={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
position='bottom'
>
<div className="px-0.5 shrink-0">
<div className={`box-border w-[30px] h-[30px] flex items-center justify-center rounded-lg hover:bg-state-base-hover cursor-pointer ${s.copyIcon} ${isCopied ? s.copied : ''}`} onClick={() => {
copy(value)
setIsCopied(true)
}}>
</div>
</div>
</Tooltip>
</div>
</div>
)
}
export default InputCopy

View File

@@ -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 items-center justify-center w-3.5 h-3.5'}>
<RiKey2Line className='w-3.5 h-3.5 text-text-tertiary' />
</div>
<div className={`text-text-tertiary system-xs-medium px-[3px] ${textCls}`}>{t('appApi.apiKey')}</div>
</Button>
<SecretKeyModal isShow={isVisible} onClose={() => setVisible(false)} appId={appId} />
</>
)
}
export default SecretKeyButton

View File

@@ -0,0 +1,41 @@
'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}`}>
<XMarkIcon className={`w-6 h-6 absolute cursor-pointer text-text-tertiary ${s.close}`} onClick={onClose} />
<p className='mt-1 text-[13px] text-text-tertiary font-normal leading-5'>{t('appApi.apiKeyModal.generateTips')}</p>
<div className='my-4'>
<InputCopy className='w-full' value={newKey?.token} />
</div>
<div className='flex justify-end my-4'>
<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

View File

@@ -0,0 +1,167 @@
'use client'
import {
useEffect,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import { PlusIcon, XMarkIcon } from '@heroicons/react/20/solid'
import useSWR, { useSWRConfig } from 'swr'
import copy from 'copy-to-clipboard'
import SecretKeyGenerateModal from './secret-key-generate'
import s from './style.module.css'
import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
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 Tooltip from '@/app/components/base/tooltip'
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 [copyValue, setCopyValue] = useState('')
useEffect(() => {
if (copyValue) {
const timeout = setTimeout(() => {
setCopyValue('')
}, 1000)
return () => {
clearTimeout(timeout)
}
}
}, [copyValue])
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} px-8 flex flex-col`}>
<XMarkIcon className={`w-6 h-6 absolute cursor-pointer text-text-tertiary ${s.close}`} onClick={onClose} />
<p className='mt-1 text-[13px] text-text-tertiary font-normal leading-5 shrink-0'>{t('appApi.apiKeyModal.apiSecretKeyTips')}</p>
{!apiKeysList && <div className='mt-4'><Loading /></div>}
{
!!apiKeysList?.data?.length && (
<div className='flex flex-col grow mt-4 overflow-hidden'>
<div className='flex items-center shrink-0 text-xs font-semibold text-text-tertiary border-b border-solid h-9'>
<div className='shrink-0 w-64 px-3'>{t('appApi.apiKeyModal.secretKey')}</div>
<div className='shrink-0 px-3 w-[200px]'>{t('appApi.apiKeyModal.created')}</div>
<div className='shrink-0 px-3 w-[200px]'>{t('appApi.apiKeyModal.lastUsed')}</div>
<div className='grow px-3'></div>
</div>
<div className='grow overflow-auto'>
{apiKeysList.data.map(api => (
<div className='flex items-center text-sm font-normal text-text-secondary border-b border-solid h-9' key={api.id}>
<div className='shrink-0 w-64 px-3 font-mono truncate'>{generateToken(api.token)}</div>
<div className='shrink-0 px-3 truncate w-[200px]'>{formatTime(Number(api.created_at), t('appLog.dateTimeFormat') as string)}</div>
<div className='shrink-0 px-3 truncate w-[200px]'>{api.last_used_at ? formatTime(Number(api.last_used_at), t('appLog.dateTimeFormat') as string) : t('appApi.never')}</div>
<div className='flex grow px-3'>
<Tooltip
popupContent={copyValue === api.token ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
popupClassName='mr-1'
>
<div className={`flex items-center justify-center shrink-0 w-6 h-6 mr-1 rounded-lg cursor-pointer hover:bg-state-base-hover ${s.copyIcon} ${copyValue === api.token ? s.copied : ''}`} onClick={() => {
// setIsCopied(true)
copy(api.token)
setCopyValue(api.token)
}}></div>
</Tooltip>
{isCurrentWorkspaceManager
&& <div className={`flex items-center justify-center shrink-0 w-6 h-6 rounded-lg cursor-pointer ${s.trashIcon}`} onClick={() => {
setDelKeyId(api.id)
setShowConfirmDelete(true)
}}>
</div>
}
</div>
</div>
))}
</div>
</div>
)
}
<div className='flex'>
<Button className={`flex shrink-0 mt-4 ${s.autoWidth}`} onClick={onCreate} disabled={!currentWorkspace || !isCurrentWorkspaceEditor}>
<PlusIcon className='flex shrink-0 w-4 h-4 mr-1' />
<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

View 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);
}

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

View File

@@ -0,0 +1,586 @@
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}>
```javascript
```
</CodeGroup>
### 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">
```javascript
Authorization: Bearer {API_KEY}
```
</CodeGroup>
</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 for retrieval and statistics.
Should be uniquely defined by the developer within the application.
</Property>
<Property name='files' type='array[object]' key='files'>
File list, suitable for inputting files (images) combined with text understanding and answering questions, available only when the model supports Vision capability.
- `type` (string) Supported type: `image` (currently only supports image type)
- `transfer_method` (string) Transfer method, `remote_url` for image URL / `local_file` for file upload
- `url` (string) Image URL (when the transfer method is `remote_url`)
- `upload_file_id` (string) Uploaded file ID, which must be obtained by uploading through the File Upload API in advance (when the 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' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": {"query": "Hello, world!"},\n "response_mode": "streaming",\n "user": "abc-123"\n}'\n`}>
```bash {{ title: 'cURL' }}
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>
### 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.
### 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' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'file=@localfile;type=image/[png|jpeg|jpg|webp|gif] \\\n--form 'user=abc-123'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/files/upload' \
--header 'Authorization: Bearer {api_key}' \
--form 'file=@"/path/to/file"'
```
</CodeGroup>
### 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='/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.
### 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' \\\n-H 'Authorization: Bearer {api_key}' \\\n-H 'Content-Type: application/json' \\\n--data-raw '{ "user": "abc-123"}'`}>
```bash {{ title: 'cURL' }}
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>
### 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 \\\n --header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "rating": "like",\n "user": "abc-123",\n "content": "message feedback information"\n}'`}>
```bash {{ title: 'cURL' }}
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>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"result": "success"
}
```
</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' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",\n "text": "Hello Dify",\n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
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>
<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
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</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
- `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
- `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'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/parameters' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<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>

View File

@@ -0,0 +1,584 @@
import { CodeGroup } from '../code.tsx'
import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from '../md.tsx'
# Completion アプリ API
テキスト生成アプリケーションはセッションレスをサポートし、翻訳、記事作成、要約AI等に最適です。
<div>
### ベースURL
<CodeGroup title="Code" targetCode={props.appDetail.api_base_url}>
```javascript
```
</CodeGroup>
### 認証
サービスAPIは`API-Key`認証を使用します。
<i>**APIキーの漏洩による重大な結果を避けるため、APIキーはサーバーサイドに保存し、クライアントサイドでは共有や保存しないことを強く推奨します。**</i>
すべてのAPIリクエストで、以下のように`Authorization` HTTPヘッダーにAPIキーを含めてください
<CodeGroup title="Code">
```javascript
Authorization: Bearer {API_KEY}
```
</CodeGroup>
</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機能をサポートしている場合のみ、テキスト理解と質問応答を組み合わせたファイル画像の入力に適しています。
- `type` (string) サポートされるタイプ:`image`(現在は画像タイプのみサポート)
- `transfer_method` (string) 転送方法、画像URLの場合は`remote_url` / ファイルアップロードの場合は`local_file`
- `url` (string) 画像URL転送方法が`remote_url`の場合)
- `upload_file_id` (string) アップロードされたファイルID、事前にファイルアップロードAPIを通じてアップロードする必要があります転送方法が`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' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": {"query": "Hello, world!"},\n "response_mode": "streaming",\n "user": "abc-123"\n}'\n`}>
```bash {{ title: 'cURL' }}
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>
### ブロッキングモード
<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) 必須
開発者のルールで定義されたユーザー識別子。アプリケーション内で一意である必要があります。
### レスポンス
アップロードが成功すると、サーバーはファイルの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' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'file=@localfile;type=image/[png|jpeg|jpg|webp|gif] \\\n--form 'user=abc-123'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/files/upload' \
--header 'Authorization: Bearer {api_key}' \
--form 'file=@"/path/to/file"'
```
</CodeGroup>
### レスポンス例
<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='/completion-messages/:task_id/stop'
method='POST'
title='生成の停止'
name='#stop-generatebacks'
/>
<Row>
<Col>
ストリーミングモードでのみサポートされています。
### パス
- `task_id` (string) タスクID、ストリーミングチャンクの返信から取得可能
リクエストボディ
- `user` (string) 必須
ユーザー識別子。エンドユーザーの身元を定義するために使用され、メッセージ送信インターフェースで渡されたユーザーと一致する必要があります。
### レスポンス
- `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' \\\n-H 'Authorization: Bearer {api_key}' \\\n-H 'Content-Type: application/json' \\\n--data-raw '{ "user": "abc-123"}'`}>
```bash {{ title: 'cURL' }}
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>
### レスポンス例
<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 \\\n --header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "rating": "like",\n "user": "abc-123",\n "content": "message feedback information"\n}'`}>
```bash {{ title: 'cURL' }}
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>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"result": "success"
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/text-to-audio'
method='POST'
title='テキストから音声'
name='#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' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",\n "text": "Hello Dify",\n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
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>
<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]) アプリケーションのタグ
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</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) ファイルアップロード設定
- `image` (object) 画像設定
現在は画像タイプのみ対応:`png`、`jpg`、`jpeg`、`webp`、`gif`
- `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'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/parameters' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<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>

View File

@@ -0,0 +1,550 @@
import { CodeGroup } from '../code.tsx'
import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
# 文本生成型应用 API
文本生成应用无会话支持,适合用于翻译/文章写作/总结 AI 等等。
<div>
### 基础 URL
<CodeGroup title="Code" targetCode={props.appDetail.api_base_url}>
```javascript
```
</CodeGroup>
### 鉴权
Dify Service API 使用 `API-Key` 进行鉴权。
<i>**强烈建议开发者把 `API-Key` 放在后端存储,而非分享或者放在客户端存储,以免 `API-Key` 泄露,导致财产损失。**</i>
所有 API 请求都应在 **`Authorization`** HTTP Header 中包含您的 `API-Key`,如下所示:
<CodeGroup title="Code">
```javascript
Authorization: Bearer {API_KEY}
```
</CodeGroup>
</div>
---
<Heading
url='/completion-messages'
method='POST'
title='发送消息'
name='#Create-Completion-Message'
/>
<Row>
<Col>
发送请求给文本生成型应用。
### Request Body
<Properties>
<Property name='inputs' type='object' key='inputs'>
(选填)允许传入 App 定义的各变量值。
inputs 参数包含了多组键值对Key/Value pairs每组的键对应一个特定变量每组的值则是该变量的具体值。
文本生成型应用要求至少传入一组键值对。
- `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'>
上传的文件。
- `type` (string) 支持类型:图片 `image`(目前仅支持图片格式) 。
- `transfer_method` (string) 传递方式:
- `remote_url`: 图片地址。
- `local_file`: 上传文件。
- `url` 图片地址。(仅当传递方式为 `remote_url` 时)。
- `upload_file_id` 上传文件 ID。仅当传递方式为 `local_file `时)。
</Property>
</Properties>
### Response
<Properties>
当 `response_mode` 为 `blocking` 时,返回 ChatCompletionResponse object。
当 `response_mode` 为 `streaming`时,返回 ChunkChatCompletionResponse object 流式序列。
### ChatCompletionResponse
返回完整的 App 结果,`Content-Type` 为 `application/json`。
- `message_id` (string) 消息唯一 ID
- `mode` (string) App 模式,固定为 chat
- `answer` (string) 完整回复内容
- `metadata` (object) 元数据
- `usage` (Usage) 模型用量信息
- `retriever_resources` (array[RetrieverResource]) 引用和归属分段列表
- `created_at` (int) 消息创建时间戳1705395332
### ChunkChatCompletionResponse
返回 App 输出的流式块,`Content-Type` 为 `text/event-stream`。
每个流式块均为 data: 开头,块之间以 `\n\n` 即两个换行符分隔,如下所示:
<CodeGroup>
```streaming {{ title: 'Response' }}
data: {"event": "message", "task_id": "900bbd43-dc0b-4383-a372-aa6e6c414227", "id": "663c5084-a254-4040-8ad3-51f2a3c1a77c", "answer": "Hi", "created_at": 1705398420}\n\n
```
</CodeGroup>
流式块中根据 `event` 不同,结构也不同:
- `event: message` LLM 返回文本块事件,即:完整的文本以分块的方式输出。
- `task_id` (string) 任务 ID用于请求跟踪和下方的停止响应接口
- `message_id` (string) 消息唯一 ID
- `answer` (string) LLM 返回文本块内容
- `created_at` (int) 创建时间戳1705395332
- `event: message_end` 消息结束事件,收到此事件则代表文本流式返回结束。
- `task_id` (string) 任务 ID用于请求跟踪和下方的停止响应接口
- `message_id` (string) 消息唯一 ID
- `metadata` (object) 元数据
- `usage` (Usage) 模型用量信息
- `retriever_resources` (array[RetrieverResource]) 引用和归属分段列表
- `event: tts_message` TTS 音频流事件语音合成输出。内容是Mp3格式的音频块使用 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用于请求跟踪和下方的停止响应接口
- `message_id` (string) 消息唯一 ID
- `answer` (string) 替换内容(直接替换 LLM 所有回复文本)
- `created_at` (int) 创建时间戳1705395332
- `event: error`
流式输出过程中出现的异常会以 stream event 形式输出,收到异常事件后即结束。
- `task_id` (string) 任务 ID用于请求跟踪和下方的停止响应接口
- `message_id` (string) 消息唯一 ID
- `status` (int) HTTP 状态码
- `code` (string) 错误码
- `message` (string) 错误消息
- `event: ping` 每 10s 一次的 ping 事件,保持连接存活。
### Errors
- 404对话不存在
- 400`invalid_param`,传入参数异常
- 400`app_unavailable`App 配置不可用
- 400`provider_not_initialize`,无可用模型凭据配置
- 400`provider_quota_exceeded`,模型调用额度不足
- 400`model_currently_not_support`,当前模型不可用
- 400`completion_request_error`,文本生成失败
- 500服务内部异常
</Properties>
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/completion-messages" targetCode={`curl -X POST '${props.appDetail.api_base_url}/completion-messages' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": {"query": "Hello, world!"},\n "response_mode": "streaming",\n "user": "abc-123"\n}'\n`}>
```bash {{ title: 'cURL' }}
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>
### blocking
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"id": "0b089b9a-24d9-48cc-94f8-762677276261",
"answer": "how are you?",
"created_at": 1679586667
}
```
</CodeGroup>
### streaming
<CodeGroup title="Response">
```streaming {{ title: 'Response' }}
data: {"id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " I", "created_at": 1679586595}
data: {"id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " I", "created_at": 1679586595}
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='#files-upload'
/>
<Row>
<Col>
上传文件(目前仅支持图片)并在发送消息时使用,可实现图文多模态理解。
支持 png, jpg, jpeg, webp, gif 格式。
<i>上传的文件仅供当前终端用户使用。</i>
### Request Body
该接口需使用 `multipart/form-data` 进行请求。
<Properties>
<Property name='file' type='file' key='file'>
要上传的文件。
</Property>
<Property name='user' type='string' key='user'>
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。
</Property>
</Properties>
### Response
成功上传后,服务器会返回文件的 ID 和相关信息。
- `id` (uuid) ID
- `name` (string) 文件名
- `size` (int) 文件大小byte
- `extension` (string) 文件后缀
- `mime_type` (string) 文件 mime-type
- `created_by` (uuid) 上传人 ID
- `created_at` (timestamp) 上传时间
### Errors
- 400`no_file_uploaded`,必须提供文件
- 400`too_many_files`,目前只接受一个文件
- 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 大小限制
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/files/upload" targetCode={`curl -X POST '${props.appDetail.api_base_url}/files/upload' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'file=@localfile;type=image/[png|jpeg|jpg|webp|gif] \\\n--form 'user=abc-123'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/files/upload' \
--header 'Authorization: Bearer {api_key}' \
--form 'file=@"/path/to/file"'
```
</CodeGroup>
<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": 123,
"created_at": 1577836800,
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/completion-messages/:task_id/stop'
method='POST'
title='停止响应'
name='#Stop'
/>
<Row>
<Col>
仅支持流式模式。
### Path
- `task_id` (string) 任务 ID可在流式返回 Chunk 中获取
### Request Body
- `user` (string) Required
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。
### Response
- `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' \\\n-H 'Authorization: Bearer {api_key}' \\\n-H 'Content-Type: application/json' \\\n--data-raw '{ "user": "abc-123"}'`}>
```bash {{ title: 'cURL' }}
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>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"result": "success"
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/messages/:message_id/feedbacks'
method='POST'
title='消息反馈(点赞)'
name='#feedbacks'
/>
<Row>
<Col>
消息终端用户反馈、点赞,方便应用开发者优化输出预期。
### Path Params
<Properties>
<Property name='message_id' type='string' key='message_id'>
消息 ID
</Property>
</Properties>
### Request Body
<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>
### Response
- `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 \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "rating": "like",\n "user": "abc-123",\n "content": "message feedback information"\n}'`}>
```bash {{ title: 'cURL' }}
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>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"result": "success"
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/text-to-audio'
method='POST'
title='文字转语音'
name='#audio'
/>
<Row>
<Col>
文字转语音。
### Request Body
<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'>
语音生成内容。如果没有传 message-id的话则会使用这个字段的内容
</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' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",\n "text": "你好Dify",\n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
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": "你好Dify",
"user": "abc-123",
"streaming": false
}'
```
</CodeGroup>
<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]) 应用标签
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='获取应用参数'
name='#parameters'
/>
<Row>
<Col>
用于进入页面一开始,获取功能开关、输入参数名称、类型及默认值等使用。
### Response
- `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) 文件上传配置
- `image` (object) 图片设置
当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`
- `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'\\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/parameters' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"introduction": "nice to meet you",
"user_input_form": [
{
"text-input": {
"label": "a",
"variable": "a",
"required": true,
"max_length": 48,
"default": ""
}
},
{
// ...
}
],
"file_upload": {
"image": {
"enabled": true,
"number_limits": 3,
"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>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,729 @@
import { CodeGroup } from '../code.tsx'
import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from '../md.tsx'
# Workflow App API
Workflow applications 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}>
```javascript
```
</CodeGroup>
### 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">
```javascript
Authorization: Bearer {API_KEY}
```
</CodeGroup>
</div>
---
<Heading
url='/workflows/run'
method='POST'
title='Execute Workflow'
name='#Execute-Workflow'
/>
<Row>
<Col>
Execute workflow, cannot be executed without a published workflow.
### Request Body
- `inputs` (object) Required
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 workflow application requires at least one key/value pair to be inputted.
If the variable is of File type, specify an object that has the keys described in `files` below.
- `response_mode` (string) Required
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>
- `user` (string) Required
User identifier, used to define the identity of the end-user for retrieval and statistics.
Should be uniquely defined by the developer within the application.
- `files` (array[object]) Optional
File list, suitable for inputting files combined with text understanding and answering questions, available only when the model supports file parsing and understanding capability.
- `type` (string) Supported type:
- `document` ('TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB')
- `image` ('JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG')
- `audio` ('MP3', 'M4A', 'WAV', 'WEBM', 'AMR')
- `video` ('MP4', 'MOV', 'MPEG', 'MPGA')
- `custom` (Other file types)
- `transfer_method` (string) Transfer method, `remote_url` for image URL / `local_file` for file upload
- `url` (string) Image URL (when the transfer method is `remote_url`)
- `upload_file_id` (string) Uploaded file ID, which must be obtained by uploading through the File Upload API in advance (when the transfer method is `local_file`)
### Response
When `response_mode` is `blocking`, return a CompletionResponse object.
When `response_mode` is `streaming`, return a ChunkCompletionResponse stream.
### CompletionResponse
Returns the App result, `Content-Type` is `application/json`.
- `workflow_run_id` (string) Unique ID of workflow execution
- `task_id` (string) Task ID, used for request tracking and the below Stop Generate API
- `data` (object) detail of result
- `id` (string) ID of workflow execution
- `workflow_id` (string) ID of related workflow
- `status` (string) status of execution, `running` / `succeeded` / `failed` / `stopped`
- `outputs` (json) Optional content of output
- `error` (string) Optional reason of error
- `elapsed_time` (float) Optional total seconds to be used
- `total_tokens` (int) Optional tokens to be used
- `total_steps` (int) default 0
- `created_at` (timestamp) start time
- `finished_at` (timestamp) end time
### ChunkCompletionResponse
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: workflow_started` workflow starts execution
- `task_id` (string) Task ID, used for request tracking and the below Stop Generate API
- `workflow_run_id` (string) Unique ID of workflow execution
- `event` (string) fixed to `workflow_started`
- `data` (object) detail
- `id` (string) Unique ID of workflow execution
- `workflow_id` (string) ID of related workflow
- `sequence_number` (int) Self-increasing serial number, self-increasing in the App, starting from 1
- `created_at` (timestamp) Creation timestamp, e.g., 1705395332
- `event: node_started` node execution started
- `task_id` (string) Task ID, used for request tracking and the below Stop Generate API
- `workflow_run_id` (string) Unique ID of workflow execution
- `event` (string) fixed to `node_started`
- `data` (object) detail
- `id` (string) Unique ID of workflow execution
- `node_id` (string) ID of node
- `node_type` (string) type of node
- `title` (string) name of node
- `index` (int) Execution sequence number, used to display Tracing Node sequence
- `predecessor_node_id` (string) optional Prefix node ID, used for canvas display execution path
- `inputs` (object) Contents of all preceding node variables used in the node
- `created_at` (timestamp) timestamp of start, e.g., 1705395332
- `event: node_finished` node execution ends, success or failure in different states in the same event
- `task_id` (string) Task ID, used for request tracking and the below Stop Generate API
- `workflow_run_id` (string) Unique ID of workflow execution
- `event` (string) fixed to `node_finished`
- `data` (object) detail
- `id` (string) Unique ID of workflow execution
- `node_id` (string) ID of node
- `node_type` (string) type of node
- `title` (string) name of node
- `index` (int) Execution sequence number, used to display Tracing Node sequence
- `predecessor_node_id` (string) optional Prefix node ID, used for canvas display execution path
- `inputs` (object) Contents of all preceding node variables used in the node
- `process_data` (json) Optional node process data
- `outputs` (json) Optional content of output
- `status` (string) status of execution, `running` / `succeeded` / `failed` / `stopped`
- `error` (string) Optional reason of error
- `elapsed_time` (float) Optional total seconds to be used
- `execution_metadata` (json) meta data
- `total_tokens` (int) optional tokens to be used
- `total_price` (decimal) optional Total cost
- `currency` (string) optional e.g. `USD` / `RMB`
- `created_at` (timestamp) timestamp of start, e.g., 1705395332
- `event: workflow_finished` workflow execution ends, success or failure in different states in the same event
- `task_id` (string) Task ID, used for request tracking and the below Stop Generate API
- `workflow_run_id` (string) Unique ID of workflow execution
- `event` (string) fixed to `workflow_finished`
- `data` (object) detail
- `id` (string) ID of workflow execution
- `workflow_id` (string) ID of related workflow
- `status` (string) status of execution, `running` / `succeeded` / `failed` / `stopped`
- `outputs` (json) Optional content of output
- `error` (string) Optional reason of error
- `elapsed_time` (float) Optional total seconds to be used
- `total_tokens` (int) Optional tokens to be used
- `total_steps` (int) default 0
- `created_at` (timestamp) start time
- `finished_at` (timestamp) end time
- `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: ping` Ping event every 10 seconds to keep the connection alive.
### Errors
- 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, `workflow_request_error`, workflow execution failed
- 500, internal server error
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/workflows/run" targetCode={`curl -X POST '${props.appDetail.api_base_url}/workflows/run' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": ${JSON.stringify(props.inputs)},\n "response_mode": "streaming",\n "user": "abc-123"\n}'\n`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/workflows/run' \
--header 'Authorization: Bearer {api_key}' \
--header 'Content-Type: application/json' \
--data-raw '{
"inputs": {},
"response_mode": "streaming",
"user": "abc-123"
}'
```
</CodeGroup>
<CodeGroup title="File variable example">
```json {{ title: 'File variable example' }}
{
"inputs": {
"{variable_name}": {
"transfer_method": "local_file",
"upload_file_id": "{upload_file_id}",
"type": "{document_type}"
}
}
}
```
</CodeGroup>
### Blocking Mode
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"workflow_run_id": "djflajgkldjgd",
"task_id": "9da23599-e713-473b-982c-4328d4f5c78a",
"data": {
"id": "fdlsjfjejkghjda",
"workflow_id": "fldjaslkfjlsda",
"status": "succeeded",
"outputs": {
"text": "Nice to meet you."
},
"error": null,
"elapsed_time": 0.875,
"total_tokens": 3562,
"total_steps": 8,
"created_at": 1705407629,
"finished_at": 1727807631
}
}
```
</CodeGroup>
### Streaming Mode
<CodeGroup title="Response">
```streaming {{ title: 'Response' }}
data: {"event": "workflow_started", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "workflow_id": "dfjasklfjdslag", "sequence_number": 1, "created_at": 1679586595}}
data: {"event": "node_started", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "node_id": "dfjasklfjdslag", "node_type": "start", "title": "Start", "index": 0, "predecessor_node_id": "fdljewklfklgejlglsd", "inputs": {}, "created_at": 1679586595}}
data: {"event": "node_finished", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "node_id": "dfjasklfjdslag", "node_type": "start", "title": "Start", "index": 0, "predecessor_node_id": "fdljewklfklgejlglsd", "inputs": {}, "outputs": {}, "status": "succeeded", "elapsed_time": 0.324, "execution_metadata": {"total_tokens": 63127864, "total_price": 2.378, "currency": "USD"}, "created_at": 1679586595}}
data: {"event": "workflow_finished", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "workflow_id": "dfjasklfjdslag", "outputs": {}, "status": "succeeded", "elapsed_time": 0.324, "total_tokens": 63127864, "total_steps": "1", "created_at": 1679586595, "finished_at": 1679976595}}
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>
<CodeGroup title="File upload sample code">
```json {{ title: 'File upload sample code' }}
import requests
import json
def upload_file(file_path, user):
upload_url = "https://api.dify.ai/v1/files/upload"
headers = {
"Authorization": "Bearer app-xxxxxxxx",
}
try:
print("Upload file...")
with open(file_path, 'rb') as file:
files = {
'file': (file_path, file, 'text/plain') # Make sure the file is uploaded with the appropriate MIME type
}
data = {
"user": user,
"type": "TXT" # Set the file type to TXT
}
response = requests.post(upload_url, headers=headers, files=files, data=data)
if response.status_code == 201: # 201 means creation is successful
print("File uploaded successfully")
return response.json().get("id") # Get the uploaded file ID
else:
print(f"File upload failed, status code: {response.status_code}")
return None
except Exception as e:
print(f"Error occurred: {str(e)}")
return None
def run_workflow(file_id, user, response_mode="blocking"):
workflow_url = "https://api.dify.ai/v1/workflows/run"
headers = {
"Authorization": "Bearer app-xxxxxxxxx",
"Content-Type": "application/json"
}
data = {
"inputs": {
"orig_mail": {
"transfer_method": "local_file",
"upload_file_id": file_id,
"type": "document"
}
},
"response_mode": response_mode,
"user": user
}
try:
print("Run Workflow...")
response = requests.post(workflow_url, headers=headers, json=data)
if response.status_code == 200:
print("Workflow execution successful")
return response.json()
else:
print(f"Workflow execution failed, status code: {response.status_code}")
return {"status": "error", "message": f"Failed to execute workflow, status code: {response.status_code}"}
except Exception as e:
print(f"Error occurred: {str(e)}")
return {"status": "error", "message": str(e)}
# Usage Examples
file_path = "{your_file_path}"
user = "difyuser"
# Upload files
file_id = upload_file(file_path, user)
if file_id:
# The file was uploaded successfully, and the workflow continues to run
result = run_workflow(file_id, user)
print(result)
else:
print("File upload failed and workflow cannot be executed")
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/workflows/run/:workflow_id'
method='GET'
title='Get Workflow Run Detail'
name='#get-workflow-run-detail'
/>
<Row>
<Col>
Retrieve the current execution results of a workflow task based on the workflow execution ID.
### Path
- `workflow_id` (string) Workflow ID, can be obtained from the streaming chunk return
### Response
- `id` (string) ID of workflow execution
- `workflow_id` (string) ID of related workflow
- `status` (string) status of execution, `running` / `succeeded` / `failed` / `stopped`
- `inputs` (json) content of input
- `outputs` (json) content of output
- `error` (string) reason of error
- `total_steps` (int) total steps of task
- `total_tokens` (int) total tokens to be used
- `created_at` (timestamp) start time
- `finished_at` (timestamp) end time
- `elapsed_time` (float) total seconds to be used
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="GET" label="/workflows/run/:workflow_id" targetCode={`curl -X GET '${props.appDetail.api_base_url}/workflows/run/:workflow_id' \\\n-H 'Authorization: Bearer {api_key}' \\\n-H 'Content-Type: application/json'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/workflows/run/:workflow_id' \
-H 'Authorization: Bearer {api_key}' \
-H 'Content-Type: application/json'
```
</CodeGroup>
### Response Example
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"id": "b1ad3277-089e-42c6-9dff-6820d94fbc76",
"workflow_id": "19eff89f-ec03-4f75-b0fc-897e7effea02",
"status": "succeeded",
"inputs": "{\"sys.files\": [], \"sys.user_id\": \"abc-123\"}",
"outputs": null,
"error": null,
"total_steps": 3,
"total_tokens": 0,
"created_at": "Thu, 18 Jul 2024 03:17:40 -0000",
"finished_at": "Thu, 18 Jul 2024 03:18:10 -0000",
"elapsed_time": 30.098514399956912
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/workflows/tasks/: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.
### Response
- `result` (string) Always returns "success"
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="POST" label="/workflows/tasks/:task_id/stop" targetCode={`curl -X POST '${props.appDetail.api_base_url}/workflows/tasks/:task_id/stop' \\\n-H 'Authorization: Bearer {api_key}' \\\n-H 'Content-Type: application/json' \\\n--data-raw '{"user": "abc-123"}'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/workflows/tasks/:task_id/stop' \
-H 'Authorization: Bearer {api_key}' \
-H 'Content-Type: application/json' \
--data-raw '{
"user": "abc-123"
}'
```
</CodeGroup>
### Response Example
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"result": "success"
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/files/upload'
method='POST'
title='File Upload'
name='#file-upload'
/>
<Row>
<Col>
Upload a file for use when sending messages, enabling multimodal understanding of images and text.
Supports any formats that are supported by your workflow.
Uploaded files are for use by the current end-user only.
### 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.
### 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' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'file=@localfile;type=image/[png|jpeg|jpg|webp|gif] \\\n--form 'user=abc-123'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/files/upload' \
--header 'Authorization: Bearer {api_key}' \
--form 'file=@"/path/to/file"'
```
</CodeGroup>
### 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='/workflows/logs'
method='GET'
title='Get Workflow Logs'
name='#Get-Workflow-Logs'
/>
<Row>
<Col>
Returns workflow logs, with the first page returning the latest `{limit}` messages, i.e., in reverse order.
### Query
<Properties>
<Property name='keyword' type='string' key='keyword'>
Keyword to search
</Property>
<Property name='status' type='string' key='status'>
succeeded/failed/stopped
</Property>
<Property name='page' type='int' key='page'>
current page, default is 1.
</Property>
<Property name='limit' type='int' key='limit'>
How many chat history messages to return in one request, default is 20.
</Property>
</Properties>
### Response
- `page` (int) Current page
- `limit` (int) Number of returned items, if input exceeds system limit, returns system limit amount
- `total` (int) Number of total items
- `has_more` (bool) Whether there is a next page
- `data` (array[object]) Log list
- `id` (string) ID
- `workflow_run` (object) Workflow run
- `id` (string) ID
- `version` (string) Version
- `status` (string) status of execution, `running` / `succeeded` / `failed` / `stopped`
- `error` (string) Optional reason of error
- `elapsed_time` (float) total seconds to be used
- `total_tokens` (int) tokens to be used
- `total_steps` (int) default 0
- `created_at` (timestamp) start time
- `finished_at` (timestamp) end time
- `created_from` (string) Created from
- `created_by_role` (string) Created by role
- `created_by_account` (string) Optional Created by account
- `created_by_end_user` (object) Created by end user
- `id` (string) ID
- `type` (string) Type
- `is_anonymous` (bool) Is anonymous
- `session_id` (string) Session ID
- `created_at` (timestamp) create time
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/workflows/logs" targetCode={`curl -X GET '${props.appDetail.api_base_url}/workflows/logs'\\\n --header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/workflows/logs?limit=1'
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### Response Example
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"page": 1,
"limit": 1,
"total": 7,
"has_more": true,
"data": [
{
"id": "e41b93f1-7ca2-40fd-b3a8-999aeb499cc0",
"workflow_run": {
"id": "c0640fc8-03ef-4481-a96c-8a13b732a36e",
"version": "2024-08-01 12:17:09.771832",
"status": "succeeded",
"error": null,
"elapsed_time": 1.3588523610014818,
"total_tokens": 0,
"total_steps": 3,
"created_at": 1726139643,
"finished_at": 1726139644
},
"created_from": "service-api",
"created_by_role": "end_user",
"created_by_account": null,
"created_by_end_user": {
"id": "7f7d9117-dd9d-441d-8970-87e5e7e687a3",
"type": "service_api",
"is_anonymous": false,
"session_id": "abc-123"
},
"created_at": 1726139644
}
]
}
```
</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
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</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
- `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
- `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
- `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'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/parameters' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"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>

View File

@@ -0,0 +1,729 @@
import { CodeGroup } from '../code.tsx'
import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from '../md.tsx'
# ワークフローアプリAPI
ワークフローアプリケーションは、セッションをサポートせず、翻訳、記事作成、要約AIなどに最適です。
<div>
### ベースURL
<CodeGroup title="コード" targetCode={props.appDetail.api_base_url}>
```javascript
```
</CodeGroup>
### 認証
サービスAPIは`API-Key`認証を使用します。
<i>**APIキーの漏洩を防ぐため、APIキーはクライアント側で共有または保存せず、サーバー側で保存することを強くお勧めします。**</i>
すべてのAPIリクエストにおいて、以下のように`Authorization`HTTPヘッダーにAPIキーを含めてください
<CodeGroup title="コード">
```javascript
Authorization: Bearer {API_KEY}
```
</CodeGroup>
</div>
---
<Heading
url='/workflows/run'
method='POST'
title='ワークフローを実行'
name='#Execute-Workflow'
/>
<Row>
<Col>
ワークフローを実行します。公開されたワークフローがないと実行できません。
### リクエストボディ
- `inputs` (object) 必須
アプリで定義されたさまざまな変数値の入力を許可します。
`inputs`パラメータには複数のキー/値ペアが含まれ、各キーは特定の変数に対応し、各値はその変数の特定の値です。
ワークフローアプリケーションは少なくとも1つのキー/値ペアの入力を必要とします。
変数がファイルタイプの場合、以下の`files`で説明されているキーを持つオブジェクトを指定してください。
- `response_mode` (string) 必須
応答の返却モードを指定します。サポートされているモード:
- `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>
- `user` (string) 必須
ユーザー識別子、エンドユーザーのアイデンティティを定義するために使用されます。
アプリケーション内で開発者によって一意に定義される必要があります。
- `files` (array[object]) オプション
ファイルリストは、テキスト理解と質問への回答を組み合わせたファイルの入力に適しています。モデルがファイルの解析と理解機能をサポートしている場合にのみ使用できます。
- `type` (string) サポートされているタイプ:
- `document` ('TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB')
- `image` ('JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG')
- `audio` ('MP3', 'M4A', 'WAV', 'WEBM', 'AMR')
- `video` ('MP4', 'MOV', 'MPEG', 'MPGA')
- `custom` (他のファイルタイプ)
- `transfer_method` (string) 転送方法、画像URLの場合は`remote_url` / ファイルアップロードの場合は`local_file`
- `url` (string) 画像URL転送方法が`remote_url`の場合)
- `upload_file_id` (string) アップロードされたファイルID、事前にファイルアップロードAPIを通じて取得する必要があります転送方法が`local_file`の場合)
### 応答
`response_mode`が`blocking`の場合、CompletionResponseオブジェクトを返します。
`response_mode`が`streaming`の場合、ChunkCompletionResponseストリームを返します。
### CompletionResponse
アプリの結果を返します。`Content-Type`は`application/json`です。
- `workflow_run_id` (string) ワークフロー実行の一意のID
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
- `data` (object) 結果の詳細
- `id` (string) ワークフロー実行のID
- `workflow_id` (string) 関連するワークフローのID
- `status` (string) 実行のステータス、`running` / `succeeded` / `failed` / `stopped`
- `outputs` (json) オプションの出力内容
- `error` (string) オプションのエラー理由
- `elapsed_time` (float) オプションの使用時間(秒)
- `total_tokens` (int) オプションの使用トークン数
- `total_steps` (int) デフォルト0
- `created_at` (timestamp) 開始時間
- `finished_at` (timestamp) 終了時間
### ChunkCompletionResponse
アプリによって出力されたストリームチャンクを返します。`Content-Type`は`text/event-stream`です。
各ストリーミングチャンクは`data:`で始まり、2つの改行文字`\n\n`で区切られます。以下のように表示されます:
<CodeGroup>
```streaming {{ title: '応答' }}
data: {"event": "message", "task_id": "900bbd43-dc0b-4383-a372-aa6e6c414227", "id": "663c5084-a254-4040-8ad3-51f2a3c1a77c", "answer": "Hi", "created_at": 1705398420}\n\n
```
</CodeGroup>
ストリーミングチャンクの構造は`event`に応じて異なります:
- `event: workflow_started` ワークフローが実行を開始
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
- `workflow_run_id` (string) ワークフロー実行の一意のID
- `event` (string) `workflow_started`に固定
- `data` (object) 詳細
- `id` (string) ワークフロー実行の一意のID
- `workflow_id` (string) 関連するワークフローのID
- `sequence_number` (int) 自己増加シリアル番号、アプリ内で自己増加し、1から始まります
- `created_at` (timestamp) 作成タイムスタンプ、例1705395332
- `event: node_started` ノード実行開始
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
- `workflow_run_id` (string) ワークフロー実行の一意のID
- `event` (string) `node_started`に固定
- `data` (object) 詳細
- `id` (string) ワークフロー実行の一意のID
- `node_id` (string) ードのID
- `node_type` (string) ノードのタイプ
- `title` (string) ノードの名前
- `index` (int) 実行シーケンス番号、トレースノードシーケンスを表示するために使用
- `predecessor_node_id` (string) オプションのプレフィックスードID、キャンバス表示実行パスに使用
- `inputs` (object) ノードで使用されるすべての前のノード変数の内容
- `created_at` (timestamp) 開始のタイムスタンプ、例1705395332
- `event: node_finished` ノード実行終了、同じイベントで異なる状態で成功または失敗
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
- `workflow_run_id` (string) ワークフロー実行の一意のID
- `event` (string) `node_finished`に固定
- `data` (object) 詳細
- `id` (string) ワークフロー実行の一意のID
- `node_id` (string) ードのID
- `node_type` (string) ノードのタイプ
- `title` (string) ノードの名前
- `index` (int) 実行シーケンス番号、トレースノードシーケンスを表示するために使用
- `predecessor_node_id` (string) オプションのプレフィックスードID、キャンバス表示実行パスに使用
- `inputs` (object) ノードで使用されるすべての前のノード変数の内容
- `process_data` (json) オプションのノードプロセスデータ
- `outputs` (json) オプションの出力内容
- `status` (string) 実行のステータス、`running` / `succeeded` / `failed` / `stopped`
- `error` (string) オプションのエラー理由
- `elapsed_time` (float) オプションの使用時間(秒)
- `execution_metadata` (json) メタデータ
- `total_tokens` (int) オプションの使用トークン数
- `total_price` (decimal) オプションの総コスト
- `currency` (string) オプション 例:`USD` / `RMB`
- `created_at` (timestamp) 開始のタイムスタンプ、例1705395332
- `event: workflow_finished` ワークフロー実行終了、同じイベントで異なる状態で成功または失敗
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
- `workflow_run_id` (string) ワークフロー実行の一意のID
- `event` (string) `workflow_finished`に固定
- `data` (object) 詳細
- `id` (string) ワークフロー実行のID
- `workflow_id` (string) 関連するワークフローのID
- `status` (string) 実行のステータス、`running` / `succeeded` / `failed` / `stopped`
- `outputs` (json) オプションの出力内容
- `error` (string) オプションのエラー理由
- `elapsed_time` (float) オプションの使用時間(秒)
- `total_tokens` (int) オプションの使用トークン数
- `total_steps` (int) デフォルト0
- `created_at` (timestamp) 開始時間
- `finished_at` (timestamp) 終了時間
- `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: ping` 接続を維持するために10秒ごとに送信されるPingイベント。
### エラー
- 400, `invalid_param`, 異常なパラメータ入力
- 400, `app_unavailable`, アプリの設定が利用できません
- 400, `provider_not_initialize`, 利用可能なモデル資格情報の設定がありません
- 400, `provider_quota_exceeded`, モデル呼び出しのクォータが不足しています
- 400, `model_currently_not_support`, 現在のモデルは利用できません
- 400, `workflow_request_error`, ワークフロー実行に失敗しました
- 500, 内部サーバーエラー
</Col>
<Col sticky>
<CodeGroup title="リクエスト" tag="POST" label="/workflows/run" targetCode={`curl -X POST '${props.appDetail.api_base_url}/workflows/run' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": ${JSON.stringify(props.inputs)},\n "response_mode": "streaming",\n "user": "abc-123"\n}'\n`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/workflows/run' \
--header 'Authorization: Bearer {api_key}' \
--header 'Content-Type: application/json' \
--data-raw '{
"inputs": {},
"response_mode": "streaming",
"user": "abc-123"
}'
```
</CodeGroup>
<CodeGroup title="ファイル変数の例">
```json {{ title: 'ファイル変数の例' }}
{
"inputs": {
"{variable_name}": {
"transfer_method": "local_file",
"upload_file_id": "{upload_file_id}",
"type": "{document_type}"
}
}
}
```
</CodeGroup>
### ブロッキングモード
<CodeGroup title="応答">
```json {{ title: '応答' }}
{
"workflow_run_id": "djflajgkldjgd",
"task_id": "9da23599-e713-473b-982c-4328d4f5c78a",
"data": {
"id": "fdlsjfjejkghjda",
"workflow_id": "fldjaslkfjlsda",
"status": "succeeded",
"outputs": {
"text": "Nice to meet you."
},
"error": null,
"elapsed_time": 0.875,
"total_tokens": 3562,
"total_steps": 8,
"created_at": 1705407629,
"finished_at": 1727807631
}
}
```
</CodeGroup>
### ストリーミングモード
<CodeGroup title="応答">
```streaming {{ title: '応答' }}
data: {"event": "workflow_started", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "workflow_id": "dfjasklfjdslag", "sequence_number": 1, "created_at": 1679586595}}
data: {"event": "node_started", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "node_id": "dfjasklfjdslag", "node_type": "start", "title": "Start", "index": 0, "predecessor_node_id": "fdljewklfklgejlglsd", "inputs": {}, "created_at": 1679586595}}
data: {"event": "node_finished", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "node_id": "dfjasklfjdslag", "node_type": "start", "title": "Start", "index": 0, "predecessor_node_id": "fdljewklfklgejlglsd", "inputs": {}, "outputs": {}, "status": "succeeded", "elapsed_time": 0.324, "execution_metadata": {"total_tokens": 63127864, "total_price": 2.378, "currency": "USD"}, "created_at": 1679586595}}
data: {"event": "workflow_finished", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "workflow_id": "dfjasklfjdslag", "outputs": {}, "status": "succeeded", "elapsed_time": 0.324, "total_tokens": 63127864, "total_steps": "1", "created_at": 1679586595, "finished_at": 1679976595}}
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>
<CodeGroup title="ファイルアップロードのサンプルコード">
```json {{ title: 'ファイルアップロードのサンプルコード' }}
import requests
import json
def upload_file(file_path, user):
upload_url = "https://api.dify.ai/v1/files/upload"
headers = {
"Authorization": "Bearer app-xxxxxxxx",
}
try:
print("ファイルをアップロードしています...")
with open(file_path, 'rb') as file:
files = {
'file': (file_path, file, 'text/plain') # ファイルが適切な MIME タイプでアップロードされていることを確認してください
}
data = {
"user": user,
"type": "TXT" # ファイルタイプをTXTに設定します
}
response = requests.post(upload_url, headers=headers, files=files, data=data)
if response.status_code == 201: # 201 は作成が成功したことを意味します
print("ファイルが正常にアップロードされました")
return response.json().get("id") # アップロードされたファイルIDを取得する
else:
print(f"ファイルのアップロードに失敗しました。ステータス コード: {response.status_code}")
return None
except Exception as e:
print(f"エラーが発生しました: {str(e)}")
return None
def run_workflow(file_id, user, response_mode="blocking"):
workflow_url = "https://api.dify.ai/v1/workflows/run"
headers = {
"Authorization": "Bearer app-xxxxxxxxx",
"Content-Type": "application/json"
}
data = {
"inputs": {
"orig_mail": {
"transfer_method": "local_file",
"upload_file_id": file_id,
"type": "document"
}
},
"response_mode": response_mode,
"user": user
}
try:
print("ワークフローを実行...")
response = requests.post(workflow_url, headers=headers, json=data)
if response.status_code == 200:
print("ワークフローが正常に実行されました")
return response.json()
else:
print(f"ワークフローの実行がステータス コードで失敗しました: {response.status_code}")
return {"status": "error", "message": f"Failed to execute workflow, status code: {response.status_code}"}
except Exception as e:
print(f"エラーが発生しました: {str(e)}")
return {"status": "error", "message": str(e)}
# 使用例
file_path = "{your_file_path}"
user = "difyuser"
# ファイルをアップロードする
file_id = upload_file(file_path, user)
if file_id:
# ファイルは正常にアップロードされました。ワークフローの実行を続行します
result = run_workflow(file_id, user)
print(result)
else:
print("ファイルのアップロードに失敗し、ワークフローを実行できません")
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/workflows/run/:workflow_id'
method='GET'
title='ワークフロー実行詳細を取得'
name='#get-workflow-run-detail'
/>
<Row>
<Col>
ワークフロー実行IDに基づいて、ワークフロータスクの現在の実行結果を取得します。
### パス
- `workflow_id` (string) ワークフローID、ストリーミングチャンクの返り値から取得可能
### 応答
- `id` (string) ワークフロー実行のID
- `workflow_id` (string) 関連するワークフローのID
- `status` (string) 実行のステータス、`running` / `succeeded` / `failed` / `stopped`
- `inputs` (json) 入力内容
- `outputs` (json) 出力内容
- `error` (string) エラー理由
- `total_steps` (int) タスクの総ステップ数
- `total_tokens` (int) 使用されるトークンの総数
- `created_at` (timestamp) 開始時間
- `finished_at` (timestamp) 終了時間
- `elapsed_time` (float) 使用される総秒数
</Col>
<Col sticky>
### リクエスト例
<CodeGroup title="リクエスト" tag="GET" label="/workflows/run/:workflow_id" targetCode={`curl -X GET '${props.appDetail.api_base_url}/workflows/run/:workflow_id' \\\n-H 'Authorization: Bearer {api_key}' \\\n-H 'Content-Type: application/json'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/workflows/run/:workflow_id' \
-H 'Authorization: Bearer {api_key}' \
-H 'Content-Type: application/json'
```
</CodeGroup>
### 応答例
<CodeGroup title="応答">
```json {{ title: '応答' }}
{
"id": "b1ad3277-089e-42c6-9dff-6820d94fbc76",
"workflow_id": "19eff89f-ec03-4f75-b0fc-897e7effea02",
"status": "succeeded",
"inputs": "{\"sys.files\": [], \"sys.user_id\": \"abc-123\"}",
"outputs": null,
"error": null,
"total_steps": 3,
"total_tokens": 0,
"created_at": "Thu, 18 Jul 2024 03:17:40 -0000",
"finished_at": "Thu, 18 Jul 2024 03:18:10 -0000",
"elapsed_time": 30.098514399956912
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/workflows/tasks/:task_id/stop'
method='POST'
title='生成を停止'
name='#stop-generatebacks'
/>
<Row>
<Col>
ストリーミングモードでのみサポートされています。
### パス
- `task_id` (string) タスクID、ストリーミングチャンクの返り値から取得可能
### リクエストボディ
- `user` (string) 必須
ユーザー識別子、エンドユーザーのアイデンティティを定義するために使用され、送信メッセージインターフェースで渡されたユーザーと一致している必要があります。
### 応答
- `result` (string) 常に"success"を返します
</Col>
<Col sticky>
### リクエスト例
<CodeGroup title="リクエスト" tag="POST" label="/workflows/tasks/:task_id/stop" targetCode={`curl -X POST '${props.appDetail.api_base_url}/workflows/tasks/:task_id/stop' \\\n-H 'Authorization: Bearer {api_key}' \\\n-H 'Content-Type: application/json' \\\n--data-raw '{"user": "abc-123"}'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/workflows/tasks/:task_id/stop' \
-H 'Authorization: Bearer {api_key}' \
-H 'Content-Type: application/json' \
--data-raw '{
"user": "abc-123"
}'
```
</CodeGroup>
### 応答例
<CodeGroup title="応答">
```json {{ title: '応答' }}
{
"result": "success"
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/files/upload'
method='POST'
title='ファイルアップロード'
name='#file-upload'
/>
<Row>
<Col>
メッセージ送信時に使用するためのファイルをアップロードし、画像とテキストのマルチモーダル理解を可能にします。
ワークフローでサポートされている任意の形式をサポートします。
アップロードされたファイルは、現在のエンドユーザーのみが使用できます。
### リクエストボディ
このインターフェースは`multipart/form-data`リクエストを必要とします。
- `file` (File) 必須
アップロードするファイル。
- `user` (string) 必須
ユーザー識別子、開発者のルールで定義され、アプリケーション内で一意でなければなりません。
### 応答
アップロードが成功すると、サーバーはファイルの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="リクエスト" tag="POST" label="/files/upload" targetCode={`curl -X POST '${props.appDetail.api_base_url}/files/upload' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'file=@localfile;type=image/[png|jpeg|jpg|webp|gif] \\\n--form 'user=abc-123'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/files/upload' \
--header 'Authorization: Bearer {api_key}' \
--form 'file=@"/path/to/file"'
```
</CodeGroup>
### 応答例
<CodeGroup title="応答">
```json {{ title: '応答' }}
{
"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='/workflows/logs'
method='GET'
title='ワークフローログを取得'
name='#Get-Workflow-Logs'
/>
<Row>
<Col>
ワークフローログを返します。最初のページは最新の`{limit}`メッセージを返します。つまり、逆順です。
### クエリ
<Properties>
<Property name='keyword' type='string' key='keyword'>
検索するキーワード
</Property>
<Property name='status' type='string' key='status'>
succeeded/failed/stopped
</Property>
<Property name='page' type='int' key='page'>
現在のページ、デフォルトは1。
</Property>
<Property name='limit' type='int' key='limit'>
1回のリクエストで返すチャット履歴メッセージの数、デフォルトは20。
</Property>
</Properties>
### 応答
- `page` (int) 現在のページ
- `limit` (int) 返されたアイテムの数、入力がシステム制限を超える場合、システム制限量を返します
- `total` (int) 合計アイテム数
- `has_more` (bool) 次のページがあるかどうか
- `data` (array[object]) ログリスト
- `id` (string) ID
- `workflow_run` (object) ワークフロー実行
- `id` (string) ID
- `version` (string) バージョン
- `status` (string) 実行のステータス、`running` / `succeeded` / `failed` / `stopped`
- `error` (string) オプションのエラー理由
- `elapsed_time` (float) 使用される総秒数
- `total_tokens` (int) 使用されるトークン数
- `total_steps` (int) デフォルト0
- `created_at` (timestamp) 開始時間
- `finished_at` (timestamp) 終了時間
- `created_from` (string) 作成元
- `created_by_role` (string) 作成者の役割
- `created_by_account` (string) オプションの作成者アカウント
- `created_by_end_user` (object) エンドユーザーによって作成
- `id` (string) ID
- `type` (string) タイプ
- `is_anonymous` (bool) 匿名かどうか
- `session_id` (string) セッションID
- `created_at` (timestamp) 作成時間
</Col>
<Col sticky>
<CodeGroup title="リクエスト" tag="GET" label="/workflows/logs" targetCode={`curl -X GET '${props.appDetail.api_base_url}/workflows/logs'\\\n --header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/workflows/logs?limit=1'
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### 応答例
<CodeGroup title="応答">
```json {{ title: '応答' }}
{
"page": 1,
"limit": 1,
"total": 7,
"has_more": true,
"data": [
{
"id": "e41b93f1-7ca2-40fd-b3a8-999aeb499cc0",
"workflow_run": {
"id": "c0640fc8-03ef-4481-a96c-8a13b732a36e",
"version": "2024-08-01 12:17:09.771832",
"status": "succeeded",
"error": null,
"elapsed_time": 1.3588523610014818,
"total_tokens": 0,
"total_steps": 3,
"created_at": 1726139643,
"finished_at": 1726139644
},
"created_from": "service-api",
"created_by_role": "end_user",
"created_by_account": null,
"created_by_end_user": {
"id": "7f7d9117-dd9d-441d-8970-87e5e7e687a3",
"type": "service_api",
"is_anonymous": false,
"session_id": "abc-123"
},
"created_at": 1726139644
}
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='アプリケーションの基本情報を取得'
name='#info'
/>
<Row>
<Col>
このアプリケーションの基本情報を取得するために使用されます
### Response
- `name` (string) アプリケーションの名前
- `description` (string) アプリケーションの説明
- `tags` (array[string]) アプリケーションのタグ
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='アプリケーションのパラメータ情報を取得'
name='#parameters'
/>
<Row>
<Col>
ページに入る際に、機能、入力パラメータ名、タイプ、デフォルト値などの情報を取得するために使用されます。
### 応答
- `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) ファイルアップロード設定
- `image` (object) 画像設定
現在サポートされている画像タイプのみ:`png`, `jpg`, `jpeg`, `webp`, `gif`
- `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="リクエスト" tag="GET" label="/parameters" targetCode={` curl -X GET '${props.appDetail.api_base_url}/parameters'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/parameters' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="応答">
```json {{ title: '応答' }}
{
"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>

View File

@@ -0,0 +1,720 @@
import { CodeGroup } from '../code.tsx'
import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from '../md.tsx'
# Workflow 应用 API
Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等等。
<div>
### Base URL
<CodeGroup title="Code" targetCode={props.appDetail.api_base_url}>
```javascript
```
</CodeGroup>
### Authentication
Dify Service API 使用 `API-Key` 进行鉴权。
<i>**强烈建议开发者把 `API-Key` 放在后端存储,而非分享或者放在客户端存储,以免 `API-Key` 泄露,导致财产损失。**</i>
所有 API 请求都应在 **`Authorization`** HTTP Header 中包含您的 `API-Key`,如下所示:
<CodeGroup title="Code">
```javascript
Authorization: Bearer {API_KEY}
```
</CodeGroup>
</div>
---
<Heading
url='/workflows/run'
method='POST'
title='执行 workflow'
name='#Execute-Workflow'
/>
<Row>
<Col>
执行 workflow没有已发布的 workflow不可执行。
### Request Body
- `inputs` (object) Required
允许传入 App 定义的各变量值。
inputs 参数包含了多组键值对Key/Value pairs每组的键对应一个特定变量每组的值则是该变量的具体值。
如果变量是文件类型,请指定一个包含以下 `files` 中所述键的对象。
- `response_mode` (string) Required
返回响应模式,支持:
- `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>
- `user` (string) Required
用户标识,用于定义终端用户的身份,方便检索、统计。
由开发者定义规则,需保证用户标识在应用内唯一。
- `files` (array[object]) Optional
文件列表,适用于传入文件结合文本理解并回答问题,仅当模型支持该类型文件解析能力时可用。
- `type` (string) 支持类型:
- `document` 具体类型包含:'TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
- `image` 具体类型包含:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
- `audio` 具体类型包含:'MP3', 'M4A', 'WAV', 'WEBM', 'AMR'
- `video` 具体类型包含:'MP4', 'MOV', 'MPEG', 'MPGA'
- `custom` 具体类型包含:其他文件类型
- `transfer_method` (string) 传递方式,`remote_url` 图片地址 / `local_file` 上传文件
- `url` (string) 图片地址(仅当传递方式为 `remote_url` 时)
- `upload_file_id` (string) (string) 上传文件 ID仅当传递方式为 `local_file` 时)
### Response
当 `response_mode` 为 `blocking` 时,返回 CompletionResponse object。
当 `response_mode` 为 `streaming`时,返回 ChunkCompletionResponse object 流式序列。
### CompletionResponse
返回完整的 App 结果,`Content-Type` 为 `application/json` 。
- `workflow_run_id` (string) workflow 执行 ID
- `task_id` (string) 任务 ID用于请求跟踪和下方的停止响应接口
- `data` (object) 详细内容
- `id` (string) workflow 执行 ID
- `workflow_id` (string) 关联 Workflow ID
- `status` (string) 执行状态, `running` / `succeeded` / `failed` / `stopped`
- `outputs` (json) Optional 输出内容
- `error` (string) Optional 错误原因
- `elapsed_time` (float) Optional 耗时(s)
- `total_tokens` (int) Optional 总使用 tokens
- `total_steps` (int) 总步数(冗余),默认 0
- `created_at` (timestamp) 开始时间
- `finished_at` (timestamp) 结束时间
### ChunkCompletionResponse
返回 App 输出的流式块,`Content-Type` 为 `text/event-stream`。
每个流式块均为 data: 开头,块之间以 `\n\n` 即两个换行符分隔,如下所示:
<CodeGroup>
```streaming {{ title: 'Response' }}
data: {"event": "message", "task_id": "900bbd43-dc0b-4383-a372-aa6e6c414227", "id": "663c5084-a254-4040-8ad3-51f2a3c1a77c", "answer": "Hi", "created_at": 1705398420}\n\n
```
</CodeGroup>
流式块中根据 `event` 不同,结构也不同,包含以下类型:
- `event: workflow_started` workflow 开始执行
- `task_id` (string) 任务 ID用于请求跟踪和下方的停止响应接口
- `workflow_run_id` (string) workflow 执行 ID
- `event` (string) 固定为 `workflow_started`
- `data` (object) 详细内容
- `id` (string) workflow 执行 ID
- `workflow_id` (string) 关联 Workflow ID
- `sequence_number` (int) 自增序号App 内自增,从 1 开始
- `created_at` (timestamp) 开始时间
- `event: node_started` node 开始执行
- `task_id` (string) 任务 ID用于请求跟踪和下方的停止响应接口
- `workflow_run_id` (string) workflow 执行 ID
- `event` (string) 固定为 `node_started`
- `data` (object) 详细内容
- `id` (string) workflow 执行 ID
- `node_id` (string) 节点 ID
- `node_type` (string) 节点类型
- `title` (string) 节点名称
- `index` (int) 执行序号,用于展示 Tracing Node 顺序
- `predecessor_node_id` (string) 前置节点 ID用于画布展示执行路径
- `inputs` (object) 节点中所有使用到的前置节点变量内容
- `created_at` (timestamp) 开始时间
- `event: node_finished` node 执行结束,成功失败同一事件中不同状态
- `task_id` (string) 任务 ID用于请求跟踪和下方的停止响应接口
- `workflow_run_id` (string) workflow 执行 ID
- `event` (string) 固定为 `node_finished`
- `data` (object) 详细内容
- `id` (string) node 执行 ID
- `node_id` (string) 节点 ID
- `index` (int) 执行序号,用于展示 Tracing Node 顺序
- `predecessor_node_id` (string) optional 前置节点 ID用于画布展示执行路径
- `inputs` (object) 节点中所有使用到的前置节点变量内容
- `process_data` (json) Optional 节点过程数据
- `outputs` (json) Optional 输出内容
- `status` (string) 执行状态 `running` / `succeeded` / `failed` / `stopped`
- `error` (string) Optional 错误原因
- `elapsed_time` (float) Optional 耗时(s)
- `execution_metadata` (json) 元数据
- `total_tokens` (int) optional 总使用 tokens
- `total_price` (decimal) optional 总费用
- `currency` (string) optional 货币,如 `USD` / `RMB`
- `created_at` (timestamp) 开始时间
- `event: workflow_finished` workflow 执行结束,成功失败同一事件中不同状态
- `task_id` (string) 任务 ID用于请求跟踪和下方的停止响应接口
- `workflow_run_id` (string) workflow 执行 ID
- `event` (string) 固定为 `workflow_finished`
- `data` (object) 详细内容
- `id` (string) workflow 执行 ID
- `workflow_id` (string) 关联 Workflow ID
- `status` (string) 执行状态 `running` / `succeeded` / `failed` / `stopped`
- `outputs` (json) Optional 输出内容
- `error` (string) Optional 错误原因
- `elapsed_time` (float) Optional 耗时(s)
- `total_tokens` (int) Optional 总使用 tokens
- `total_steps` (int) 总步数(冗余),默认 0
- `created_at` (timestamp) 开始时间
- `finished_at` (timestamp) 结束时间
- `event: tts_message` TTS 音频流事件语音合成输出。内容是Mp3格式的音频块使用 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: ping` 每 10s 一次的 ping 事件,保持连接存活。
### Errors
- 400`invalid_param`,传入参数异常
- 400`app_unavailable`App 配置不可用
- 400`provider_not_initialize`,无可用模型凭据配置
- 400`provider_quota_exceeded`,模型调用额度不足
- 400`model_currently_not_support`,当前模型不可用
- 400`workflow_request_error`workflow 执行失败
- 500服务内部异常
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/workflows/run" targetCode={`curl -X POST '${props.appDetail.api_base_url}/workflows/run' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": ${JSON.stringify(props.inputs)},\n "response_mode": "streaming",\n "user": "abc-123"\n}'\n`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/workflows/run' \
--header 'Authorization: Bearer {api_key}' \
--header 'Content-Type: application/json' \
--data-raw '{
"inputs": {},
"response_mode": "streaming",
"user": "abc-123"
}'
```
</CodeGroup>
<CodeGroup title="File variable example">
```json {{ title: 'File variable example' }}
{
"inputs": {
"{variable_name}": {
"transfer_method": "local_file",
"upload_file_id": "{upload_file_id}",
"type": "{document_type}"
}
}
}
```
</CodeGroup>
### Blocking Mode
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"workflow_run_id": "djflajgkldjgd",
"task_id": "9da23599-e713-473b-982c-4328d4f5c78a",
"data": {
"id": "fdlsjfjejkghjda",
"workflow_id": "fldjaslkfjlsda",
"status": "succeeded",
"outputs": {
"text": "Nice to meet you."
},
"error": null,
"elapsed_time": 0.875,
"total_tokens": 3562,
"total_steps": 8,
"created_at": 1705407629,
"finished_at": 1727807631
}
}
```
</CodeGroup>
### Streaming Mode
<CodeGroup title="Response">
```streaming {{ title: 'Response' }}
data: {"event": "workflow_started", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "workflow_id": "dfjasklfjdslag", "sequence_number": 1, "created_at": 1679586595}}
data: {"event": "node_started", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "node_id": "dfjasklfjdslag", "node_type": "start", "title": "Start", "index": 0, "predecessor_node_id": "fdljewklfklgejlglsd", "inputs": {}, "created_at": 1679586595}}
data: {"event": "node_finished", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "node_id": "dfjasklfjdslag", "node_type": "start", "title": "Start", "index": 0, "predecessor_node_id": "fdljewklfklgejlglsd", "inputs": {}, "outputs": {}, "status": "succeeded", "elapsed_time": 0.324, "execution_metadata": {"total_tokens": 63127864, "total_price": 2.378, "currency": "USD"}, "created_at": 1679586595}}
data: {"event": "workflow_finished", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "workflow_id": "dfjasklfjdslag", "outputs": {}, "status": "succeeded", "elapsed_time": 0.324, "total_tokens": 63127864, "total_steps": "1", "created_at": 1679586595, "finished_at": 1679976595}}
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>
<CodeGroup title="File upload sample code">
```json {{ title: 'File upload sample code' }}
import requests
import json
def upload_file(file_path, user):
upload_url = "https://api.dify.ai/v1/files/upload"
headers = {
"Authorization": "Bearer app-xxxxxxxx",
}
try:
print("上传文件中...")
with open(file_path, 'rb') as file:
files = {
'file': (file_path, file, 'text/plain') # 确保文件以适当的MIME类型上传
}
data = {
"user": user,
"type": "TXT" # 设置文件类型为TXT
}
response = requests.post(upload_url, headers=headers, files=files, data=data)
if response.status_code == 201: # 201 表示创建成功
print("文件上传成功")
return response.json().get("id") # 获取上传的文件 ID
else:
print(f"文件上传失败,状态码: {response.status_code}")
return None
except Exception as e:
print(f"发生错误: {str(e)}")
return None
def run_workflow(file_id, user, response_mode="blocking"):
workflow_url = "https://api.dify.ai/v1/workflows/run"
headers = {
"Authorization": "Bearer app-xxxxxxxxx",
"Content-Type": "application/json"
}
data = {
"inputs": {
"orig_mail": {
"transfer_method": "local_file",
"upload_file_id": file_id,
"type": "document"
}
},
"response_mode": response_mode,
"user": user
}
try:
print("运行工作流...")
response = requests.post(workflow_url, headers=headers, json=data)
if response.status_code == 200:
print("工作流执行成功")
return response.json()
else:
print(f"工作流执行失败,状态码: {response.status_code}")
return {"status": "error", "message": f"Failed to execute workflow, status code: {response.status_code}"}
except Exception as e:
print(f"发生错误: {str(e)}")
return {"status": "error", "message": str(e)}
# 使用示例
file_path = "{your_file_path}"
user = "difyuser"
# 上传文件
file_id = upload_file(file_path, user)
if file_id:
# 文件上传成功,继续运行工作流
result = run_workflow(file_id, user)
print(result)
else:
print("文件上传失败,无法执行工作流")
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/workflows/run/:workflow_id'
method='GET'
title='获取workflow执行情况'
name='#get-workflow-run-detail'
/>
<Row>
<Col>
根据 workflow 执行 ID 获取 workflow 任务当前执行结果
### Path
- `workflow_id` (string) workflow 执行 ID可在流式返回 Chunk 中获取
### Response
- `id` (string) workflow 执行 ID
- `workflow_id` (string) 关联的 Workflow ID
- `status` (string) 执行状态 `running` / `succeeded` / `failed` / `stopped`
- `inputs` (json) 任务输入内容
- `outputs` (json) 任务输出内容
- `error` (string) 错误原因
- `total_steps` (int) 任务执行总步数
- `total_tokens` (int) 任务执行总 tokens
- `created_at` (timestamp) 任务开始时间
- `finished_at` (timestamp) 任务结束时间
- `elapsed_time` (float) 耗时(s)
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="GET" label="/workflows/run/:workflow_id" targetCode={`curl -X GET '${props.appDetail.api_base_url}/workflows/run/:workflow_id' \\\n-H 'Authorization: Bearer {api_key}' \\\n-H 'Content-Type: application/json'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/workflows/run/:workflow_id' \
-H 'Authorization: Bearer {api_key}' \
-H 'Content-Type: application/json'
```
</CodeGroup>
### Response Example
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"id": "b1ad3277-089e-42c6-9dff-6820d94fbc76",
"workflow_id": "19eff89f-ec03-4f75-b0fc-897e7effea02",
"status": "succeeded",
"inputs": "{\"sys.files\": [], \"sys.user_id\": \"abc-123\"}",
"outputs": null,
"error": null,
"total_steps": 3,
"total_tokens": 0,
"created_at": "Thu, 18 Jul 2024 03:17:40 -0000",
"finished_at": "Thu, 18 Jul 2024 03:18:10 -0000",
"elapsed_time": 30.098514399956912
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/workflows/tasks/:task_id/stop'
method='POST'
title='停止响应'
name='#stop-generatebacks'
/>
<Row>
<Col>
仅支持流式模式。
### Path
- `task_id` (string) 任务 ID可在流式返回 Chunk 中获取
### Request Body
- `user` (string) Required
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。
### Response
- `result` (string) 固定返回 "success"
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="POST" label="/workflows/tasks/:task_id/stop" targetCode={`curl -X POST '${props.appDetail.api_base_url}/workflows/tasks/:task_id/stop' \\\n-H 'Authorization: Bearer {api_key}' \\\n-H 'Content-Type: application/json' \\\n--data-raw '{"user": "abc-123"}'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/workflows/tasks/:task_id/stop' \
-H 'Authorization: Bearer {api_key}' \
-H 'Content-Type: application/json' \
--data-raw '{
"user": "abc-123"
}'
```
</CodeGroup>
### Response Example
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"result": "success"
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/files/upload'
method='POST'
title='上传文件'
name='#files-upload'
/>
<Row>
<Col>
上传文件并在发送消息时使用,可实现图文多模态理解。
支持您的工作流程所支持的任何格式。
<i>上传的文件仅供当前终端用户使用。</i>
### Request Body
该接口需使用 `multipart/form-data` 进行请求。
<Properties>
<Property name='file' type='file' key='file'>
要上传的文件。
</Property>
<Property name='user' type='string' key='user'>
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。
</Property>
</Properties>
### Response
成功上传后,服务器会返回文件的 ID 和相关信息。
- `id` (uuid) ID
- `name` (string) 文件名
- `size` (int) 文件大小byte
- `extension` (string) 文件后缀
- `mime_type` (string) 文件 mime-type
- `created_by` (uuid) 上传人 ID
- `created_at` (timestamp) 上传时间
### Errors
- 400`no_file_uploaded`,必须提供文件
- 400`too_many_files`,目前只接受一个文件
- 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 大小限制
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/files/upload" targetCode={`curl -X POST '${props.appDetail.api_base_url}/files/upload' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'file=@localfile;type=image/[png|jpeg|jpg|webp|gif] \\\n--form 'user=abc-123'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/files/upload' \
--header 'Authorization: Bearer {api_key}' \
--form 'file=@"/path/to/file"'
```
</CodeGroup>
<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": 123,
"created_at": 1577836800,
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/workflows/logs'
method='GET'
title='获取 workflow 日志'
name='#Get-Workflow-Logs'
/>
<Row>
<Col>
倒序返回workflow日志
### Query
<Properties>
<Property name='keyword' type='string' key='keyword'>
关键字
</Property>
<Property name='status' type='string' key='status'>
执行状态 succeeded/failed/stopped
</Property>
<Property name='page' type='int' key='page'>
当前页码, 默认1.
</Property>
<Property name='limit' type='int' key='limit'>
每页条数, 默认20.
</Property>
</Properties>
### Response
- `page` (int) 当前页码
- `limit` (int) 每页条数
- `total` (int) 总条数
- `has_more` (bool) 是否还有更多数据
- `data` (array[object]) 当前页码的数据
- `id` (string) 标识
- `workflow_run` (object) Workflow 执行日志
- `id` (string) 标识
- `version` (string) 版本
- `status` (string) 执行状态, `running` / `succeeded` / `failed` / `stopped`
- `error` (string) (可选) 错误
- `elapsed_time` (float) 耗时,单位秒
- `total_tokens` (int) 消耗的token数量
- `total_steps` (int) 执行步骤长度
- `created_at` (timestamp) 开始时间
- `finished_at` (timestamp) 结束时间
- `created_from` (string) 来源
- `created_by_role` (string) 角色
- `created_by_account` (string) (可选) 帐号
- `created_by_end_user` (object) 用户
- `id` (string) 标识
- `type` (string) 类型
- `is_anonymous` (bool) 是否匿名
- `session_id` (string) 会话标识
- `created_at` (timestamp) 创建时间
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/workflows/logs" targetCode={`curl -X GET '${props.appDetail.api_base_url}/workflows/logs'\\\n --header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/workflows/logs?limit=1'
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### Response Example
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"page": 1,
"limit": 1,
"total": 7,
"has_more": true,
"data": [
{
"id": "e41b93f1-7ca2-40fd-b3a8-999aeb499cc0",
"workflow_run": {
"id": "c0640fc8-03ef-4481-a96c-8a13b732a36e",
"version": "2024-08-01 12:17:09.771832",
"status": "succeeded",
"error": null,
"elapsed_time": 1.3588523610014818,
"total_tokens": 0,
"total_steps": 3,
"created_at": 1726139643,
"finished_at": 1726139644
},
"created_from": "service-api",
"created_by_role": "end_user",
"created_by_account": null,
"created_by_end_user": {
"id": "7f7d9117-dd9d-441d-8970-87e5e7e687a3",
"type": "service_api",
"is_anonymous": false,
"session_id": "abc-123"
},
"created_at": 1726139644
}
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='获取应用基本信息'
name='#info'
/>
<Row>
<Col>
用于获取应用的基本信息
### Response
- `name` (string) 应用名称
- `description` (string) 应用描述
- `tags` (array[string]) 应用标签
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='获取应用参数'
name='#parameters'
/>
<Row>
<Col>
用于进入页面一开始,获取功能开关、输入参数名称、类型及默认值等使用。
### Response
- `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) 文件上传配置
- `image` (object) 图片设置
当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`
- `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'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/parameters' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"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>