Commit 1118e560 authored by jaden's avatar jaden

feat: add query check

parent 484b7b14
import { ChatMessage } from './../../components/AITool/index';
import getConfig from 'next/config';
import APi, { backendApi } from '.';
const {
......@@ -17,4 +18,7 @@ export default class getView {
static getViewFunction(params: { data: Record<string, any>; need: string }) {
return backendApi.post('/openAi/api/code', params);
}
static checkChatResult(messageList: ChatMessage[]) {
return backendApi.post('/openAi/api/checkQuery', { messageList });
}
}
......@@ -25,6 +25,7 @@ import React, {
} from 'react';
import { IconRecord, IconRobot, IconSend, IconSwap, IconVoice } from '@arco-design/web-react/icon';
import OpenAI from '@/client/api/openAI';
import getView from '@/client/api/getView';
let useSpeechToText: any;
export interface ChatMessage {
......@@ -52,7 +53,8 @@ type AIProps = {
noHistory?: boolean;
functions: any[];
stream?: boolean;
function_call: any;
function_call?: any;
isCheck?: boolean;
};
export function AIWrapper({
......@@ -74,6 +76,7 @@ export function AIWrapper({
functions,
function_call,
stream,
isCheck = false,
}: AIProps) {
const input = useRef<any>();
const scrollContainer = useRef<any>();
......@@ -121,6 +124,28 @@ export function AIWrapper({
),
[]
);
const requestMessageList = useMemo(() => {
if (functions) {
return messageList.map(v => {
if (v.role === 'assistant') {
let fn;
try {
fn = JSON.parse(get(v, 'content'));
} catch (e) {
fn = '';
}
return {
...v,
content: functions && fn ? '' : get(v, 'content'),
function_call: functions && fn ? fn : undefined,
};
}
return v;
});
}
return messageList;
}, [messageList]);
const handleButtonClick = useCallback(
(message?: string, callBack?: (m: string) => void) => {
const inputRef = input.current?.dom;
......@@ -128,18 +153,26 @@ export function AIWrapper({
if (!inputValue) {
return;
}
const initMessageList = [
...messageList,
let initMessageList = [
...requestMessageList,
{
role: 'user',
content: inputValue,
},
];
setMessageList(initMessageList);
setMessageList([
...messageList,
{
role: 'user',
content: inputValue,
},
]);
// @ts-ignore
inputRef.value = '';
setLoading(true);
toView();
let check = !isCheck;
const request = () => {
OpenAI.request(
noHistory
? initMessageList.slice(0, startView).concat({
......@@ -149,16 +182,60 @@ export function AIWrapper({
: initMessageList,
currentAssistantMessageStr => {
// setTinking(true);
// setTimeout(() => {
setTinking(true);
const done = () => {
setLoading(false);
// setTinking(false);
setTimeout(toView, 100);
// }, 100);
setCurrentAssistantMessage(currentAssistantMessageStr);
callBack && callBack(currentAssistantMessageStr);
doneFx && doneFx(currentAssistantMessageStr);
console.log(currentAssistantMessageStr, 'currentAssistantMessageStr');
};
check
? done()
: getView
.checkChatResult([
...messageList,
{
role: 'user',
content: inputValue,
},
{
role: 'assistant',
function_call: functions
? JSON.parse(currentAssistantMessageStr)
: undefined,
content: functions ? '' : currentAssistantMessageStr,
},
])
.then((v: any) => {
const { answerMeetsRequirements, why } = get(
v,
'data.output',
{}
);
check = true;
setTinking(false);
if (answerMeetsRequirements) {
done();
} else {
initMessageList.push(
{
role: 'assistant',
function_call: functions
? JSON.parse(currentAssistantMessageStr)
: undefined,
content: functions
? ''
: currentAssistantMessageStr,
},
{
role: 'user',
content: why,
}
);
request();
}
});
},
val => {
setCurrentAssistantMessage(val);
......@@ -175,6 +252,8 @@ export function AIWrapper({
function_call,
stream || !functions
);
};
request();
},
[
loading,
......
......@@ -6,20 +6,26 @@ export const QUERY_FUNCTION = [
parameters: {
type: 'object',
properties: {
simulation: {
type: 'boolean',
description:
'This requirement needs you to generate simulated data, with true or false options.',
},
sql: {
type: 'string',
description:
'The SQL statement to execute must be standard and cannot contain any other code.',
description: `If the user's requirements include generating simulated data, you should generate this data first and ensure that it is included in the SQL output.,The SQL statement required to execute the operation must adhere to standard syntax. Additionally, the data inserted must comply with the existing table structure.`,
},
variablesArr: {
type: 'array',
description: 'The array containing variables and their descriptions',
description:
'Execute SQL statement containing an array of variables and their descriptions: If the requirement specifies simulated data, the parameter should be null.',
items: {
type: 'object',
properties: {
variable: {
type: 'string',
description: 'The variable',
description:
'The name of the variables.The information that needs to be filled in by the user should be treated as variables, and the variable names must be enclosed between $ signs.',
},
varDescription: {
type: 'string',
......@@ -38,7 +44,7 @@ export const QUERY_FUNCTION = [
description: 'The description of the query',
},
},
required: ['sql', 'variablesArr', 'queryName', 'queryDescription'],
required: ['sql', 'queryName', 'queryDescription', 'simulation'],
},
},
];
......@@ -7,22 +7,19 @@ export const GET_QUERY = (
${sql}
\`\`\`
按照需求生成结果之后,将结果按照xml语法,将模版中的标签中的内容进行分析,并且替换标签中的内容(但必须保留标签)作为最后结果输出,并且与模版排版必须保持一致(输出结果标签必须保留。变量的命名必须请放在$和$之间,例如:$tab$、$name$...)。:
将以下中的xml标签中的内容进行分析(用户需要填入的信息作为变量,如果需要变量:变量的命名必须请放在$和$之间,例如:$tab$、$name$...)。:
根据您提供的数据库模型,已为您生成查询:
<sql>按照需要生产的sql语句</sql>
执行所需变量:
判断解决当前问题的查询执行时是否需要变量。若条件成立,请使用:
<var>执行 SQL 所需变量名称</var>: <varDescription>变量解释</varDescription>
若不需要变量,则无需提供任何信息。
判断解决当前问题的查询执行时是否需要变量。若不需要变量,则无需提供任何信息。 若条件成立,请使用:
<var>$执行SQL所需变量名称$</var>: <varDescription>变量解释</varDescription>
查询命名和描述:
<queryName>查询名称</queryName>
<queryDescription>查询描述</queryDescription>
`;
export const GET_SCHEMA_INFO = (
sql: dbml
......
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Clone the request headers
// You can modify them with headers API: https://developer.mozilla.org/en-US/docs/Web/API/Headers
const requestHeaders = new Headers(request.headers)
const requestHeaders = new Headers(request.headers);
// Add new request headers
requestHeaders.set('authorization', `Bearer ${process.env.NEXT_PUBLIC_OPEN_AI_API_KEY || ''}`)
requestHeaders.set('authorization', `Bearer ${process.env.NEXT_PUBLIC_OPEN_AI_API_KEY || ''}`);
//Todo 需要获取questionType,动态添加Prompt
// You can also set request headers in NextResponse.rewrite
return NextResponse.next({
......@@ -15,5 +17,5 @@ export function middleware(request: NextRequest) {
// New request headers
headers: requestHeaders,
},
})
});
}
......@@ -21,6 +21,8 @@ import {
Tabs,
Typography,
Select,
Descriptions,
Alert,
} from '@arco-design/web-react';
import { memo, useCallback, useEffect, useMemo, useReducer, useState } from 'react';
import { getAllGraphs } from '@/data/db';
......@@ -75,7 +77,8 @@ import { GET_QUERY, GET_SCHEMA_INFO } from '@/data/prompt';
import Welcome from 'components/AITool/MessageItem';
import * as queryTipRaw from '@/data/prompt/query-tip';
import { QUERY_FUNCTION } from '@/data/prompt/functions';
import { FunctionsJson, functionsJson } from '@/utils/getXMLContent';
import { FunctionsJson } from '@/utils/getXMLContent';
import { codeWrapper, htmlString } from '@/components/AITool/MessageItem';
const queryTip = map(queryTipRaw, v => {
return {
name: v,
......@@ -285,16 +288,6 @@ const MessageItemHOC = ({ setShowQueriesList, activeDb, currentModels, activeMod
queryDescription: any;
}>(() => {
const funcJson = new FunctionsJson(message);
const elNode = document.createElement('div');
elNode.innerHTML = message;
let sqlNodes = elNode.querySelectorAll('sql');
if (!sqlNodes.length) {
let str = message.replace(/```sql/g, `<sql>`);
str = str.replace(/```/g, `</sql>`);
elNode.innerHTML = str;
sqlNodes = elNode.querySelectorAll('sql');
}
const params = {};
forEach(funcJson.get('variablesArr'), (item: any) => {
const key = get(item, 'variable');
......@@ -303,7 +296,7 @@ const MessageItemHOC = ({ setShowQueriesList, activeDb, currentModels, activeMod
}
});
return {
sql: [funcJson.get('sql').replace(/;\s+/g, ';\n')],
sql: [funcJson.get('sql').replace(/;\s+/g, ';\n')].filter(v => v.trim()),
types: ['sql'],
params,
queryName: funcJson.get('queryName'),
......@@ -315,6 +308,8 @@ const MessageItemHOC = ({ setShowQueriesList, activeDb, currentModels, activeMod
const [loading, setLoading] = useState(false);
const [modal, contextHolder] = Modal.useModal();
const [formInstance] = Form.useForm();
const { t: ct } = useTranslation('queriesList');
return (
<div
className={`flex gap-3 p-4 box-border shadow mx-[5px] rounded transition-colors mt-[20px] font-hm ${
......@@ -341,14 +336,77 @@ const MessageItemHOC = ({ setShowQueriesList, activeDb, currentModels, activeMod
)}
</div>
{rawSqlMode || !size(sql) ? (
<pre
className="break-before-all mb-[20px]"
style={{
whiteSpace: 'break-spaces',
<div className="my-[10px] ">
<Typography.Paragraph className=" h-full !m-0 my-[5px] text-[rgb(var(--primary-6))] font-bold text-[12px] inline-block">
{ct('Query Description')}
</Typography.Paragraph>
{queryDescription ? (
<div className="my-[5px] text-[12px] indent-[2em]">
{queryDescription}
</div>
) : (
<Empty
description={ct('No description needed')}
icon={<span></span>}
/>
)}
{size(sql) ? (
<>
<Descriptions
border
size="mini"
title={
<div className="my-[10px] text-[rgb(var(--primary-6))] font-bold text-[15px]">
{ct('Execution Details')}
</div>
}
column={1}
data={map(sql, sql => ({
label: 'sql',
value: (
<div
className="message prose text-slate break-words overflow-hidden"
dangerouslySetInnerHTML={{
__html: htmlString(
codeWrapper('sql', sql)
),
}}
/>
),
}))}
style={{ marginBottom: 20 }}
labelStyle={{ paddingRight: 36 }}
/>
<Typography.Paragraph
className=" h-full !m-0 my-[5px] text-[rgb(var(--primary-6))] font-bold text-[12px] inline-block"
copyable={{
text: JSON.stringify({
params: formInstance.getFields(),
}),
}}
>
{message}
</pre>
{ct('Parameter Description')}
</Typography.Paragraph>
{Object.keys(params).length ? (
<Descriptions
border
size="mini"
column={1}
data={map(params, (value, label) => {
return { value, label };
})}
style={{ margin: '20px 0' }}
labelStyle={{ paddingRight: 36 }}
/>
) : (
<Empty
description={ct('No parameters needed')}
icon={<span></span>}
/>
)}
</>
) : null}
</div>
) : (
map(params, (val, key) => {
console.log(val, key);
......@@ -459,9 +517,12 @@ const MessageItemHOC = ({ setShowQueriesList, activeDb, currentModels, activeMod
</div>
</>
) : (
<Empty
description={t('Error in query')}
icon={<IconCloseCircleFill className="text-red-500" />}
<Alert
content={
<div className="text-[#7e7d7d]">
从需求描述中不能为您生成可执行的查询,请继续描述的需求!
</div>
}
/>
)}
</Form>
......@@ -632,17 +693,12 @@ export default function Actions() {
content: GET_QUERY(sql),
},
{
role: 'assistant',
content: `好的,请问这个示例对吗?
根据您提供的数据库模型,生成的查询如下:
{"name":"python","arguments":"import pandas as pd\n\n# 模拟查询数据库中的表\ntables = ['departments', 'doctors', 'patients', 'appointments']\n\n# 将查询结果转换为DataFrame格式\ndf = pd.DataFrame(tables, columns=['Table Name'])\n\n# 将DataFrame转换为HTML格式\nhtml_result = df.to_html(index=False)\n\nhtml_result"}
`,
},
role: 'system',
content: `你第一步应该思考分析:用户是否指定生成模拟数据,如果有,你还需要生成模拟数据,你第二步应该思考分析:用户需求中是否需要变量(例如:生成模拟数据就不需要变量,因为数据由你生成),如果需要变量:变量的命名必须请放在$和$之间,例如:"variablesArr": [
{
role: 'user',
content: `不对!必须且仅能调用functions中saveExecuteSqlInfo,(输出结果标签(<sql>、<var>等)必须保留。变量的命名必须请放在$和$之间,例如:$tab$、$name$...),下面是一个示例:
根据您提供的数据库模型,已为您生成查询:
{"name":"saveExecuteSqlInfo","arguments":"{\n \"sql\": \"SELECT * FROM departments; SELECT * FROM doctors; SELECT * FROM patients; SELECT * FROM appointments;\",\n \"variablesArr\": [],\n \"queryName\": \"查询所有表中的数据\",\n \"queryDescription\": \"该查询用于检索所有表中的数据。\"\n}"}
"variable": "$variable$",
"varDescription": "这个变量的描述"
}):
`,
},
{
......@@ -778,6 +834,7 @@ export default function Actions() {
>
<Content>
<AI
isCheck
functions={QUERY_FUNCTION}
function_call={{
name: 'saveExecuteSqlInfo',
......@@ -821,7 +878,7 @@ export default function Actions() {
/>
</>
}
startView={5}
startView={4}
key={session.id}
messageList={
session.messageList
......
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