Commit 907203ba authored by jaden's avatar jaden

fix: fix bug: cancel、input text、check

parent 1118e560
...@@ -34,7 +34,7 @@ type message = { ...@@ -34,7 +34,7 @@ type message = {
export default class OpenAI { export default class OpenAI {
static async request( static async request(
messages: message[], messages: message[],
onFinish: (responseText: string) => any, onFinish: (responseText: string, cancel?: boolean) => any,
onUpdate: (responseText: string, delta: string) => any, onUpdate: (responseText: string, delta: string) => any,
onError: (e: Error) => any, onError: (e: Error) => any,
functions: any[], functions: any[],
...@@ -65,8 +65,12 @@ export default class OpenAI { ...@@ -65,8 +65,12 @@ export default class OpenAI {
}, },
}; };
const requestTimeoutId = setTimeout(() => controller.abort(), 1000 * 120); const requestTimeoutId = setTimeout(() => controller.abort(), 1000 * 120);
const chatPath = '/openai/v1/chat/completions'; const chatPath = '/openai/v1/chat/completions';
if (closeFn) { if (closeFn) {
controller.signal.onabort = () => {
onFinish('', true);
};
closeFn.current = () => { closeFn.current = () => {
clearTimeout(requestTimeoutId); clearTimeout(requestTimeoutId);
controller.abort(); controller.abort();
...@@ -75,13 +79,15 @@ export default class OpenAI { ...@@ -75,13 +79,15 @@ export default class OpenAI {
if (stream) { if (stream) {
let responseText = ''; let responseText = '';
let finished = false; let finished = false;
const finish = () => { const finish = (cancel?: boolean) => {
if (!finished) { if (!finished) {
onFinish(responseText); onFinish(responseText, cancel);
finished = true; finished = true;
} }
}; };
controller.signal.onabort = finish; controller.signal.onabort = () => {
finish(true);
};
fetchEventSource(chatPath, { fetchEventSource(chatPath, {
...chatPayload, ...chatPayload,
async onopen(res) { async onopen(res) {
...@@ -152,7 +158,6 @@ export default class OpenAI { ...@@ -152,7 +158,6 @@ export default class OpenAI {
const message = const message =
resJson.choices?.at(0)?.message?.content ?? resJson.choices?.at(0)?.message?.content ??
(JSON.stringify(get(resJson, 'choices[0].message.function_call')) || ''); (JSON.stringify(get(resJson, 'choices[0].message.function_call')) || '');
console.log(message);
onFinish(message); onFinish(message);
} }
} }
......
...@@ -9,6 +9,8 @@ import { ...@@ -9,6 +9,8 @@ import {
Spin, Spin,
Notification, Notification,
AutoComplete, AutoComplete,
Alert,
Switch,
} from '@arco-design/web-react'; } from '@arco-design/web-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import MessageItem from './MessageItem'; import MessageItem from './MessageItem';
...@@ -16,6 +18,7 @@ import MessageItem from './MessageItem'; ...@@ -16,6 +18,7 @@ import MessageItem from './MessageItem';
import { debounce, filter, get, map } from 'lodash'; import { debounce, filter, get, map } from 'lodash';
import React, { import React, {
ReactElement, ReactElement,
ReactNode,
useCallback, useCallback,
useEffect, useEffect,
useMemo, useMemo,
...@@ -84,7 +87,9 @@ export function AIWrapper({ ...@@ -84,7 +87,9 @@ export function AIWrapper({
// 发送信息事件和成功的事件 // 发送信息事件和成功的事件
const [currentAssistantMessage, setCurrentAssistantMessage] = useState(''); const [currentAssistantMessage, setCurrentAssistantMessage] = useState('');
const [tinking, setTinking] = useState(false); //假的加载 const [thinking, setThinking] = useState(false); //假的加载
const [check, setCheck] = useState<string | boolean>(false); // 检查需求
const [checkOpen, setIsCheck] = useState(isCheck);
const handleButtonClickSuccess = useEffect(() => { const handleButtonClickSuccess = useEffect(() => {
if (!currentAssistantMessage || loading) { if (!currentAssistantMessage || loading) {
return; return;
...@@ -104,11 +109,9 @@ export function AIWrapper({ ...@@ -104,11 +109,9 @@ export function AIWrapper({
const closeRef = useRef<any>(); const closeRef = useRef<any>();
const close = () => { const close = () => {
closeRef.current && closeRef.current(); if (closeRef.current) {
// setCurrentAssistantMessage(''); closeRef.current();
// requestAnimationFrame(() => { }
// setLoading(false);
// });
}; };
const toView = useCallback( const toView = useCallback(
debounce( debounce(
...@@ -168,10 +171,10 @@ export function AIWrapper({ ...@@ -168,10 +171,10 @@ export function AIWrapper({
}, },
]); ]);
// @ts-ignore // @ts-ignore
inputRef.value = ''; setRecordValue('');
setLoading(true); setLoading(true);
toView(); toView();
let check = !isCheck; let checked = checkOpen ? false : true;
const request = () => { const request = () => {
OpenAI.request( OpenAI.request(
noHistory noHistory
...@@ -181,20 +184,22 @@ export function AIWrapper({ ...@@ -181,20 +184,22 @@ export function AIWrapper({
}) })
: initMessageList, : initMessageList,
currentAssistantMessageStr => { (currentAssistantMessageStr, cancel) => {
setTinking(true); setThinking(true);
setCheck(true);
const done = () => { const done = () => {
setCheck(false);
setLoading(false); setLoading(false);
setTimeout(toView, 100); setTimeout(toView, 100);
setCurrentAssistantMessage(currentAssistantMessageStr); setCurrentAssistantMessage(currentAssistantMessageStr);
callBack && callBack(currentAssistantMessageStr); callBack && callBack(currentAssistantMessageStr);
doneFx && doneFx(currentAssistantMessageStr); doneFx && doneFx(currentAssistantMessageStr);
}; };
check checked || cancel
? done() ? done()
: getView : getView
.checkChatResult([ .checkChatResult([
...messageList, ...initMessageList,
{ {
role: 'user', role: 'user',
content: inputValue, content: inputValue,
...@@ -213,11 +218,13 @@ export function AIWrapper({ ...@@ -213,11 +218,13 @@ export function AIWrapper({
'data.output', 'data.output',
{} {}
); );
check = true; checked = true;
setTinking(false); setThinking(false);
setTimeout(toView, 100);
if (answerMeetsRequirements) { if (answerMeetsRequirements) {
done(); done();
} else { } else {
setCheck(why);
initMessageList.push( initMessageList.push(
{ {
role: 'assistant', role: 'assistant',
...@@ -230,7 +237,7 @@ export function AIWrapper({ ...@@ -230,7 +237,7 @@ export function AIWrapper({
}, },
{ {
role: 'user', role: 'user',
content: why, content: `错误!!!!,你的回答不符合的需求,你的回答存在以下问题:\n${why}。\n请改正这些问题并重新输出。`,
} }
); );
request(); request();
...@@ -262,6 +269,8 @@ export function AIWrapper({ ...@@ -262,6 +269,8 @@ export function AIWrapper({
input, input,
handleButtonClickSuccess, handleButtonClickSuccess,
scrollContainer, scrollContainer,
setCheck,
checkOpen,
] ]
); );
...@@ -269,7 +278,7 @@ export function AIWrapper({ ...@@ -269,7 +278,7 @@ export function AIWrapper({
const clear = () => { const clear = () => {
const inputRef = get(input, 'current.dom'); const inputRef = get(input, 'current.dom');
if (inputRef) { if (inputRef) {
inputRef.value = ''; setRecordValue('');
setMessageList(messageList.slice(0, startView)); setMessageList(messageList.slice(0, startView));
setCurrentAssistantMessage(''); setCurrentAssistantMessage('');
} }
...@@ -355,10 +364,10 @@ export function AIWrapper({ ...@@ -355,10 +364,10 @@ export function AIWrapper({
const [beforeRecordValue, setBeforeRecordValue] = useState(''); const [beforeRecordValue, setBeforeRecordValue] = useState('');
const [recordValue, setRecordValue] = useState(''); const [recordValue, setRecordValue] = useState('');
useEffect(() => { useEffect(() => {
if (!isRecording) { if (!isRecording) {
const el = input.current?.dom; const el = input.current?.dom;
console.log(recordValue);
if (el) el.value = recordValue || ''; if (el) el.value = recordValue || '';
} }
}, [isRecording, recordValue]); }, [isRecording, recordValue]);
...@@ -435,6 +444,80 @@ export function AIWrapper({ ...@@ -435,6 +444,80 @@ export function AIWrapper({
/> />
); );
const detail = useMemo<ReactNode>(() => {
if (check) {
return (
<div className="my-[20px]">
<div
className={`flex gap-3 p-4 box-border shadow mx-[5px] rounded transition-colors mt-[20px] font-hm ${'bg-[var(--white-bg)] text-[#333]'}`}
>
<Form style={{ width: '100%' }} autoComplete="off">
<div className="py-[10px] pb-[30px] text-[20px] justify-between flex">
{t('Reviewing and checking if query aligns with requirements')}
<span>
<IconSwap />
</span>
</div>
{check !== true ? (
<Alert
content={t('Requirements and answers do not align')}
type={'warning'}
className="my-[20px]"
/>
) : null}
<pre
className="break-before-all"
style={{
whiteSpace: 'break-spaces',
}}
>
{check}
</pre>
</Form>
</div>
</div>
);
}
if (currentAssistantMessage) {
return (
<div className="my-[20px]">
<div
className={`flex gap-3 p-4 box-border shadow mx-[5px] rounded transition-colors mt-[20px] font-hm ${'bg-[var(--white-bg)] text-[#333]'}`}
>
<Form style={{ width: '100%' }} autoComplete="off">
<div className="py-[10px] pb-[30px] text-[20px] justify-between flex">
{t('A query has been generated for you')}
<span>
<IconSwap />
</span>
</div>
<pre
className="break-before-all"
style={{
whiteSpace: 'break-spaces',
}}
>
{currentAssistantMessage}
</pre>
</Form>
</div>
</div>
);
} else {
return (
<div className="my-[20px]">
<Skeleton
animation
text={{
rows: 5,
width: ['100%', '100%', '100%', '100%', 400],
}}
/>
</div>
);
}
}, [currentAssistantMessage, check]);
if (simpleMode) { if (simpleMode) {
if (simpleMode === 'input') { if (simpleMode === 'input') {
return inputNode; return inputNode;
...@@ -530,61 +613,41 @@ export function AIWrapper({ ...@@ -530,61 +613,41 @@ export function AIWrapper({
style={{ fontSize: '32px', color: 'rgb(var(--primary-6))' }} style={{ fontSize: '32px', color: 'rgb(var(--primary-6))' }}
/> />
} }
content={ content={detail}
!currentAssistantMessage ? (
<div className="my-[20px]">
<Skeleton
animation
text={{
rows: 5,
width: ['100%', '100%', '100%', '100%', 400],
}}
/>
</div>
) : (
<div className="my-[20px]">
<div
className={`flex gap-3 p-4 box-border shadow mx-[5px] rounded transition-colors mt-[20px] font-hm ${'bg-[var(--white-bg)] text-[#333]'}`}
>
<Form style={{ width: '100%' }} autoComplete="off">
<div className="py-[10px] pb-[30px] text-[20px] justify-between flex">
{t('A query has been generated for you')}
<span>
<IconSwap />
</span>
</div>
<pre
className="break-before-all"
style={{
whiteSpace: 'break-spaces',
}}
>
{currentAssistantMessage}
</pre>
</Form>
</div>
</div>
)
}
/> />
)} )}
<div ref={scrollContainer} /> <div ref={scrollContainer} />
</div> </div>
{loading ? ( {loading ? (
<div className="animate-bounce"> <>
<div className="h-12 my-4 flex items-center justify-center rounded-sm text-[30px] font-bold gradient-text"> <div className="animate-bounce">
{!tinking <div className="h-12 my-4 flex items-center justify-center rounded-sm text-[30px] font-bold gradient-text">
? `${t('Analyzing requirements')}...` {check
: `${t('Creating query')}...`} ? check !== true
? `${t('Requirements and answers do not align, rethinking')}...`
: `${t('Reviewing requirements and answers')}...`
: `${t('Analyzing requirements')}...`}
</div>
</div> </div>
<Button className="shadow" onClick={close} size="mini" shape="round"> <Button className="shadow" onClick={close} size="mini" shape="round">
取消 {t('Cancel')}
</Button> </Button>
</div> </>
) : ( ) : (
<Comment <Comment
className="items-stretch !mt-[5px] pt-[15px] mx-[-15px] px-[15px] shadow-t" className="items-stretch !mt-[5px] pt-[15px] mx-[-15px] px-[15px] shadow-t"
actions={[ actions={[
<span className="flex items-center">
<span className="mr-[10px] text-[12px]">自动检查</span>
<Switch
type="line"
size="small"
checked={checkOpen}
onChange={value => {
setIsCheck(value);
}}
/>
</span>,
<Button <Button
key="0" key="0"
onClick={clear} onClick={clear}
...@@ -638,8 +701,12 @@ export function AIWrapper({ ...@@ -638,8 +701,12 @@ export function AIWrapper({
value={inputValue} value={inputValue}
onChange={value => { onChange={value => {
setRecordValue(value); setRecordValue(value);
const el = input.current?.dom;
console.log(value, recordValue, el, el.value);
if (el) el.value = value || '';
}} }}
onPressEnter={e => { onPressEnter={e => {
console.log('w22');
if ( if (
e.key === 'Enter' && e.key === 'Enter' &&
!e?.nativeEvent.isComposing && !e?.nativeEvent.isComposing &&
...@@ -673,6 +740,7 @@ export function AIWrapper({ ...@@ -673,6 +740,7 @@ export function AIWrapper({
// popupVisible: true, // popupVisible: true,
}} }}
onSelect={(value: string) => { onSelect={(value: string) => {
console.log('w22');
const el = input.current?.dom; const el = input.current?.dom;
if (el) { if (el) {
el.value = value; el.value = value;
......
...@@ -152,6 +152,9 @@ i18n.use(LanguageDetector) ...@@ -152,6 +152,9 @@ i18n.use(LanguageDetector)
'Execution succeeded': '执行成功', 'Execution succeeded': '执行成功',
'Analyzing requirements': '分析需求中', 'Analyzing requirements': '分析需求中',
'Creating query': '创建查询中', 'Creating query': '创建查询中',
'Query generation in progress': '正在生成查询',
'Reviewing and checking if query aligns with requirements':
'正在审核查询是否满足需求',
'Hello! I an CHAT QUERY ai, please describe your business!': 'Hello! I an CHAT QUERY ai, please describe your business!':
'您好!我是 CHAT QUERY AI,请描述您的业务!', '您好!我是 CHAT QUERY AI,请描述您的业务!',
'Connection Name': '连接名称', 'Connection Name': '连接名称',
...@@ -166,6 +169,13 @@ i18n.use(LanguageDetector) ...@@ -166,6 +169,13 @@ i18n.use(LanguageDetector)
_QUERY_: '_查询_', _QUERY_: '_查询_',
'Failed to generate query': '生成查询失败', 'Failed to generate query': '生成查询失败',
'Error in query': '错误查询', 'Error in query': '错误查询',
Cancel: '取消',
'Cannot generate executable query from the requirement description, please continue to describe the requirement!':
'从需求描述中不能为您生成可执行的查询,请继续描述的需求!',
'Requirements and answers do not align': '需求和回答没有对齐',
'Requirements and answers do not align, rethinking':
'需求和回答没有对齐,重新思考中',
'Reviewing requirements and answers': '正在审查需求和回答',
}, },
modal: { modal: {
Import: '导入', Import: '导入',
...@@ -211,6 +221,15 @@ i18n.use(LanguageDetector) ...@@ -211,6 +221,15 @@ i18n.use(LanguageDetector)
}, },
chatView: { chatView: {
'component display': '组件显示', 'component display': '组件显示',
'Function for processing data': '处理数据的函数',
'Add component': '添加组件',
'Code generation in progress, please wait...': '代码生成中,请稍后...',
'Data processing': '数据处理',
'Comment + Tab to generate function': '注释 + Tab 生成函数',
'Execute function name': '执行函数名称',
'No function description available': '暂无函数描述',
Run: '运行',
'Execution successful': '执行成功',
}, },
}, },
}, },
......
...@@ -53,6 +53,7 @@ import 'systemjs'; ...@@ -53,6 +53,7 @@ import 'systemjs';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { simple } from 'acorn-walk'; import { simple } from 'acorn-walk';
import jsx from 'acorn-jsx'; import jsx from 'acorn-jsx';
import i18n from '@/next-i18next.config';
const CollapseItem = Collapse.Item; const CollapseItem = Collapse.Item;
...@@ -65,11 +66,16 @@ function Error() { ...@@ -65,11 +66,16 @@ function Error() {
return <code>{error}</code>; return <code>{error}</code>;
} }
const init = `/** // const initText = i18n.getFixedT(i18n.language, 'chatView')('Function for processing data');
* @description 处理数据的函数
const init = (initText: string): string => {
return `/**
* @description ${initText}
* @param record<string,any> data * @param record<string,any> data
*/ */
`; `;
};
async function getUMDPath(packageName: string) { async function getUMDPath(packageName: string) {
const response = await fetch(`https://data.jsdelivr.com/v1/packages/npm/${packageName}`); const response = await fetch(`https://data.jsdelivr.com/v1/packages/npm/${packageName}`);
const packageJson = await response.json(); const packageJson = await response.json();
...@@ -140,6 +146,8 @@ async function loadUMDModuleBySystem(packageName: string) { ...@@ -140,6 +146,8 @@ async function loadUMDModuleBySystem(packageName: string) {
function ReactView({ props }: { props: Record<string, any> }) { function ReactView({ props }: { props: Record<string, any> }) {
const { code } = useLiveContext(); const { code } = useLiveContext();
const { t } = useTranslation('chatView');
return ( return (
<div> <div>
<div className="flex justify-end my-[5px]"> <div className="flex justify-end my-[5px]">
...@@ -161,7 +169,7 @@ function ReactView({ props }: { props: Record<string, any> }) { ...@@ -161,7 +169,7 @@ function ReactView({ props }: { props: Record<string, any> }) {
} }
}} }}
> >
<IconPlus /> 添加组件 <IconPlus /> {t('Add component')}
</Button> </Button>
</div> </div>
<LivePreview className="overflow-auto min-h-[350px]" /> <LivePreview className="overflow-auto min-h-[350px]" />
...@@ -173,7 +181,7 @@ export function ChatView({ ...@@ -173,7 +181,7 @@ export function ChatView({
defaultNode, defaultNode,
props: propsRaw, props: propsRaw,
id, id,
functions = init, functions = '',
}: { }: {
defaultNode?: ReactElement; defaultNode?: ReactElement;
props: Record<string, any>; props: Record<string, any>;
...@@ -271,6 +279,7 @@ export function ChatView({ ...@@ -271,6 +279,7 @@ export function ChatView({
const example = get(data, 'data.code'); const example = get(data, 'data.code');
const { t } = useTranslation('chatView'); const { t } = useTranslation('chatView');
functions = functions || init(t('Function for processing data'));
const Live = useMemo(() => { const Live = useMemo(() => {
let node; let node;
...@@ -323,7 +332,7 @@ export function ChatView({ ...@@ -323,7 +332,7 @@ export function ChatView({
const position = editor.getPosition()!; const position = editor.getPosition()!;
const lineNumber = position.lineNumber; const lineNumber = position.lineNumber;
const column = position.column; const column = position.column;
const insertText = ' 代码生成中,请稍后...'; const insertText = ` ` + t('Code generation in progress, please wait...');
const op = { const op = {
range: new monaco.Range(lineNumber, column, lineNumber, column), range: new monaco.Range(lineNumber, column, lineNumber, column),
text: insertText, text: insertText,
...@@ -406,9 +415,9 @@ export function ChatView({ ...@@ -406,9 +415,9 @@ export function ChatView({
<CollapseItem <CollapseItem
header={ header={
<div> <div>
数据处理 {t('Data processing')}
<span className="text-[var(--pc)] mx-[10px] text-[12px]"> <span className="text-[var(--pc)] mx-[10px] text-[12px]">
(注释 + Tab 生成函数) ({t('Comment + Tab to generate function')})
</span> </span>
</div> </div>
} }
...@@ -416,7 +425,7 @@ export function ChatView({ ...@@ -416,7 +425,7 @@ export function ChatView({
extra={ extra={
<div className="flex"> <div className="flex">
<Select <Select
placeholder="执行函数名称" placeholder={t('Execute function name')}
size="mini" size="mini"
allowClear allowClear
style={{ style={{
...@@ -465,7 +474,9 @@ export function ChatView({ ...@@ -465,7 +474,9 @@ export function ChatView({
{get( {get(
item, item,
'comment', 'comment',
'暂无函数描述' t(
'No function description available'
)
)} )}
</span> </span>
</Tooltip> </Tooltip>
...@@ -497,7 +508,7 @@ export function ChatView({ ...@@ -497,7 +508,7 @@ export function ChatView({
size="mini" size="mini"
shape="round" shape="round"
> >
运行 {t('Run')}
</Button> </Button>
</div> </div>
} }
...@@ -516,7 +527,11 @@ export function ChatView({ ...@@ -516,7 +527,11 @@ export function ChatView({
</div> </div>
</> </>
) : ( ) : (
<Alert style={{ marginBottom: 20 }} type="success" content="执行成功" /> <Alert
style={{ marginBottom: 20 }}
type="success"
content={t('Execution successful')}
/>
)} )}
<Spin loading={isMutating} block className="my-[20px]"> <Spin loading={isMutating} block className="my-[20px]">
{example && !showTable ? ( {example && !showTable ? (
......
...@@ -520,7 +520,9 @@ const MessageItemHOC = ({ setShowQueriesList, activeDb, currentModels, activeMod ...@@ -520,7 +520,9 @@ const MessageItemHOC = ({ setShowQueriesList, activeDb, currentModels, activeMod
<Alert <Alert
content={ content={
<div className="text-[#7e7d7d]"> <div className="text-[#7e7d7d]">
从需求描述中不能为您生成可执行的查询,请继续描述的需求! {t(
'Cannot generate executable query from the requirement description, please continue to describe the requirement!'
)}
</div> </div>
} }
/> />
...@@ -656,7 +658,10 @@ export default function Actions() { ...@@ -656,7 +658,10 @@ export default function Actions() {
try { try {
const data = await getAllGraphs(); const data = await getAllGraphs();
if (data && data.length) { if (data && data.length) {
data.sort((a, b) => b.createdAt - a.createdAt); data.sort(
(a: { createdAt: number }, b: { createdAt: number }) =>
b.createdAt - a.createdAt
);
setGraphs(data); setGraphs(data);
} }
} catch (e) { } catch (e) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment