### What problem does this PR solve? Fix: Add agent-log-list page And RAPTOR:Save directly after enabling, incomplete form submission #3221 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)tags/v0.20.0
| import { | |||||
| endOfMonth, | |||||
| endOfYear, | |||||
| format, | |||||
| startOfMonth, | |||||
| startOfYear, | |||||
| subDays, | |||||
| subMonths, | |||||
| subYears, | |||||
| } from 'date-fns'; | |||||
| import { useEffect, useId, useState } from 'react'; | |||||
| import { Calendar, DateRange } from '@/components/originui/calendar'; | |||||
| import { Button } from '@/components/ui/button'; | |||||
| import { | |||||
| Popover, | |||||
| PopoverContent, | |||||
| PopoverTrigger, | |||||
| } from '@/components/ui/popover'; | |||||
| import { cn } from '@/lib/utils'; | |||||
| import { CalendarIcon } from 'lucide-react'; | |||||
| const CalendarComp = ({ | |||||
| selectDateRange, | |||||
| onSelect, | |||||
| ...props | |||||
| }: ITimeRangePickerProps) => { | |||||
| const today = new Date(); | |||||
| const yesterday = { | |||||
| from: subDays(today, 1), | |||||
| to: subDays(today, 1), | |||||
| }; | |||||
| const last7Days = { | |||||
| from: subDays(today, 6), | |||||
| to: today, | |||||
| }; | |||||
| const last30Days = { | |||||
| from: subDays(today, 29), | |||||
| to: today, | |||||
| }; | |||||
| const monthToDate = { | |||||
| from: startOfMonth(today), | |||||
| to: today, | |||||
| }; | |||||
| const lastMonth = { | |||||
| from: startOfMonth(subMonths(today, 1)), | |||||
| to: endOfMonth(subMonths(today, 1)), | |||||
| }; | |||||
| const yearToDate = { | |||||
| from: startOfYear(today), | |||||
| to: today, | |||||
| }; | |||||
| const lastYear = { | |||||
| from: startOfYear(subYears(today, 1)), | |||||
| to: endOfYear(subYears(today, 1)), | |||||
| }; | |||||
| const dateRangeList = [ | |||||
| { key: 'yestday', value: yesterday }, | |||||
| { key: 'last7Days', value: last7Days }, | |||||
| { key: 'last30Days', value: last30Days }, | |||||
| { key: 'monthToDate', value: monthToDate }, | |||||
| { key: 'lastMonth', value: lastMonth }, | |||||
| { key: 'yearToDate', value: yearToDate }, | |||||
| { key: 'lastYear', value: lastYear }, | |||||
| ]; | |||||
| const [month, setMonth] = useState(today); | |||||
| const [date, setDate] = useState<DateRange>(selectDateRange || last7Days); | |||||
| useEffect(() => { | |||||
| onSelect?.(date); | |||||
| }, [date, onSelect]); | |||||
| return ( | |||||
| <div> | |||||
| <div className="rounded-md border"> | |||||
| <div className="flex max-sm:flex-col"> | |||||
| <div className="relative py-4 max-sm:order-1 max-sm:border-t sm:w-32"> | |||||
| <div className="h-full sm:border-e"> | |||||
| <div className="flex flex-col px-2 gap-2"> | |||||
| <Button | |||||
| variant="ghost" | |||||
| size="sm" | |||||
| className="w-full justify-start" | |||||
| onClick={() => { | |||||
| setDate({ | |||||
| from: today, | |||||
| to: today, | |||||
| }); | |||||
| setMonth(today); | |||||
| }} | |||||
| > | |||||
| Today | |||||
| </Button> | |||||
| {dateRangeList.map((dateRange) => ( | |||||
| <Button | |||||
| key={dateRange.key} | |||||
| variant="ghost" | |||||
| size="sm" | |||||
| className="w-full justify-start" | |||||
| onClick={() => { | |||||
| setDate(dateRange.value); | |||||
| setMonth(dateRange.value.to); | |||||
| }} | |||||
| > | |||||
| {dateRange.key} | |||||
| </Button> | |||||
| ))} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <Calendar | |||||
| mode="range" | |||||
| selected={date} | |||||
| onSelect={(newDate) => { | |||||
| if (newDate) { | |||||
| setDate(newDate as DateRange); | |||||
| } | |||||
| }} | |||||
| month={month} | |||||
| onMonthChange={setMonth} | |||||
| className="p-2" | |||||
| {...props} | |||||
| // disabled={[ | |||||
| // { after: today }, // Dates before today | |||||
| // ]} | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }; | |||||
| export type ITimeRangePickerProps = { | |||||
| onSelect: (e: DateRange) => void; | |||||
| selectDateRange: DateRange; | |||||
| className?: string; | |||||
| }; | |||||
| const TimeRangePicker = ({ | |||||
| onSelect, | |||||
| selectDateRange, | |||||
| ...props | |||||
| }: ITimeRangePickerProps) => { | |||||
| const id = useId(); | |||||
| const today = new Date(); | |||||
| const [date, setDate] = useState<DateRange | undefined>( | |||||
| selectDateRange || { from: today, to: today }, | |||||
| ); | |||||
| const onChange = (e: DateRange | undefined) => { | |||||
| if (!e) return; | |||||
| setDate(e); | |||||
| onSelect?.(e); | |||||
| }; | |||||
| return ( | |||||
| <Popover> | |||||
| <PopoverTrigger asChild> | |||||
| <Button | |||||
| id={id} | |||||
| variant="outline" | |||||
| className="group bg-muted-foreground/10 hover:bg-muted-foreground/10 border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px]" | |||||
| > | |||||
| <span className={cn('truncate', !date && 'text-muted-foreground')}> | |||||
| {date?.from ? ( | |||||
| date.to ? ( | |||||
| <> | |||||
| {format(date.from, 'LLL dd, y')} -{' '} | |||||
| {format(date.to, 'LLL dd, y')} | |||||
| </> | |||||
| ) : ( | |||||
| format(date.from, 'LLL dd, y') | |||||
| ) | |||||
| ) : ( | |||||
| 'Pick a date range' | |||||
| )} | |||||
| </span> | |||||
| <CalendarIcon | |||||
| size={16} | |||||
| className="text-muted-foreground/80 group-hover:text-foreground shrink-0 transition-colors" | |||||
| aria-hidden="true" | |||||
| /> | |||||
| </Button> | |||||
| </PopoverTrigger> | |||||
| <PopoverContent className="w-auto p-2" align="start"> | |||||
| <CalendarComp selectDateRange={date} onSelect={onChange} {...props} /> | |||||
| </PopoverContent> | |||||
| </Popover> | |||||
| ); | |||||
| }; | |||||
| export default TimeRangePicker; |
| [navigate], | [navigate], | ||||
| ); | ); | ||||
| const navigateToAgentLogs = useCallback( | |||||
| (id: string) => () => { | |||||
| navigate(`${Routes.AgentLogPage}/${id}`); | |||||
| }, | |||||
| [navigate], | |||||
| ); | |||||
| const navigateToAgentTemplates = useCallback(() => { | const navigateToAgentTemplates = useCallback(() => { | ||||
| navigate(Routes.AgentTemplates); | navigate(Routes.AgentTemplates); | ||||
| }, [navigate]); | }, [navigate]); | ||||
| navigateToChunk, | navigateToChunk, | ||||
| navigateToAgents, | navigateToAgents, | ||||
| navigateToAgent, | navigateToAgent, | ||||
| navigateToAgentLogs, | |||||
| navigateToAgentTemplates, | navigateToAgentTemplates, | ||||
| navigateToSearchList, | navigateToSearchList, | ||||
| navigateToSearch, | navigateToSearch, |
| import { FileUploadProps } from '@/components/file-upload'; | import { FileUploadProps } from '@/components/file-upload'; | ||||
| import message from '@/components/ui/message'; | import message from '@/components/ui/message'; | ||||
| import { AgentGlobals } from '@/constants/agent'; | import { AgentGlobals } from '@/constants/agent'; | ||||
| import { ITraceData } from '@/interfaces/database/agent'; | |||||
| import { | |||||
| IAgentLogsRequest, | |||||
| IAgentLogsResponse, | |||||
| ITraceData, | |||||
| } from '@/interfaces/database/agent'; | |||||
| import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow'; | import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow'; | ||||
| import { IDebugSingleRequestBody } from '@/interfaces/request/agent'; | import { IDebugSingleRequestBody } from '@/interfaces/request/agent'; | ||||
| import i18n from '@/locales/config'; | import i18n from '@/locales/config'; | ||||
| import { BeginId } from '@/pages/agent/constant'; | import { BeginId } from '@/pages/agent/constant'; | ||||
| import { useGetSharedChatSearchParams } from '@/pages/chat/shared-hooks'; | import { useGetSharedChatSearchParams } from '@/pages/chat/shared-hooks'; | ||||
| import agentService, { fetchTrace } from '@/services/agent-service'; | |||||
| import agentService, { | |||||
| fetchAgentLogsByCanvasId, | |||||
| fetchTrace, | |||||
| } from '@/services/agent-service'; | |||||
| import api from '@/utils/api'; | import api from '@/utils/api'; | ||||
| import { buildMessageListWithUuid } from '@/utils/chat'; | import { buildMessageListWithUuid } from '@/utils/chat'; | ||||
| import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; | import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; | ||||
| return { data, loading, refetch }; | return { data, loading, refetch }; | ||||
| }; | }; | ||||
| export const useFetchAgentLog = (searchParams: IAgentLogsRequest) => { | |||||
| const { id } = useParams(); | |||||
| const { data, isFetching: loading } = useQuery<IAgentLogsResponse>({ | |||||
| queryKey: ['fetchAgentLog', id, searchParams], | |||||
| initialData: {} as IAgentLogsResponse, | |||||
| gcTime: 0, | |||||
| queryFn: async () => { | |||||
| console.log('useFetchAgentLog', searchParams); | |||||
| const { data } = await fetchAgentLogsByCanvasId(id as string, { | |||||
| ...searchParams, | |||||
| }); | |||||
| return data?.data ?? []; | |||||
| }, | |||||
| }); | |||||
| return { data, loading }; | |||||
| }; |
| component_id: string; | component_id: string; | ||||
| trace: Array<Record<string, any>>; | trace: Array<Record<string, any>>; | ||||
| } | } | ||||
| export interface IAgentLogResponse { | |||||
| id: string; | |||||
| message: IAgentLogMessage[]; | |||||
| update_date: string; | |||||
| create_date: string; | |||||
| update_time: number; | |||||
| create_time: number; | |||||
| round: number; | |||||
| thumb_up: number; | |||||
| errors: string; | |||||
| source: string; | |||||
| user_id: string; | |||||
| dsl: string; | |||||
| reference: IReference; | |||||
| } | |||||
| export interface IAgentLogsResponse { | |||||
| total: number; | |||||
| sessions: IAgentLogResponse[]; | |||||
| } | |||||
| export interface IAgentLogsRequest { | |||||
| keywords?: string; | |||||
| to_date?: string | Date; | |||||
| from_date?: string | Date; | |||||
| orderby?: string; | |||||
| desc?: boolean; | |||||
| page?: number; | |||||
| page_size?: number; | |||||
| } | |||||
| export interface IAgentLogMessage { | |||||
| content: string; | |||||
| role: 'user' | 'assistant'; | |||||
| id: string; | |||||
| } |
| CirclePlay, | CirclePlay, | ||||
| Download, | Download, | ||||
| History, | History, | ||||
| Key, | |||||
| LaptopMinimalCheck, | LaptopMinimalCheck, | ||||
| Logs, | Logs, | ||||
| ScreenShare, | ScreenShare, | ||||
| useGetBeginNodeDataInputs, | useGetBeginNodeDataInputs, | ||||
| useGetBeginNodeDataQueryIsSafe, | useGetBeginNodeDataQueryIsSafe, | ||||
| } from './hooks/use-get-begin-query'; | } from './hooks/use-get-begin-query'; | ||||
| import { useOpenDocument } from './hooks/use-open-document'; | |||||
| import { | import { | ||||
| useSaveGraph, | useSaveGraph, | ||||
| useSaveGraphBeforeOpeningDebugDrawer, | useSaveGraphBeforeOpeningDebugDrawer, | ||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const { data: userInfo } = useFetchUserInfo(); | const { data: userInfo } = useFetchUserInfo(); | ||||
| const openDocument = useOpenDocument(); | |||||
| // const openDocument = useOpenDocument(); | |||||
| const { | const { | ||||
| handleExportJson, | handleExportJson, | ||||
| handleImportJson, | handleImportJson, | ||||
| const { showEmbedModal, hideEmbedModal, embedVisible, beta } = | const { showEmbedModal, hideEmbedModal, embedVisible, beta } = | ||||
| useShowEmbedModal(); | useShowEmbedModal(); | ||||
| const { navigateToAgentLogs } = useNavigatePage(); | |||||
| const isBeginNodeDataQuerySafe = useGetBeginNodeDataQueryIsSafe(); | const isBeginNodeDataQuerySafe = useGetBeginNodeDataQueryIsSafe(); | ||||
| return ( | return ( | ||||
| <History /> | <History /> | ||||
| {t('flow.historyversion')} | {t('flow.historyversion')} | ||||
| </Button> | </Button> | ||||
| <Button variant={'secondary'}> | |||||
| <Button | |||||
| variant={'secondary'} | |||||
| onClick={navigateToAgentLogs(id as string)} | |||||
| > | |||||
| <Logs /> | <Logs /> | ||||
| {t('flow.log')} | {t('flow.log')} | ||||
| </Button> | </Button> | ||||
| </Button> | </Button> | ||||
| </DropdownMenuTrigger> | </DropdownMenuTrigger> | ||||
| <DropdownMenuContent> | <DropdownMenuContent> | ||||
| <AgentDropdownMenuItem onClick={openDocument}> | |||||
| {/* <AgentDropdownMenuItem onClick={openDocument}> | |||||
| <Key /> | <Key /> | ||||
| API | API | ||||
| </AgentDropdownMenuItem> | |||||
| <DropdownMenuSeparator /> | |||||
| </AgentDropdownMenuItem> */} | |||||
| {/* <DropdownMenuSeparator /> */} | |||||
| <AgentDropdownMenuItem onClick={handleImportJson}> | <AgentDropdownMenuItem onClick={handleImportJson}> | ||||
| <Download /> | <Download /> | ||||
| {t('flow.import')} | {t('flow.import')} |
| import TimeRangePicker from '@/components/originui/time-range-picker'; | |||||
| import { PageHeader } from '@/components/page-header'; | |||||
| import { | |||||
| Breadcrumb, | |||||
| BreadcrumbItem, | |||||
| BreadcrumbLink, | |||||
| BreadcrumbList, | |||||
| BreadcrumbPage, | |||||
| BreadcrumbSeparator, | |||||
| } from '@/components/ui/breadcrumb'; | |||||
| import { SearchInput } from '@/components/ui/input'; | |||||
| import { RAGFlowPagination } from '@/components/ui/ragflow-pagination'; | |||||
| import { Spin } from '@/components/ui/spin'; | |||||
| import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | |||||
| import { useFetchAgentLog } from '@/hooks/use-agent-request'; | |||||
| import { IAgentLogResponse } from '@/interfaces/database/agent'; | |||||
| import React, { useEffect, useState } from 'react'; | |||||
| import { useParams } from 'umi'; | |||||
| import { DateRange } from '../../components/originui/calendar/index'; | |||||
| import { | |||||
| Table, | |||||
| TableBody, | |||||
| TableCell, | |||||
| TableHead, | |||||
| TableHeader, | |||||
| TableRow, | |||||
| } from '../../components/ui/table'; | |||||
| import { useFetchDataOnMount } from '../agent/hooks/use-fetch-data'; | |||||
| const AgentLogPage: React.FC = () => { | |||||
| const { navigateToAgentList, navigateToAgent } = useNavigatePage(); | |||||
| const { flowDetail: agentDetail } = useFetchDataOnMount(); | |||||
| const { id: canvasId } = useParams(); | |||||
| const today = new Date(); | |||||
| const init = { | |||||
| keywords: '', | |||||
| from_date: today, | |||||
| to_date: today, | |||||
| orderby: 'create_time', | |||||
| desc: false, | |||||
| page: 1, | |||||
| page_size: 10, | |||||
| }; | |||||
| const [searchParams, setSearchParams] = useState(init); | |||||
| const columns = [ | |||||
| { | |||||
| title: 'ID', | |||||
| dataIndex: 'id', | |||||
| key: 'id', | |||||
| }, | |||||
| { | |||||
| title: 'Title', | |||||
| dataIndex: 'title', | |||||
| key: 'title', | |||||
| render: (text, record: IAgentLogResponse) => ( | |||||
| <span> | |||||
| {record?.message?.length ? record?.message[0]?.content : ''} | |||||
| </span> | |||||
| ), | |||||
| }, | |||||
| { | |||||
| title: 'State', | |||||
| dataIndex: 'state', | |||||
| key: 'state', | |||||
| render: (text, record: IAgentLogResponse) => ( | |||||
| <div | |||||
| className="size-2 rounded-full" | |||||
| style={{ backgroundColor: record.errors ? 'red' : 'green' }} | |||||
| ></div> | |||||
| ), | |||||
| }, | |||||
| { | |||||
| title: 'Number', | |||||
| dataIndex: 'round', | |||||
| key: 'round', | |||||
| }, | |||||
| { | |||||
| title: 'Latest Date', | |||||
| dataIndex: 'update_date', | |||||
| key: 'update_date', | |||||
| sortable: true, | |||||
| }, | |||||
| { | |||||
| title: 'Create Date', | |||||
| dataIndex: 'create_date', | |||||
| key: 'create_date', | |||||
| sortable: true, | |||||
| }, | |||||
| ]; | |||||
| const { data: logData, loading } = useFetchAgentLog(searchParams); | |||||
| const { sessions: data, total } = logData || {}; | |||||
| const [currentDate, setCurrentDate] = useState<DateRange>({ | |||||
| from: searchParams.from_date, | |||||
| to: searchParams.to_date, | |||||
| }); | |||||
| const [keywords, setKeywords] = useState(searchParams.keywords); | |||||
| const handleDateRangeChange = ({ | |||||
| from: startDate, | |||||
| to: endDate, | |||||
| }: DateRange) => { | |||||
| setCurrentDate({ from: startDate, to: endDate }); | |||||
| }; | |||||
| const [pagination, setPagination] = useState<{ | |||||
| current: number; | |||||
| pageSize: number; | |||||
| total: number; | |||||
| }>({ | |||||
| current: 1, | |||||
| pageSize: 10, | |||||
| total: total, | |||||
| }); | |||||
| useEffect(() => { | |||||
| setPagination((pre) => { | |||||
| return { | |||||
| ...pre, | |||||
| total: total, | |||||
| }; | |||||
| }); | |||||
| }, [total]); | |||||
| const [sortConfig, setSortConfig] = useState<{ | |||||
| orderby: string; | |||||
| desc: boolean; | |||||
| } | null>({ orderby: init.orderby, desc: init.desc ? true : false }); | |||||
| const handlePageChange = (current?: number, pageSize?: number) => { | |||||
| console.log('current', current, 'pageSize', pageSize); | |||||
| setPagination((pre) => { | |||||
| return { | |||||
| ...pre, | |||||
| current: current ?? pre.current, | |||||
| pageSize: pageSize ?? pre.pageSize, | |||||
| }; | |||||
| }); | |||||
| }; | |||||
| const handleSearch = () => { | |||||
| setSearchParams((pre) => { | |||||
| return { | |||||
| ...pre, | |||||
| from_date: currentDate.from as Date, | |||||
| to_date: currentDate.to as Date, | |||||
| page: pagination.current, | |||||
| page_size: pagination.pageSize, | |||||
| orderby: sortConfig?.orderby || '', | |||||
| desc: sortConfig?.desc as boolean, | |||||
| keywords: keywords, | |||||
| }; | |||||
| }); | |||||
| }; | |||||
| useEffect(() => { | |||||
| handleSearch(); | |||||
| }, [pagination.current, pagination.pageSize, sortConfig]); | |||||
| // handle sort | |||||
| const handleSort = (key: string) => { | |||||
| let desc = false; | |||||
| if (sortConfig && sortConfig.orderby === key) { | |||||
| desc = !sortConfig.desc; | |||||
| } | |||||
| setSortConfig({ orderby: key, desc }); | |||||
| }; | |||||
| const handleReset = () => { | |||||
| setSearchParams(init); | |||||
| }; | |||||
| return ( | |||||
| <div className=" text-white"> | |||||
| <PageHeader> | |||||
| <Breadcrumb> | |||||
| <BreadcrumbList> | |||||
| <BreadcrumbItem> | |||||
| <BreadcrumbLink onClick={navigateToAgentList}> | |||||
| Agent | |||||
| </BreadcrumbLink> | |||||
| </BreadcrumbItem> | |||||
| <BreadcrumbSeparator /> | |||||
| <BreadcrumbItem> | |||||
| <BreadcrumbLink onClick={navigateToAgent(canvasId as string)}> | |||||
| {agentDetail.title} | |||||
| </BreadcrumbLink> | |||||
| </BreadcrumbItem> | |||||
| <BreadcrumbSeparator /> | |||||
| <BreadcrumbItem> | |||||
| <BreadcrumbPage>Log</BreadcrumbPage> | |||||
| </BreadcrumbItem> | |||||
| </BreadcrumbList> | |||||
| </Breadcrumb> | |||||
| </PageHeader> | |||||
| <div className="p-4"> | |||||
| <div className="flex justify-between items-center"> | |||||
| <h1 className="text-2xl font-bold mb-4">Log</h1> | |||||
| <div className="flex justify-end space-x-2 mb-4"> | |||||
| <div className="flex items-center space-x-2"> | |||||
| <span>ID/Title</span> | |||||
| <SearchInput | |||||
| value={keywords} | |||||
| onChange={(e) => { | |||||
| setKeywords(e.target.value); | |||||
| }} | |||||
| className="w-32" | |||||
| ></SearchInput> | |||||
| </div> | |||||
| <div className="flex items-center space-x-2"> | |||||
| <span className="whitespace-nowrap">Latest Date</span> | |||||
| <TimeRangePicker | |||||
| onSelect={handleDateRangeChange} | |||||
| selectDateRange={{ from: currentDate.from, to: currentDate.to }} | |||||
| /> | |||||
| </div> | |||||
| <button | |||||
| type="button" | |||||
| className="bg-foreground text-text-title-invert px-4 py-1 rounded" | |||||
| onClick={() => { | |||||
| setPagination({ ...pagination, current: 1 }); | |||||
| handleSearch(); | |||||
| }} | |||||
| > | |||||
| Search | |||||
| </button> | |||||
| <button | |||||
| type="button" | |||||
| className="bg-transparent text-foreground px-4 py-1 rounded border" | |||||
| onClick={() => handleReset()} | |||||
| > | |||||
| Reset | |||||
| </button> | |||||
| </div> | |||||
| </div> | |||||
| <div className="border rounded-md overflow-auto"> | |||||
| {/* <div className="max-h-[500px] overflow-y-auto w-full"> */} | |||||
| <Table rootClassName="max-h-[calc(100vh-200px)]"> | |||||
| <TableHeader className="sticky top-0 bg-background z-10 shadow-sm"> | |||||
| <TableRow> | |||||
| {columns.map((column) => ( | |||||
| <TableHead | |||||
| key={column.dataIndex} | |||||
| onClick={ | |||||
| column.sortable | |||||
| ? () => handleSort(column.dataIndex) | |||||
| : undefined | |||||
| } | |||||
| className={ | |||||
| column.sortable ? 'cursor-pointer hover:bg-muted/50' : '' | |||||
| } | |||||
| > | |||||
| <div className="flex items-center"> | |||||
| {column.title} | |||||
| {column.sortable && | |||||
| sortConfig?.orderby === column.dataIndex && ( | |||||
| <span className="ml-1"> | |||||
| {sortConfig.desc ? '↓' : '↑'} | |||||
| </span> | |||||
| )} | |||||
| </div> | |||||
| </TableHead> | |||||
| ))} | |||||
| </TableRow> | |||||
| </TableHeader> | |||||
| <TableBody> | |||||
| {loading && ( | |||||
| <TableRow> | |||||
| <TableCell | |||||
| colSpan={columns.length} | |||||
| className="h-24 text-center" | |||||
| > | |||||
| <Spin size="large"> | |||||
| <span className="sr-only">Loading...</span> | |||||
| </Spin> | |||||
| </TableCell> | |||||
| </TableRow> | |||||
| )} | |||||
| {!loading && | |||||
| data?.map((item) => ( | |||||
| <TableRow key={item.id}> | |||||
| {columns.map((column) => ( | |||||
| <TableCell key={column.dataIndex}> | |||||
| {column.render | |||||
| ? column.render(item[column.dataIndex], item) | |||||
| : item[column.dataIndex]} | |||||
| </TableCell> | |||||
| ))} | |||||
| </TableRow> | |||||
| ))} | |||||
| {!loading && (!data || data.length === 0) && ( | |||||
| <TableRow> | |||||
| <TableCell | |||||
| colSpan={columns.length} | |||||
| className="h-24 text-center" | |||||
| > | |||||
| No data | |||||
| </TableCell> | |||||
| </TableRow> | |||||
| )} | |||||
| </TableBody> | |||||
| </Table> | |||||
| {/* </div> */} | |||||
| </div> | |||||
| <div className="flex justify-end mt-4 w-full"> | |||||
| <div className="space-x-2"> | |||||
| <RAGFlowPagination | |||||
| {...pagination} | |||||
| total={pagination.total} | |||||
| onChange={(page, pageSize) => { | |||||
| handlePageChange(page, pageSize); | |||||
| }} | |||||
| ></RAGFlowPagination> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }; | |||||
| export default AgentLogPage; |
| let beValid = await form.formControl.trigger(); | let beValid = await form.formControl.trigger(); | ||||
| if (beValid) { | if (beValid) { | ||||
| // setSubmitLoading(true); | // setSubmitLoading(true); | ||||
| let postData = form.formState.values; | |||||
| delete postData['avatar']; // has submitted in first form general | |||||
| saveKnowledgeConfiguration({ | |||||
| ...postData, | |||||
| kb_id, | |||||
| }); | |||||
| // let postData = form.formState.values; | |||||
| // console.log('submit form -->', form); | |||||
| // delete postData['avatar']; // has submitted in first form general | |||||
| form.handleSubmit(async (values) => { | |||||
| console.log('saveKnowledgeConfiguration: ', values); | |||||
| delete values['avatar']; | |||||
| await saveKnowledgeConfiguration({ | |||||
| kb_id, | |||||
| ...values, | |||||
| }); | |||||
| })(); | |||||
| } | } | ||||
| } catch (e) { | } catch (e) { | ||||
| console.log(e); | console.log(e); |
| Result = '/result', | Result = '/result', | ||||
| ResultView = `${Chunk}${Result}`, | ResultView = `${Chunk}${Result}`, | ||||
| KnowledgeGraph = '/knowledge-graph', | KnowledgeGraph = '/knowledge-graph', | ||||
| AgentLogPage = '/agent-log-page', | |||||
| } | } | ||||
| const routes = [ | const routes = [ | ||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| { | |||||
| path: `${Routes.AgentLogPage}/:id`, | |||||
| layout: false, | |||||
| component: `@/pages${Routes.Agents}${Routes.AgentLogPage}`, | |||||
| }, | |||||
| { | { | ||||
| path: `${Routes.Agent}/:id`, | path: `${Routes.Agent}/:id`, | ||||
| layout: false, | layout: false, |
| import { IAgentLogsRequest } from '@/interfaces/database/agent'; | |||||
| import api from '@/utils/api'; | import api from '@/utils/api'; | ||||
| import { registerNextServer } from '@/utils/register-server'; | import { registerNextServer } from '@/utils/register-server'; | ||||
| import request from '@/utils/request'; | import request from '@/utils/request'; | ||||
| fetchVersion, | fetchVersion, | ||||
| fetchCanvas, | fetchCanvas, | ||||
| fetchAgentAvatar, | fetchAgentAvatar, | ||||
| fetchAgentLogs, | |||||
| } = api; | } = api; | ||||
| const methods = { | const methods = { | ||||
| url: fetchAgentAvatar, | url: fetchAgentAvatar, | ||||
| method: 'get', | method: 'get', | ||||
| }, | }, | ||||
| fetchAgentLogs: { | |||||
| url: fetchAgentLogs, | |||||
| method: 'get', | |||||
| }, | |||||
| } as const; | } as const; | ||||
| const agentService = registerNextServer<keyof typeof methods>(methods); | const agentService = registerNextServer<keyof typeof methods>(methods); | ||||
| export const fetchTrace = (data: { canvas_id: string; message_id: string }) => { | export const fetchTrace = (data: { canvas_id: string; message_id: string }) => { | ||||
| return request.get(methods.trace.url, { params: data }); | return request.get(methods.trace.url, { params: data }); | ||||
| }; | }; | ||||
| export const fetchAgentLogsByCanvasId = ( | |||||
| canvasId: string, | |||||
| params: IAgentLogsRequest, | |||||
| ) => { | |||||
| return request.get(methods.fetchAgentLogs.url(canvasId), { params: params }); | |||||
| }; | |||||
| export default agentService; | export default agentService; |
| fetchCanvas: (id: string) => `${api_host}/canvas/get/${id}`, | fetchCanvas: (id: string) => `${api_host}/canvas/get/${id}`, | ||||
| fetchAgentAvatar: (id: string) => `${api_host}/canvas/getsse/${id}`, | fetchAgentAvatar: (id: string) => `${api_host}/canvas/getsse/${id}`, | ||||
| uploadAgentFile: (id?: string) => `${api_host}/canvas/upload/${id}`, | uploadAgentFile: (id?: string) => `${api_host}/canvas/upload/${id}`, | ||||
| fetchAgentLogs: (canvasId: string) => | |||||
| `${api_host}/canvas/${canvasId}/sessions`, | |||||
| // mcp server | // mcp server | ||||
| listMcpServer: `${api_host}/mcp_server/list`, | listMcpServer: `${api_host}/mcp_server/list`, |
| } | } | ||||
| return dayjs(date).format('DD/MM/YYYY'); | return dayjs(date).format('DD/MM/YYYY'); | ||||
| } | } | ||||
| export function formatStandardDate(date: any) { | |||||
| if (!date) { | |||||
| return ''; | |||||
| } | |||||
| const parsedDate = dayjs(date); | |||||
| if (!parsedDate.isValid()) { | |||||
| return ''; | |||||
| } | |||||
| return parsedDate.format('YYYY-MM-DD'); | |||||
| } |