mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-08-09 20:28:07 +00:00
Merge branch 'new-page-framework' into dev_ty_06_end
This commit is contained in:
commit
16655e5f13
@ -11,14 +11,27 @@ const AgentPage = (props) => {
|
||||
ready: !!props.params?.agentId
|
||||
});
|
||||
|
||||
const { handleChatSubmit, history } = useAgentChat({
|
||||
queryAgentURL: `/v1/chat/completions`,
|
||||
queryBody: {}
|
||||
const { history, handleChatSubmit } = useAgentChat({
|
||||
queryAgentURL: `http://30.183.154.8:5000/v1/chat/completions`,
|
||||
queryBody: {
|
||||
conv_uid: props.params?.agentId,
|
||||
chat_mode: 'chat_normal'
|
||||
},
|
||||
initHistory: historyList?.data
|
||||
});
|
||||
|
||||
return (
|
||||
<div className='mx-auto flex h-full max-w-3xl flex-col gap-6 px-5 pt-6 sm:gap-8 xl:max-w-4xl'>
|
||||
<ChatBoxComp messages={historyList?.data || []} onSubmit={handleChatSubmit}/>
|
||||
<ChatBoxComp
|
||||
initialMessage={historyList?.data ? (historyList?.data?.length <= 0 ? props.searchParams?.initMessage : undefined) : undefined}
|
||||
clearIntialMessage={() => {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.delete('initMessage');
|
||||
window.history.replaceState(null, null, `?${searchParams.toString()}`);
|
||||
}}
|
||||
messages={history || []}
|
||||
onSubmit={handleChatSubmit}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
Box,
|
||||
Stack,
|
||||
Input,
|
||||
Textarea,
|
||||
Chip,
|
||||
styled
|
||||
} from '@/lib/mui'
|
||||
@ -61,6 +62,8 @@ const Documents = () => {
|
||||
const [documents, setDocuments] = useState<any>([])
|
||||
const [webPageUrl, setWebPageUrl] = useState<string>('')
|
||||
const [documentName, setDocumentName] = useState<any>('')
|
||||
const [textSource, setTextSource] = useState<string>('');
|
||||
const [text, setText] = useState<string>('');
|
||||
const [originFileObj, setOriginFileObj] = useState<any>(null)
|
||||
const props: UploadProps = {
|
||||
name: 'file',
|
||||
@ -264,7 +267,6 @@ const Documents = () => {
|
||||
onChange={(e: any) => setDocumentName(e.target.value)}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
Web Page URL:
|
||||
{documentType === 'webPage' ? (
|
||||
<>
|
||||
Web Page URL:
|
||||
@ -293,7 +295,20 @@ const Documents = () => {
|
||||
</Dragger>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
<>
|
||||
Text Source(Optional):
|
||||
<Input
|
||||
placeholder="Please input the text source"
|
||||
onChange={(e: any) => setTextSource(e.target.value)}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
Text:
|
||||
<Textarea
|
||||
onChange={(e: any) => setText(e.target.value)}
|
||||
minRows={4}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
<Button
|
||||
@ -379,6 +394,47 @@ const Documents = () => {
|
||||
} else {
|
||||
message.error(data.err_msg || 'failed')
|
||||
}
|
||||
} else {
|
||||
if (text === '') {
|
||||
message.error('Please input the text')
|
||||
return
|
||||
}
|
||||
const res = await fetch(
|
||||
`http://localhost:8000/knowledge/${spaceName}/document/add`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
doc_name: documentName,
|
||||
source: textSource,
|
||||
content: text,
|
||||
doc_type: 'TEXT'
|
||||
})
|
||||
}
|
||||
)
|
||||
const data = await res.json()
|
||||
if (data.success) {
|
||||
message.success('success')
|
||||
setIsAddDocumentModalShow(false)
|
||||
const res = await fetch(
|
||||
`http://localhost:8000/knowledge/${spaceName}/document/list`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({})
|
||||
}
|
||||
)
|
||||
const data = await res.json()
|
||||
if (data.success) {
|
||||
setDocuments(data.data)
|
||||
}
|
||||
} else {
|
||||
message.error(data.err_msg || 'failed')
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
Stack,
|
||||
Box,
|
||||
Input,
|
||||
Textarea,
|
||||
styled
|
||||
} from '@/lib/mui'
|
||||
|
||||
@ -62,6 +63,8 @@ const Index = () => {
|
||||
const [knowledgeSpaceName, setKnowledgeSpaceName] = useState<string>('')
|
||||
const [webPageUrl, setWebPageUrl] = useState<string>('')
|
||||
const [documentName, setDocumentName] = useState<any>('')
|
||||
const [textSource, setTextSource] = useState<string>('');
|
||||
const [text, setText] = useState<string>('');
|
||||
const [originFileObj, setOriginFileObj] = useState<any>(null)
|
||||
const props: UploadProps = {
|
||||
name: 'file',
|
||||
@ -304,7 +307,20 @@ const Index = () => {
|
||||
</Dragger>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
<>
|
||||
Text Source(Optional):
|
||||
<Input
|
||||
placeholder="Please input the text source"
|
||||
onChange={(e: any) => setTextSource(e.target.value)}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
Text:
|
||||
<Textarea
|
||||
onChange={(e: any) => setText(e.target.value)}
|
||||
minRows={4}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
<Button
|
||||
@ -362,6 +378,33 @@ const Index = () => {
|
||||
} else {
|
||||
message.error(data.err_msg || 'failed')
|
||||
}
|
||||
} else {
|
||||
if (text === '') {
|
||||
message.error('Please input the text')
|
||||
return
|
||||
}
|
||||
const res = await fetch(
|
||||
`http://localhost:8000/knowledge/${knowledgeSpaceName}/document/add`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
doc_name: documentName,
|
||||
source: textSource,
|
||||
content: text,
|
||||
doc_type: 'TEXT'
|
||||
})
|
||||
}
|
||||
)
|
||||
const data = await res.json()
|
||||
if (data.success) {
|
||||
message.success('success')
|
||||
setIsAddKnowledgeSpaceModalShow(false)
|
||||
} else {
|
||||
message.error(data.err_msg || 'failed')
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@ -5,12 +5,12 @@ import LeftSider from '@/components/leftSider';
|
||||
import { CssVarsProvider, ThemeProvider } from '@mui/joy/styles';
|
||||
import { joyTheme } from './defaultTheme';
|
||||
import TopProgressBar from '@/components/topProgressBar';
|
||||
|
||||
export default function RootLayout({
|
||||
function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
|
||||
return (
|
||||
<html lang="en" className="h-full font-sans">
|
||||
<body className={`h-full font-sans`}>
|
||||
@ -31,3 +31,5 @@ export default function RootLayout({
|
||||
</html>
|
||||
)
|
||||
}
|
||||
|
||||
export default RootLayout;
|
@ -28,8 +28,7 @@ export default function Home() {
|
||||
chat_mode: 'chat_normal'
|
||||
});
|
||||
if (res?.success && res?.data?.conv_uid) {
|
||||
// router.push(`/agents/${res?.data?.conv_uid}?newMessage=${query}`);
|
||||
// await refreshDialogList();
|
||||
router.push(`/agents/${res?.data?.conv_uid}?initMessage=${query}`);
|
||||
}
|
||||
} catch (err) {
|
||||
} finally {
|
||||
|
@ -17,8 +17,9 @@ type Props = {
|
||||
messages: Message[];
|
||||
onSubmit: (message: string) => Promise<any>;
|
||||
messageTemplates?: string[];
|
||||
initialMessage?: Message;
|
||||
initialMessage?: string;
|
||||
readOnly?: boolean;
|
||||
clearIntialMessage?: () => void;
|
||||
};
|
||||
|
||||
const Schema = z.object({ query: z.string().min(1) });
|
||||
@ -29,6 +30,7 @@ const ChatBoxComp = ({
|
||||
messageTemplates,
|
||||
initialMessage,
|
||||
readOnly,
|
||||
clearIntialMessage
|
||||
}: Props) => {
|
||||
const scrollableRef = React.useRef<HTMLDivElement>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@ -52,6 +54,11 @@ const ChatBoxComp = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleInitMessage = async () => {
|
||||
await submit({ query: (initialMessage as string) });
|
||||
clearIntialMessage?.();
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!scrollableRef.current) {
|
||||
return;
|
||||
@ -61,11 +68,9 @@ const ChatBoxComp = ({
|
||||
}, [messages?.length]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setFirstMsg(
|
||||
initialMessage ? initialMessage : undefined
|
||||
);
|
||||
}, 0);
|
||||
if (initialMessage && messages.length <= 0) {
|
||||
handleInitMessage();
|
||||
}
|
||||
}, [initialMessage]);
|
||||
|
||||
return (
|
||||
@ -116,7 +121,7 @@ const ChatBoxComp = ({
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{messages.map((each, index) => (
|
||||
{messages.filter(item => ['ai', 'human'].includes(item.role)).map((each, index) => (
|
||||
<Stack
|
||||
key={index}
|
||||
sx={{
|
||||
|
@ -1,14 +1,13 @@
|
||||
"use client";
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { usePathname, useSearchParams } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { Box, List, ListItem, ListItemButton, ListItemDecorator, ListItemContent, Typography, Button, useColorScheme, Alert } from '@/lib/mui';
|
||||
import { Box, List, ListItem, ListItemButton, ListItemDecorator, ListItemContent, Typography, Button, useColorScheme } from '@/lib/mui';
|
||||
import SmartToyRoundedIcon from '@mui/icons-material/SmartToyRounded'; // Icons import
|
||||
import StorageRoundedIcon from '@mui/icons-material/StorageRounded';
|
||||
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||
import WbSunnyIcon from '@mui/icons-material/WbSunny';
|
||||
import HomeIcon from '@mui/icons-material/Home';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import { useQueryDialog } from '@/hooks/useQueryDialogue';
|
||||
@ -16,16 +15,10 @@ import { useQueryDialog } from '@/hooks/useQueryDialogue';
|
||||
const LeftSider = () => {
|
||||
const pathname = usePathname();
|
||||
const { mode, setMode } = useColorScheme();
|
||||
const [chatSelect, setChatSelect] = useState();
|
||||
const { dialogueList } = useQueryDialog();
|
||||
|
||||
const menus = useMemo(() => {
|
||||
return [{
|
||||
label: 'Home',
|
||||
icon: <HomeIcon fontSize="small" />,
|
||||
route: '/',
|
||||
active: pathname === '/',
|
||||
}, {
|
||||
label: 'Agents',
|
||||
icon: <SmartToyRoundedIcon fontSize="small" />,
|
||||
route: '/agents',
|
||||
@ -53,7 +46,7 @@ const LeftSider = () => {
|
||||
<MenuIcon />
|
||||
</div>
|
||||
<span className='truncate px-4'>New Chat</span>
|
||||
<a href='javascript: void(0)' className='-mr-3 flex h-9 w-9 shrink-0 items-center justify-center'>
|
||||
<a href='' className='-mr-3 flex h-9 w-9 shrink-0 items-center justify-center'>
|
||||
<AddIcon />
|
||||
</a>
|
||||
</nav>
|
||||
@ -99,7 +92,9 @@ const LeftSider = () => {
|
||||
px: 2
|
||||
}}
|
||||
>
|
||||
<Button variant="outlined" color="primary" className='w-full'>+ 新建对话</Button>
|
||||
<Link href={`/`}>
|
||||
<Button variant="outlined" color="primary" className='w-full'>+ 新建对话</Button>
|
||||
</Link>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
@ -121,26 +116,25 @@ const LeftSider = () => {
|
||||
'& .JoyListItemButton-root': { p: '8px' },
|
||||
}}
|
||||
>
|
||||
{dialogueList?.data?.map((each) => (
|
||||
<ListItem key={each.conv_uid}>
|
||||
<ListItemButton
|
||||
selected={chatSelect === each.conv_uid}
|
||||
variant={chatSelect === each.conv_uid ? 'soft' : 'plain'}
|
||||
onClick={() => {
|
||||
setChatSelect(each.conv_uid);
|
||||
}}
|
||||
>
|
||||
<ListItemContent>
|
||||
<Link href={`/agents/${each.conv_uid}`}>
|
||||
<Typography fontSize={14} noWrap={true}>
|
||||
{each?.user_name || each?.user_input || 'undefined'}
|
||||
</Typography>
|
||||
</Link>
|
||||
|
||||
</ListItemContent>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
{dialogueList?.data?.map((each) => {
|
||||
const isSelect = pathname === `/agents/${each.conv_uid}`;
|
||||
return (
|
||||
<ListItem key={each.conv_uid}>
|
||||
<ListItemButton
|
||||
selected={isSelect}
|
||||
variant={isSelect ? 'soft' : 'plain'}
|
||||
>
|
||||
<ListItemContent>
|
||||
<Link href={`/agents/${each.conv_uid}`}>
|
||||
<Typography fontSize={14} noWrap={true}>
|
||||
{each?.user_name || each?.user_input || 'undefined'}
|
||||
</Typography>
|
||||
</Link>
|
||||
</ListItemContent>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
</ListItem>
|
||||
</List>
|
||||
@ -178,7 +172,6 @@ const LeftSider = () => {
|
||||
color="neutral"
|
||||
selected={each.active}
|
||||
variant={each.active ? 'soft' : 'plain'}
|
||||
onClick={() => { setChatSelect(undefined); }}
|
||||
>
|
||||
<ListItemDecorator
|
||||
sx={{
|
||||
|
@ -2,35 +2,31 @@ import {
|
||||
EventStreamContentType,
|
||||
fetchEventSource,
|
||||
} from '@microsoft/fetch-event-source';
|
||||
import { ApiError, ApiErrorType } from '@/utils/api-error';
|
||||
import useStateReducer from './useStateReducer';
|
||||
import useVisitorId from './useVisitorId';
|
||||
|
||||
import { Message } from '@/types';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
type Props = {
|
||||
queryAgentURL: string;
|
||||
queryHistoryURL?: string;
|
||||
channel?: "dashboard" | "website" | "slack" | "crisp";
|
||||
queryBody?: any;
|
||||
initHistory: Message[];
|
||||
};
|
||||
|
||||
const useAgentChat = ({
|
||||
queryAgentURL,
|
||||
queryHistoryURL,
|
||||
channel,
|
||||
queryBody,
|
||||
initHistory
|
||||
}: Props) => {
|
||||
const [state, setState] = useStateReducer({
|
||||
history: [{
|
||||
role: 'human',
|
||||
context: 'hello',
|
||||
}, {
|
||||
role: 'agent',
|
||||
context: 'Hello! How can I assist you today?',
|
||||
}] as { role: 'human' | 'agent'; context: string; id?: string }[],
|
||||
history: (initHistory || []) as { role: 'human' | 'ai'; context: string; id?: string }[],
|
||||
});
|
||||
|
||||
const { visitorId } = useVisitorId();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (initHistory) setState({ history: initHistory });
|
||||
}, [initHistory]);
|
||||
|
||||
const handleChatSubmit = async (context: string) => {
|
||||
if (!context) {
|
||||
return;
|
||||
@ -50,9 +46,6 @@ import {
|
||||
const ctrl = new AbortController();
|
||||
let buffer = '';
|
||||
|
||||
class RetriableError extends Error {}
|
||||
class FatalError extends Error {}
|
||||
|
||||
await fetchEventSource(queryAgentURL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@ -60,9 +53,7 @@ import {
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...queryBody,
|
||||
streaming: true,
|
||||
query: context,
|
||||
visitorId: visitorId,
|
||||
user_input: context,
|
||||
channel,
|
||||
}),
|
||||
signal: ctrl.signal,
|
||||
@ -79,64 +70,59 @@ import {
|
||||
response.status !== 429
|
||||
) {
|
||||
if (response.status === 402) {
|
||||
throw new ApiError(ApiErrorType.USAGE_LIMIT);
|
||||
//throw new ApiError(ApiErrorType.USAGE_LIMIT);
|
||||
}
|
||||
throw new FatalError();
|
||||
// client-side errors are usually non-retriable:
|
||||
//throw new FatalError();
|
||||
} else {
|
||||
throw new RetriableError();
|
||||
//throw new RetriableError();
|
||||
}
|
||||
},
|
||||
onclose() {
|
||||
throw new RetriableError();
|
||||
// if the server closes the connection unexpectedly, retry:
|
||||
console.log('onclose');
|
||||
//throw new RetriableError();
|
||||
},
|
||||
onerror(err) {
|
||||
throw new Error(err);
|
||||
// if (err instanceof FatalError) {
|
||||
// ctrl.abort();
|
||||
// throw new Error(); // rethrow to stop the operation
|
||||
// } else if (err instanceof ApiError) {
|
||||
// console.log('ApiError', ApiError);
|
||||
// throw new Error();
|
||||
// } else {
|
||||
// throw new Error(err);
|
||||
// }
|
||||
},
|
||||
onmessage: (event) => {
|
||||
event.data = event.data.replaceAll('\\n', '\n');
|
||||
console.log(event, 'event');
|
||||
if (event.data === '[DONE]') {
|
||||
ctrl.abort();
|
||||
} else if (event.data?.startsWith('[ERROR]')) {
|
||||
ctrl.abort();
|
||||
|
||||
setState({
|
||||
history: [
|
||||
...history,
|
||||
{
|
||||
role: 'agent',
|
||||
role: 'ai',
|
||||
context: event.data.replace('[ERROR]', ''),
|
||||
} as any,
|
||||
],
|
||||
});
|
||||
} else {
|
||||
buffer += decodeURIComponent(event.data) as string;
|
||||
const h = [...history];
|
||||
if (h?.[nextIndex]) {
|
||||
h[nextIndex].context = `${buffer}`;
|
||||
} else {
|
||||
h.push({ role: 'agent', context: buffer });
|
||||
if (event.data) {
|
||||
if (h?.[nextIndex]) {
|
||||
h[nextIndex].context = `${event.data}`;
|
||||
} else {
|
||||
h.push({ role: 'ai', context: event.data });
|
||||
}
|
||||
setState({
|
||||
history: h as any,
|
||||
});
|
||||
}
|
||||
setState({
|
||||
history: h as any,
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.log('err', err);
|
||||
setState({
|
||||
history: [
|
||||
...history,
|
||||
{ role: 'agent', context: answer || '请求出错' as string },
|
||||
{ role: 'ai', context: answer || '请求出错' as string },
|
||||
] as any,
|
||||
});
|
||||
// if (err instanceof ApiError) {
|
||||
@ -156,13 +142,12 @@ import {
|
||||
// setState({
|
||||
// history: [
|
||||
// ...history,
|
||||
// { from: 'agent', message: answer as string },
|
||||
// { from: 'ai', message: answer as string },
|
||||
// ] as any,
|
||||
// });
|
||||
// }
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
handleChatSubmit,
|
||||
history: state.history,
|
||||
|
10
datacenter/hooks/useNewChat.ts
Normal file
10
datacenter/hooks/useNewChat.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
export const useNewChat = () => {
|
||||
const [message, setMessage] = useState<string | undefined>("hello");
|
||||
|
||||
return {
|
||||
message,
|
||||
setMessage
|
||||
};
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import axios from 'axios';
|
||||
import { isPlainObject } from 'lodash';
|
||||
|
||||
axios.defaults.baseURL = 'http://30.183.153.244:5000';
|
||||
axios.defaults.baseURL = 'http://30.183.154.8:5000';
|
||||
|
||||
axios.defaults.timeout = 10000;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user