Merge remote-tracking branch 'origin/new-page-framework' into llm_framework

This commit is contained in:
aries_ckt 2023-07-01 21:52:34 +08:00
commit 9c8265d3a4
8 changed files with 435 additions and 172 deletions

View File

@ -1,7 +1,7 @@
"use client";
import { useRequest } from 'ahooks';
import { useState } from 'react';
import { Button, Input, useColorScheme, Box, buttonClasses } from '@/lib/mui';
import { Button, Input, Box, buttonClasses } from '@/lib/mui';
import IconButton from '@mui/joy/IconButton';
import SendRoundedIcon from '@mui/icons-material/SendRounded';
import { zodResolver } from '@hookform/resolvers/zod';
@ -13,7 +13,6 @@ import { useRouter } from 'next/navigation';
export default function Home() {
const Schema = z.object({ query: z.string().min(1) });
const router = useRouter();
const { mode } = useColorScheme();
const [isLoading, setIsLoading] = useState(false);
const methods = useForm<z.infer<typeof Schema>>({
resolver: zodResolver(Schema),
@ -29,7 +28,7 @@ export default function Home() {
chat_mode: 'chat_normal'
});
if (res?.success && res?.data?.conv_uid) {
router.push(`/agents/${res?.data?.conv_uid}?initMessage=${query}`);
router.push(`/chat?id=${res?.data?.conv_uid}&initMessage=${query}`);
}
} catch (err) {
} finally {
@ -67,7 +66,7 @@ export default function Home() {
chat_mode: scene['chat_scene']
});
if (res?.success && res?.data?.conv_uid) {
router.push(`/agents/${res?.data?.conv_uid}?scene=${scene['chat_scene']}`);
router.push(`/chat?id=${res?.data?.conv_uid}&scene=${scene['chat_scene']}`);
}
}}
>

View File

@ -2,14 +2,12 @@
import { useRequest } from 'ahooks';
import { sendGetRequest, sendPostRequest } from '@/utils/request';
import useAgentChat from '@/hooks/useAgentChat';
import ChatBoxComp from '@/components/chatBox';
import ChatBoxComp from '@/components/chatBoxTemp';
import { useDialogueContext } from '@/app/context/dialogue';
const AgentPage = (props: {
params: {
agentId?: string;
},
searchParams: {
id?: string;
scene?: string;
initMessage?: string;
}
@ -17,26 +15,28 @@ const AgentPage = (props: {
const { refreshDialogList } = useDialogueContext();
const { data: historyList } = useRequest(async () => await sendGetRequest('/v1/chat/dialogue/messages/history', {
con_uid: props.params?.agentId
con_uid: props.searchParams?.id
}), {
ready: !!props.params?.agentId
ready: !!props.searchParams?.id,
refreshDeps: [props.searchParams?.id]
});
const { data: paramsList } = useRequest(async () => await sendPostRequest(`/v1/chat/mode/params/list?chat_mode=${props.searchParams?.scene}`), {
ready: !!props.searchParams?.scene
ready: !!props.searchParams?.scene,
refreshDeps: [props.searchParams?.scene]
});
const { history, handleChatSubmit } = useAgentChat({
queryAgentURL: `/v1/chat/completions`,
queryBody: {
conv_uid: props.params?.agentId,
conv_uid: props.searchParams?.id,
chat_mode: props.searchParams?.scene || 'chat_normal',
},
initHistory: historyList?.data
});
return (
<div className='mx-auto flex h-full max-w-3xl flex-col gap-6 px-5 py-6 sm:gap-8 xl:max-w-5xl '>
<>
<ChatBoxComp
initialMessage={historyList?.data ? (historyList?.data?.length <= 0 ? props.searchParams?.initMessage : undefined) : undefined}
clearIntialMessage={async () => {
@ -46,7 +46,7 @@ const AgentPage = (props: {
onSubmit={handleChatSubmit}
paramsList={paramsList?.data}
/>
</div>
</>
)
}

View File

@ -96,168 +96,171 @@ const ChatBoxComp = ({
}, [paramsList]);
return (
<Stack
direction={'column'}
gap={2}
sx={{
display: 'flex',
flex: 1,
flexBasis: '100%',
width: '100%',
height: '100%',
maxHeight: '100%',
minHeight: '100%',
mx: 'auto',
}}
>
<div className='mx-auto flex h-full max-w-3xl flex-col gap-6 px-5 py-6 sm:gap-8 xl:max-w-5xl'>
<Stack
ref={scrollableRef}
direction={'column'}
gap={2}
sx={{
boxSizing: 'border-box',
maxWidth: '100%',
width: '100%',
mx: 'auto',
display: 'flex',
flex: 1,
flexBasis: '100%',
width: '100%',
height: '100%',
maxHeight: '100%',
overflowY: 'auto',
p: 2,
border: '1px solid',
borderColor: 'var(--joy-palette-divider)'
minHeight: '100%',
mx: 'auto',
}}
>
{firstMsg && (
<Card
size="sm"
variant={'outlined'}
color={'primary'}
className="message-agent"
sx={{
mr: 'auto',
ml: 'none',
whiteSpace: 'pre-wrap',
}}
>
{firstMsg?.context}
</Card>
)}
{messages.filter(item => ['view', 'human'].includes(item.role)).map((each, index) => (
<Stack
key={index}
sx={{
mr: each.role === 'view' ? 'auto' : 'none',
ml: each.role === 'human' ? 'auto' : 'none',
}}
>
<Stack
ref={scrollableRef}
direction={'column'}
gap={2}
sx={{
boxSizing: 'border-box',
maxWidth: '100%',
width: '100%',
mx: 'auto',
flex: 1,
maxHeight: '100%',
overflowY: 'auto',
p: 2,
border: '1px solid',
borderColor: 'var(--joy-palette-divider)'
}}
>
{firstMsg && (
<Card
size="sm"
variant={'outlined'}
className={
each.role === 'view' ? 'message-agent' : 'message-human'
}
color={each.role === 'view' ? 'primary' : 'neutral'}
sx={(theme) => ({
px: 2,
'ol, ul': {
my: 0,
pl: 2,
},
ol: {
listStyle: 'numeric',
},
ul: {
listStyle: 'disc',
mb: 2,
},
li: {
my: 1,
},
a: {
textDecoration: 'underline',
},
})}
color={'primary'}
className="message-agent"
sx={{
mr: 'auto',
ml: 'none',
whiteSpace: 'pre-wrap',
}}
>
<Markdown options={options}>
{each.context?.replaceAll('\\n', '\n')}
</Markdown>
{firstMsg?.context}
</Card>
</Stack>
))}
{isLoading && (
<CircularProgress
variant="soft"
color="neutral"
size="sm"
sx={{ mx: 'auto', my: 2 }}
/>
)}
</Stack>
{!readOnly && (
<form
style={{
maxWidth: '100%',
width: '100%',
position: 'relative',
display: 'flex',
marginTop: 'auto',
overflow: 'visible',
background: 'none',
justifyContent: 'center',
marginLeft: 'auto',
marginRight: 'auto',
flexDirection: 'column',
gap: '12px'
}}
onSubmit={(e) => {
e.stopPropagation();
methods.handleSubmit(submit)(e);
}}
>
{(Object.keys(paramsList || {}).length > 0) && (
<div className='flex items-center gap-3'>
<Select
value={currentParam}
onChange={(e, newValue) => {
setCurrentParam(newValue);
}}
className='max-w-xs'
>
{Object.keys(paramsList || {}).map(paramItem => (
<Option
key={paramItem}
value={paramItem}
>
{paramsList?.[paramItem]}
</Option>
))}
</Select>
<Tooltip
className="cursor-pointer"
title={currentParam}
placement="top"
variant="outlined"
>
<InfoOutlinedIcon />
</Tooltip>
</div>
)}
<Input
sx={{ width: '100%' }}
variant="outlined"
endDecorator={
<IconButton type="submit" disabled={isLoading}>
<SendRoundedIcon />
</IconButton>
}
{...methods.register('query')}
/>
</form>
)}
</Stack>
{messages.filter(item => ['view', 'human'].includes(item.role)).map((each, index) => (
<Stack
key={index}
sx={{
mr: each.role === 'view' ? 'auto' : 'none',
ml: each.role === 'human' ? 'auto' : 'none',
}}
>
<Card
size="sm"
variant={'outlined'}
className={
each.role === 'view' ? 'message-agent' : 'message-human'
}
color={each.role === 'view' ? 'primary' : 'neutral'}
sx={(theme) => ({
px: 2,
'ol, ul': {
my: 0,
pl: 2,
},
ol: {
listStyle: 'numeric',
},
ul: {
listStyle: 'disc',
mb: 2,
},
li: {
my: 1,
},
a: {
textDecoration: 'underline',
},
})}
>
<Markdown options={options}>
{each.context?.replaceAll('\\n', '\n')}
</Markdown>
</Card>
</Stack>
))}
{isLoading && (
<CircularProgress
variant="soft"
color="neutral"
size="sm"
sx={{ mx: 'auto', my: 2 }}
/>
)}
</Stack>
{!readOnly && (
<form
style={{
maxWidth: '100%',
width: '100%',
position: 'relative',
display: 'flex',
marginTop: 'auto',
overflow: 'visible',
background: 'none',
justifyContent: 'center',
marginLeft: 'auto',
marginRight: 'auto',
flexDirection: 'column',
gap: '12px'
}}
onSubmit={(e) => {
e.stopPropagation();
methods.handleSubmit(submit)(e);
}}
>
{(Object.keys(paramsList || {}).length > 0) && (
<div className='flex items-center gap-3'>
<Select
value={currentParam}
onChange={(e, newValue) => {
setCurrentParam(newValue);
}}
className='max-w-xs'
>
{Object.keys(paramsList || {}).map(paramItem => (
<Option
key={paramItem}
value={paramItem}
>
{paramsList?.[paramItem]}
</Option>
))}
</Select>
<Tooltip
className="cursor-pointer"
title={currentParam}
placement="top"
variant="outlined"
>
<InfoOutlinedIcon />
</Tooltip>
</div>
)}
<Input
sx={{ width: '100%' }}
variant="outlined"
endDecorator={
<IconButton type="submit" disabled={isLoading}>
<SendRoundedIcon />
</IconButton>
}
{...methods.register('query')}
/>
</form>
)}
</Stack>
</div>
);
}

View File

@ -0,0 +1,252 @@
import { zodResolver } from '@hookform/resolvers/zod';
import SendRoundedIcon from '@mui/icons-material/SendRounded';
import { Card, CircularProgress, IconButton, Input, Stack, Select, Option, Tooltip, Box, useColorScheme } from '@/lib/mui';
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { Message } from '@/types';
import FaceRetouchingNaturalOutlinedIcon from '@mui/icons-material/FaceRetouchingNaturalOutlined';
import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined';
import Markdown from 'markdown-to-jsx';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { okaidia } from 'react-syntax-highlighter/dist/esm/styles/prism';
type Props = {
messages: Message[];
onSubmit: (message: string, otherQueryBody?: any) => Promise<any>;
initialMessage?: string;
readOnly?: boolean;
paramsList?: { [key: string]: string };
clearIntialMessage?: () => void;
};
const Schema = z.object({ query: z.string().min(1) });
const ChatBoxComp = ({
messages,
onSubmit,
initialMessage,
readOnly,
paramsList,
clearIntialMessage
}: Props) => {
const { mode } = useColorScheme();
const scrollableRef = React.useRef<HTMLDivElement>(null);
const [isLoading, setIsLoading] = useState(false);
const [currentParam, setCurrentParam] = useState<string | undefined | null>();
const methods = useForm<z.infer<typeof Schema>>({
resolver: zodResolver(Schema),
defaultValues: {},
});
const submit = async ({ query }: z.infer<typeof Schema>) => {
try {
setIsLoading(true);
methods.reset();
await onSubmit(query, {
select_param: paramsList?.[currentParam]
});
} catch (err) {
} finally {
setIsLoading(false);
}
};
const handleInitMessage = async () => {
try {
const searchParams = new URLSearchParams(window.location.search);
searchParams.delete('initMessage');
window.history.replaceState(null, null, `?${searchParams.toString()}`);
await submit({ query: (initialMessage as string) });
} catch (err) {
console.log(err);
} finally {
clearIntialMessage?.();
}
}
const options = {
overrides: {
code: ({ children }) => (
<SyntaxHighlighter language="javascript" style={okaidia}>
{children}
</SyntaxHighlighter>
),
},
};
React.useEffect(() => {
if (!scrollableRef.current) {
return;
}
scrollableRef.current.scrollTo(0, scrollableRef.current.scrollHeight);
}, [messages?.length]);
React.useEffect(() => {
if (initialMessage && messages.length <= 0) {
handleInitMessage();
}
}, [initialMessage]);
React.useEffect(() => {
if (paramsList && Object.keys(paramsList || {})?.length > 0) {
setCurrentParam(Object.keys(paramsList || {})?.[0]);
}
}, [paramsList]);
return (
<div className='w-full h-full'>
<Stack
className="w-full h-full"
sx={{
background: mode === 'light' ? '#fefefe' : '#212121',
'table': {
borderCollapse: 'collapse',
border: '1px solid #ccc',
},
'th, td': {
border: '1px solid #ccc',
padding: '10px',
textAlign: 'center'
},
}}
>
<Stack
ref={scrollableRef}
direction={'column'}
sx={{
overflowY: 'auto',
maxHeight: '100%',
flex: 1
}}
>
{messages.filter(item => ['view', 'human'].includes(item.role))?.map((each, index) => {
return (
<Stack
key={index}
>
<Card
size="sm"
variant={'outlined'}
color={each.role === 'view' ? 'primary' : 'neutral'}
sx={(theme) => ({
background: each.role === 'view' ? 'var(--joy-palette-primary-softBg, var(--joy-palette-primary-100, #DDF1FF))': 'unset',
border: 'unset',
borderRadius: 'unset',
padding: '24px 0 26px 0',
lineHeight: '24px',
})}
>
<Box sx={{ width: '76%', margin: '0 auto' }} className="flex flex-row">
<div className='mr-3 inline'>
{each.role === 'view' ? (
<SmartToyOutlinedIcon />
) : (
<FaceRetouchingNaturalOutlinedIcon />
)}
</div>
<div className='inline align-middle mt-0.5'>
<Markdown options={options}>
{each.context?.replaceAll('\\n', '\n')}
</Markdown>
</div>
</Box>
</Card>
</Stack>
)
})}
{isLoading && (
<CircularProgress
variant="soft"
color="neutral"
size="sm"
sx={{ mx: 'auto', my: 2 }}
/>
)}
</Stack>
{!readOnly && (
<Box
sx={{
position: 'relative',
background: mode === 'light' ? '#fefefe' : '#212121',
'&::before': {
content: '" "',
position: 'absolute',
top: '-18px',
left: '0',
right: '0',
width: '100%',
margin: '0 auto',
height: '20px',
background: mode === 'light' ? '#fefefe' : '#212121',
filter: 'blur(10px)',
zIndex: 2,
}
}}
>
<form
style={{
maxWidth: '100%',
width: '76%',
position: 'relative',
display: 'flex',
marginTop: 'auto',
overflow: 'visible',
background: 'none',
justifyContent: 'center',
marginLeft: 'auto',
marginRight: 'auto',
flexDirection: 'column',
gap: '12px',
paddingBottom: '58px',
paddingTop: '20px'
}}
onSubmit={(e) => {
e.stopPropagation();
methods.handleSubmit(submit)(e);
}}
>
{(Object.keys(paramsList || {}).length > 0) && (
<div className='flex items-center gap-3'>
<Select
value={currentParam}
onChange={(e, newValue) => {
setCurrentParam(newValue);
}}
sx={{ maxWidth: '100%' }}
>
{Object.keys(paramsList || {}).map(paramItem => (
<Option
key={paramItem}
value={paramItem}
>
{paramItem}
</Option>
))}
</Select>
</div>
)}
<Input
className='w-full h-12'
variant="outlined"
endDecorator={
<IconButton type="submit" disabled={isLoading}>
<SendRoundedIcon />
</IconButton>
}
{...methods.register('query')}
/>
</form>
</Box>
)}
</Stack>
</div>
);
}
export default ChatBoxComp;

View File

@ -1,6 +1,6 @@
"use client";
import React, { useEffect, useMemo } from 'react';
import { usePathname, useRouter } from 'next/navigation';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import Link from 'next/link';
import { Modal } from 'antd';
import { Box, List, ListItem, ListItemButton, ListItemDecorator, ListItemContent, Typography, Button, useColorScheme, IconButton } from '@/lib/mui';
@ -15,6 +15,7 @@ import { sendPostRequest } from '@/utils/request';
const LeftSider = () => {
const pathname = usePathname();
const searchParams = useSearchParams();
const router = useRouter();
const { dialogueList, queryDialogueList, refreshDialogList } = useDialogueContext();
const { mode, setMode } = useColorScheme();
@ -44,7 +45,7 @@ const LeftSider = () => {
return (
<>
<nav className='flex h-12 items-center justify-between border-b bg-gray-50 px-4 dark:border-gray-800 dark:bg-gray-800/70 md:hidden'>
<nav className='flex h-12 items-center justify-between border-b px-4 dark:border-gray-800 dark:bg-gray-800/70 md:hidden'>
<div>
<MenuIcon />
</div>
@ -114,7 +115,7 @@ const LeftSider = () => {
}}
>
{dialogueList?.data?.map((each) => {
const isSelect = pathname === `/agents/${each.conv_uid}`;
const isSelect = pathname === `/chat` && searchParams.get('id') === each.conv_uid;
return (
<ListItem key={each.conv_uid}>
<ListItemButton
@ -127,7 +128,7 @@ const LeftSider = () => {
}}
>
<ListItemContent>
<Link href={`/agents/${each.conv_uid}?scene=${each?.chat_mode}`} className="flex items-center justify-between">
<Link href={`/chat?id=${each.conv_uid}&scene=${each?.chat_mode}`} className="flex items-center justify-between">
<Typography fontSize={14} noWrap={true}>
<SmsOutlinedIcon className='mr-2' />
{each?.user_name || each?.user_input || 'undefined'}
@ -147,7 +148,7 @@ const LeftSider = () => {
async onOk() {
await sendPostRequest(`v1/chat/dialogue/delete?con_uid=${each.conv_uid}`);
await refreshDialogList();
if (pathname === `/agents/${each.conv_uid}`) {
if (pathname === `/chat` && searchParams.get('id') === each.conv_uid) {
router.push('/');
}
}

View File

@ -25,26 +25,34 @@ export const joyTheme = extendTheme({
surface: '#fff'
},
text: {
primary: '#505050'
primary: '#505050',
},
},
},
dark: {
palette: {
mode: 'light',
primary: {
...colors.grey,
softBg: '#353539',
softHoverBg: '#35353978',
softDisabledBg: '#353539'
},
neutral: {
plainColor: '#D8D8DF',
plainHoverColor: '#F7F7F8',
plainHoverBg: '#25252D',
plainHoverBg: '#353539',
plainActiveBg: '#434356',
plainDisabledColor: '#434356'
plainDisabledColor: '#434356',
outlinedBorder: '#353539',
outlinedHoverBorder: '#454651'
},
text: {
primary: '#EBEBEF'
},
background: {
body: '#0f172a',
surface: '#1e293b40'
body: '#212121',
surface: '#525262',
}
},
},

View File

@ -8,7 +8,7 @@ const nextConfig = {
ignoreBuildErrors: true
},
env: {
API_BASE_URL: process.env.API_BASE_URL || 'http://30.183.154.76:5000'
API_BASE_URL: process.env.API_BASE_URL || 'https://u158074-879a-d00019a9.westa.seetacloud.com:8443'
}
}