mirror of https://github.com/langgenius/dify.git
workflow log list
This commit is contained in:
parent
b113711a86
commit
2691164fc4
|
|
@ -27,12 +27,15 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
|||
const { data: response } = useSWR(detailParams, fetchAppDetail)
|
||||
|
||||
const appModeName = (() => {
|
||||
if (response?.mode === 'chat')
|
||||
if (response?.mode === 'chat' || response?.mode === 'advanced-chat')
|
||||
return t('app.types.chatbot')
|
||||
|
||||
if (response?.mode === 'agent')
|
||||
if (response?.mode === 'agent-chat')
|
||||
return t('app.types.agent')
|
||||
|
||||
if (response?.mode === 'completion')
|
||||
return t('app.types.completion')
|
||||
|
||||
return t('app.types.workflow')
|
||||
})()
|
||||
|
||||
|
|
@ -55,7 +58,9 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
|||
return (
|
||||
<div className={cn(s.app, 'flex', 'overflow-hidden')}>
|
||||
<AppSideBar title={response.name} icon={response.icon} icon_background={response.icon_background} desc={appModeName} navigation={navigation} />
|
||||
<div className="bg-white grow overflow-hidden">{children}</div>
|
||||
<div className="bg-white grow overflow-hidden">
|
||||
{React.cloneElement(children as React.ReactElement<any>, { appMode: response.mode })}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
import React from 'react'
|
||||
import Main from '@/app/components/app/log-annotation'
|
||||
import { PageType } from '@/app/components/app/configuration/toolbox/annotation/type'
|
||||
import type { AppMode } from '@/types/app'
|
||||
|
||||
export type IProps = {
|
||||
params: { appId: string }
|
||||
appMode: AppMode
|
||||
}
|
||||
|
||||
const Logs = async ({
|
||||
params: { appId },
|
||||
appMode,
|
||||
}: IProps) => {
|
||||
return (
|
||||
<Main pageType={PageType.log} appId={appId} />
|
||||
<Main appMode={'workflow'} pageType={PageType.log} appId={appId} />
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,26 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import cn from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import Log from '@/app/components/app/log'
|
||||
import WorkflowLog from '@/app/components/app/workflow-log'
|
||||
import Annotation from '@/app/components/app/annotation'
|
||||
import { PageType } from '@/app/components/app/configuration/toolbox/annotation/type'
|
||||
import TabSlider from '@/app/components/base/tab-slider-plain'
|
||||
import type { AppMode } from '@/types/app'
|
||||
|
||||
type Props = {
|
||||
pageType: PageType
|
||||
appId: string
|
||||
appMode: AppMode
|
||||
}
|
||||
|
||||
const LogAnnotation: FC<Props> = ({
|
||||
pageType,
|
||||
appId,
|
||||
appMode,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const router = useRouter()
|
||||
|
|
@ -27,17 +32,20 @@ const LogAnnotation: FC<Props> = ({
|
|||
|
||||
return (
|
||||
<div className='pt-4 px-6 h-full flex flex-col'>
|
||||
<TabSlider
|
||||
className='shrink-0'
|
||||
value={pageType}
|
||||
onChange={(value) => {
|
||||
router.push(`/app/${appId}/${value === PageType.log ? 'logs' : 'annotations'}`)
|
||||
}}
|
||||
options={options}
|
||||
/>
|
||||
<div className='mt-3 grow'>
|
||||
{pageType === PageType.log && (<Log appId={appId} />)}
|
||||
{appMode !== 'workflow' && (
|
||||
<TabSlider
|
||||
className='shrink-0'
|
||||
value={pageType}
|
||||
onChange={(value) => {
|
||||
router.push(`/app/${appId}/${value === PageType.log ? 'logs' : 'annotations'}`)
|
||||
}}
|
||||
options={options}
|
||||
/>
|
||||
)}
|
||||
<div className={cn('grow', appMode !== 'workflow' && 'mt-3')}>
|
||||
{pageType === PageType.log && appMode !== 'workflow' && (<Log appId={appId} />)}
|
||||
{pageType === PageType.annotation && (<Annotation appId={appId} />)}
|
||||
{pageType === PageType.log && appMode === 'workflow' && (<WorkflowLog appId={appId} />)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
MagnifyingGlassIcon,
|
||||
} from '@heroicons/react/24/solid'
|
||||
import type { QueryParam } from './index'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
|
||||
type IFilterProps = {
|
||||
queryParams: QueryParam
|
||||
setQueryParams: (v: QueryParam) => void
|
||||
}
|
||||
|
||||
const Filter: FC<IFilterProps> = ({ queryParams, setQueryParams }: IFilterProps) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className='flex flex-row flex-wrap gap-y-2 gap-x-4 items-center mb-4 text-gray-900 text-base'>
|
||||
<div className="relative rounded-md">
|
||||
<SimpleSelect
|
||||
defaultValue={'all'}
|
||||
className='!w-[100px]'
|
||||
onSelect={
|
||||
(item) => {
|
||||
setQueryParams({ ...queryParams, status: item.value as string })
|
||||
}
|
||||
}
|
||||
items={[{ value: 'all', name: 'All' },
|
||||
{ value: 'succeeded', name: 'Success' },
|
||||
{ value: 'failed', name: 'Fail' },
|
||||
{ value: 'stopped', name: 'Stop' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
<MagnifyingGlassIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
name="query"
|
||||
className="block w-[240px] bg-gray-100 shadow-sm rounded-md border-0 py-1.5 pl-10 text-gray-900 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-gray-200 focus-visible:outline-none sm:text-sm sm:leading-6"
|
||||
placeholder={t('common.operation.search')!}
|
||||
value={queryParams.keyword}
|
||||
onChange={(e) => {
|
||||
setQueryParams({ ...queryParams, keyword: e.target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Filter
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
'use client'
|
||||
import type { FC, SVGProps } from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { Pagination } from 'react-headless-pagination'
|
||||
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import Link from 'next/link'
|
||||
import List from './list'
|
||||
import Filter from './filter'
|
||||
import s from './style.module.css'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { fetchWorkflowLogs } from '@/service/log'
|
||||
import { fetchAppDetail } from '@/service/apps'
|
||||
import { APP_PAGE_LIMIT } from '@/config'
|
||||
import type { AppMode } from '@/types/app'
|
||||
|
||||
export type ILogsProps = {
|
||||
appId: string
|
||||
}
|
||||
|
||||
export type QueryParam = {
|
||||
status?: string
|
||||
keyword?: string
|
||||
}
|
||||
|
||||
const ThreeDotsIcon = ({ className }: SVGProps<SVGElement>) => {
|
||||
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
|
||||
<path d="M5 6.5V5M8.93934 7.56066L10 6.5M10.0103 11.5H11.5103" stroke="#374151" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
}
|
||||
|
||||
const EmptyElement: FC<{ appUrl: string }> = ({ appUrl }) => {
|
||||
const { t } = useTranslation()
|
||||
const pathname = usePathname()
|
||||
const pathSegments = pathname.split('/')
|
||||
pathSegments.pop()
|
||||
return <div className='flex items-center justify-center h-full'>
|
||||
<div className='bg-gray-50 w-[560px] h-fit box-border px-5 py-4 rounded-2xl'>
|
||||
<span className='text-gray-700 font-semibold'>{t('appLog.table.empty.element.title')}<ThreeDotsIcon className='inline relative -top-3 -left-1.5' /></span>
|
||||
<div className='mt-2 text-gray-500 text-sm font-normal'>
|
||||
<Trans
|
||||
i18nKey="appLog.table.empty.element.content"
|
||||
components={{ shareLink: <Link href={`${pathSegments.join('/')}/overview`} className='text-primary-600' />, testLink: <Link href={appUrl} className='text-primary-600' target='_blank' rel='noopener noreferrer' /> }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
const Logs: FC<ILogsProps> = ({ appId }) => {
|
||||
const { t } = useTranslation()
|
||||
const [queryParams, setQueryParams] = useState<QueryParam>({ status: 'all' })
|
||||
const [currPage, setCurrPage] = React.useState<number>(0)
|
||||
|
||||
const query = {
|
||||
page: currPage + 1,
|
||||
limit: APP_PAGE_LIMIT,
|
||||
...queryParams,
|
||||
}
|
||||
|
||||
// Get the app type first
|
||||
const { data: appDetail } = useSWR({ url: '/apps', id: appId }, fetchAppDetail)
|
||||
|
||||
const getWebAppType = (appType?: AppMode) => {
|
||||
if (!appType)
|
||||
return ''
|
||||
if (appType === 'completion' || appType === 'workflow')
|
||||
return 'completion'
|
||||
return 'chat'
|
||||
}
|
||||
|
||||
const { data: workflowLogs, mutate } = useSWR({
|
||||
url: `/apps/${appId}/workflow-app-logs`,
|
||||
params: query,
|
||||
}, fetchWorkflowLogs)
|
||||
const total = workflowLogs?.total
|
||||
|
||||
return (
|
||||
<div className='flex flex-col h-full'>
|
||||
<h1 className='text-md font-semibold text-gray-900'>{t('appLog.workflowTitle')}</h1>
|
||||
<p className='flex text-sm font-normal text-gray-500'>{t('appLog.workflowSubtitle')}</p>
|
||||
<div className='flex flex-col py-4 flex-1'>
|
||||
<Filter queryParams={queryParams} setQueryParams={setQueryParams} />
|
||||
{/* workflow log */}
|
||||
{total === undefined
|
||||
? <Loading type='app' />
|
||||
: total > 0
|
||||
? <List logs={workflowLogs} appDetail={appDetail} onRefresh={mutate} />
|
||||
: <EmptyElement appUrl={`${appDetail?.site.app_base_url}/${getWebAppType(appDetail?.mode)}/${appDetail?.site.access_token}`} />
|
||||
}
|
||||
{/* Show Pagination only if the total is more than the limit */}
|
||||
{(total && total > APP_PAGE_LIMIT)
|
||||
? <Pagination
|
||||
className="flex items-center w-full h-10 text-sm select-none mt-8"
|
||||
currentPage={currPage}
|
||||
edgePageCount={2}
|
||||
middlePagesSiblingCount={1}
|
||||
setCurrentPage={setCurrPage}
|
||||
totalPages={Math.ceil(total / APP_PAGE_LIMIT)}
|
||||
truncableClassName="w-8 px-0.5 text-center"
|
||||
truncableText="..."
|
||||
>
|
||||
<Pagination.PrevButton
|
||||
disabled={currPage === 0}
|
||||
className={`flex items-center mr-2 text-gray-500 focus:outline-none ${currPage === 0 ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:text-gray-600 dark:hover:text-gray-200'}`} >
|
||||
<ArrowLeftIcon className="mr-3 h-3 w-3" />
|
||||
{t('appLog.table.pagination.previous')}
|
||||
</Pagination.PrevButton>
|
||||
<div className={`flex items-center justify-center flex-grow ${s.pagination}`}>
|
||||
<Pagination.PageButton
|
||||
activeClassName="bg-primary-50 dark:bg-opacity-0 text-primary-600 dark:text-white"
|
||||
className="flex items-center justify-center h-8 w-8 rounded-full cursor-pointer"
|
||||
inactiveClassName="text-gray-500"
|
||||
/>
|
||||
</div>
|
||||
<Pagination.NextButton
|
||||
disabled={currPage === Math.ceil(total / APP_PAGE_LIMIT) - 1}
|
||||
className={`flex items-center mr-2 text-gray-500 focus:outline-none ${currPage === Math.ceil(total / APP_PAGE_LIMIT) - 1 ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:text-gray-600 dark:hover:text-gray-200'}`} >
|
||||
{t('appLog.table.pagination.next')}
|
||||
<ArrowRightIcon className="ml-3 h-3 w-3" />
|
||||
</Pagination.NextButton>
|
||||
</Pagination>
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Logs
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import dayjs from 'dayjs'
|
||||
import { createContext } from 'use-context-selector'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import cn from 'classnames'
|
||||
import s from './style.module.css'
|
||||
import type { WorkflowAppLogDetail, WorkflowLogsResponse } from '@/models/log'
|
||||
import type { App } from '@/types/app'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Drawer from '@/app/components/base/drawer'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
|
||||
type ILogs = {
|
||||
logs?: WorkflowLogsResponse
|
||||
appDetail?: App
|
||||
onRefresh: () => void
|
||||
}
|
||||
|
||||
const defaultValue = 'N/A'
|
||||
|
||||
type IDrawerContext = {
|
||||
onClose: () => void
|
||||
appDetail?: App
|
||||
}
|
||||
|
||||
const DrawerContext = createContext<IDrawerContext>({} as IDrawerContext)
|
||||
|
||||
const WorkflowAppLogList: FC<ILogs> = ({ logs, appDetail, onRefresh }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const media = useBreakpoints()
|
||||
const isMobile = media === MediaType.mobile
|
||||
|
||||
const [showDrawer, setShowDrawer] = useState<boolean>(false)
|
||||
const [currentLog, setCurrentLog] = useState<WorkflowAppLogDetail | undefined>()
|
||||
|
||||
const statusTdRender = (status: string) => {
|
||||
if (status === 'succeeded') {
|
||||
return (
|
||||
<div className='inline-flex items-center gap-1'>
|
||||
<Indicator color={'green'} />
|
||||
<span>Success</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (status === 'failed') {
|
||||
return (
|
||||
<div className='inline-flex items-center gap-1'>
|
||||
<Indicator color={'red'} />
|
||||
<span className='text-red-600'>Fail</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (status === 'stopped') {
|
||||
return (
|
||||
<div className='inline-flex items-center gap-1'>
|
||||
<Indicator color={'gray'} />
|
||||
<span>Stop</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (status === 'running') {
|
||||
return (
|
||||
<div className='inline-flex items-center gap-1'>
|
||||
<Indicator color={'blue'} />
|
||||
<span className='text-primary-600'>Running</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const onCloseDrawer = () => {
|
||||
onRefresh()
|
||||
setShowDrawer(false)
|
||||
setCurrentLog(undefined)
|
||||
}
|
||||
|
||||
if (!logs)
|
||||
return <Loading />
|
||||
|
||||
return (
|
||||
<div className='overflow-x-auto'>
|
||||
<table className={`w-full min-w-[440px] border-collapse border-0 text-sm mt-3 ${s.logTable}`}>
|
||||
<thead className="h-8 !pl-3 py-2 leading-[18px] border-b border-gray-200 text-xs text-gray-500 font-medium">
|
||||
<tr>
|
||||
<td className='w-[1.375rem] whitespace-nowrap'></td>
|
||||
<td className='whitespace-nowrap'>{t('appLog.table.header.startTime')}</td>
|
||||
<td className='whitespace-nowrap'>{t('appLog.table.header.status')}</td>
|
||||
<td className='whitespace-nowrap'>{t('appLog.table.header.runtime')}</td>
|
||||
<td className='whitespace-nowrap'>{t('appLog.table.header.tokens')}</td>
|
||||
<td className='whitespace-nowrap'>{t('appLog.table.header.user')}</td>
|
||||
{/* <td className='whitespace-nowrap'>{t('appLog.table.header.version')}</td> */}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-gray-700 text-[13px]">
|
||||
{logs.data.map((log: WorkflowAppLogDetail) => {
|
||||
const endUser = log.created_by_end_user ? log.created_by_end_user.session_id : defaultValue
|
||||
return <tr
|
||||
key={log.id}
|
||||
className={`border-b border-gray-200 h-8 hover:bg-gray-50 cursor-pointer ${currentLog?.id !== log.id ? '' : 'bg-gray-50'}`}
|
||||
onClick={() => {
|
||||
setShowDrawer(true)
|
||||
setCurrentLog(log)
|
||||
}}>
|
||||
<td className='text-center align-middle'>{!log.read_at && <span className='inline-block bg-[#3F83F8] h-1.5 w-1.5 rounded'></span>}</td>
|
||||
<td className='w-[160px]'>{dayjs.unix(log.created_at).format(t('appLog.dateTimeFormat') as string)}</td>
|
||||
<td>{statusTdRender(log.workflow_run.status)}</td>
|
||||
<td>
|
||||
<div className={cn(
|
||||
log.workflow_run.elapsed_time === 0 && 'text-gray-400',
|
||||
log.workflow_run.elapsed_time > 10 && 'text-orange-400',
|
||||
)}>{`${log.workflow_run.elapsed_time}s`}</div>
|
||||
</td>
|
||||
<td>{log.workflow_run.total_tokens}</td>
|
||||
<td>
|
||||
<div className={cn(endUser === defaultValue ? 'text-gray-400' : 'text-gray-700', 'text-sm overflow-hidden text-ellipsis whitespace-nowrap')}>
|
||||
{endUser}
|
||||
</div>
|
||||
</td>
|
||||
{/* <td>VERSION</td> */}
|
||||
</tr>
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
<Drawer
|
||||
isOpen={showDrawer}
|
||||
onClose={onCloseDrawer}
|
||||
mask={isMobile}
|
||||
footer={null}
|
||||
panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'
|
||||
>
|
||||
<DrawerContext.Provider value={{
|
||||
onClose: onCloseDrawer,
|
||||
appDetail,
|
||||
}}>
|
||||
{<div>TODO</div>}
|
||||
</DrawerContext.Provider>
|
||||
</Drawer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkflowAppLogList
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
.logTable td {
|
||||
padding: 7px 8px;
|
||||
box-sizing: border-box;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
|
|
@ -12,6 +12,12 @@ const translation = {
|
|||
messageCount: 'Message Count',
|
||||
userRate: 'User Rate',
|
||||
adminRate: 'Op. Rate',
|
||||
startTime: 'START TIME',
|
||||
status: 'STATUS',
|
||||
runtime: 'RUN TIME',
|
||||
tokens: 'TOKENS',
|
||||
user: 'END-USER',
|
||||
version: 'VERSION',
|
||||
},
|
||||
pagination: {
|
||||
previous: 'Prev',
|
||||
|
|
@ -64,6 +70,8 @@ const translation = {
|
|||
not_annotated: 'Not Annotated',
|
||||
},
|
||||
},
|
||||
workflowTitle: 'Workflow Logs',
|
||||
workflowSubtitle: 'The log recorded the operation of Automate.',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const translation = {
|
|||
chatbot: 'Chatbot',
|
||||
agent: 'Agent',
|
||||
workflow: 'Workflow',
|
||||
completion: 'Completion',
|
||||
},
|
||||
duplicate: 'Duplicate',
|
||||
duplicateTitle: 'Duplicate App',
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const translation = {
|
|||
chatbot: 'Chatbot',
|
||||
agent: 'Agente',
|
||||
workflow: 'Fluxo de trabalho',
|
||||
completion: 'Gerador de Texto',
|
||||
},
|
||||
duplicate: 'Duplicar',
|
||||
duplicateTitle: 'Duplicate aplicativo',
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const translation = { // Add the Ukrainian translation object
|
|||
chatbot: 'Чатбот',
|
||||
agent: 'Агент',
|
||||
workflow: 'Робочий Процес',
|
||||
completion: 'Автодоповнення',
|
||||
},
|
||||
duplicate: 'Дублювати',
|
||||
duplicateTitle: 'Дублювати додаток',
|
||||
|
|
|
|||
|
|
@ -12,6 +12,12 @@ const translation = {
|
|||
messageCount: '消息数',
|
||||
userRate: '用户反馈',
|
||||
adminRate: '管理员反馈',
|
||||
startTime: '开始时间',
|
||||
status: '状态',
|
||||
runtime: '运行时间',
|
||||
tokens: 'TOKENS',
|
||||
user: '用户',
|
||||
version: '版本',
|
||||
},
|
||||
pagination: {
|
||||
previous: '上一页',
|
||||
|
|
@ -64,6 +70,8 @@ const translation = {
|
|||
not_annotated: '未标注',
|
||||
},
|
||||
},
|
||||
workflowTitle: 'Workflow Logs',
|
||||
workflowSubtitle: 'The log recorded the operation of Automate.',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const translation = {
|
|||
chatbot: '聊天助手',
|
||||
agent: 'Agent',
|
||||
workflow: '工作流',
|
||||
completion: '文本生成',
|
||||
},
|
||||
duplicate: '复制',
|
||||
duplicateTitle: '复制应用',
|
||||
|
|
|
|||
|
|
@ -217,3 +217,50 @@ export type LogMessageAnnotationsResponse = LogMessageFeedbacksResponse
|
|||
export type AnnotationsCountResponse = {
|
||||
count: number
|
||||
}
|
||||
|
||||
export type WorkflowRunDetail = {
|
||||
id: string
|
||||
version: string
|
||||
status: 'running' | 'succeeded' | 'failed' | 'stopped'
|
||||
error?: string
|
||||
elapsed_time: number
|
||||
total_tokens: number
|
||||
total_price: number
|
||||
currency: string
|
||||
total_steps: number
|
||||
finished_at: number
|
||||
}
|
||||
export type AccountInfo = {
|
||||
id: string
|
||||
name: string
|
||||
email: string
|
||||
}
|
||||
export type EndUserInfo = {
|
||||
id: string
|
||||
type: 'browser' | 'service_api'
|
||||
is_anonymous: boolean
|
||||
session_id: string
|
||||
}
|
||||
export type WorkflowAppLogDetail = {
|
||||
id: string
|
||||
workflow_run: WorkflowRunDetail
|
||||
created_from: 'service-api' | 'web-app' | 'explore'
|
||||
created_by_role: 'account' | 'end_user'
|
||||
created_by_account?: AccountInfo
|
||||
created_by_end_user?: EndUserInfo
|
||||
created_at: number
|
||||
read_at?: number
|
||||
}
|
||||
export type WorkflowLogsResponse = {
|
||||
data: Array<WorkflowAppLogDetail>
|
||||
has_more: boolean
|
||||
limit: number
|
||||
total: number
|
||||
page: number
|
||||
}
|
||||
export type WorkflowLogsRequest = {
|
||||
keyword: string
|
||||
status: string
|
||||
page: number
|
||||
limit: number // The default value is 20 and the range is 1-100
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import type {
|
|||
LogMessageAnnotationsResponse,
|
||||
LogMessageFeedbacksRequest,
|
||||
LogMessageFeedbacksResponse,
|
||||
WorkflowLogsRequest,
|
||||
WorkflowLogsResponse,
|
||||
} from '@/models/log'
|
||||
|
||||
export const fetchConversationList: Fetcher<ConversationListResponse, { name: string; appId: string; params?: Record<string, any> }> = ({ appId, params }) => {
|
||||
|
|
@ -57,3 +59,17 @@ export const updateLogMessageAnnotations: Fetcher<LogMessageAnnotationsResponse,
|
|||
export const fetchAnnotationsCount: Fetcher<AnnotationsCountResponse, { url: string }> = ({ url }) => {
|
||||
return get<AnnotationsCountResponse>(url)
|
||||
}
|
||||
|
||||
export const fetchWorkflowLogs: Fetcher<WorkflowLogsResponse, { url: string; params?: WorkflowLogsRequest }> = ({ url, params }) => {
|
||||
return get<WorkflowLogsResponse>(url, { params })
|
||||
}
|
||||
|
||||
// TODO
|
||||
export const fetchFullRunDetail: Fetcher<WorkflowLogsResponse, { url: string; params?: WorkflowLogsRequest }> = ({ url, params }) => {
|
||||
return get<WorkflowLogsResponse>(url, { params })
|
||||
}
|
||||
|
||||
// TODO
|
||||
export const fetchTracingDetail: Fetcher<WorkflowLogsResponse, { url: string; params?: WorkflowLogsRequest }> = ({ url, params }) => {
|
||||
return get<WorkflowLogsResponse>(url, { params })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export type VariableInput = {
|
|||
/**
|
||||
* App modes
|
||||
*/
|
||||
export const AppModes = ['agent', 'chat', 'workflow'] as const
|
||||
export const AppModes = ['advanced-chat', 'agent-chat', 'chat', 'completion', 'workflow'] as const
|
||||
export type AppMode = typeof AppModes[number]
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue