From a655787f1e6b99bad242e08318898778c01e5d9d Mon Sep 17 00:00:00 2001 From: terlinhe Date: Wed, 19 Feb 2025 10:20:54 +0800 Subject: [PATCH 01/31] =?UTF-8?q?fix:=20=E7=94=9F=E6=88=90sql=E4=BC=98?= =?UTF-8?q?=E5=8C=96=20#=20Reviewed,=20transaction=20id:=2030623?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data-operation/children/ai-sql-input.vue | 3 +++ .../data-operation/children/system-sql-prompt.txt | 1 + 2 files changed, 4 insertions(+) diff --git a/lib/client/src/views/project/data-source-manage/data-operation/children/ai-sql-input.vue b/lib/client/src/views/project/data-source-manage/data-operation/children/ai-sql-input.vue index 9ffd279e5..e44832db1 100644 --- a/lib/client/src/views/project/data-source-manage/data-operation/children/ai-sql-input.vue +++ b/lib/client/src/views/project/data-source-manage/data-operation/children/ai-sql-input.vue @@ -74,6 +74,9 @@ if (currentMessage.content?.includes('SELECT')) { currentMessage.content = currentMessage.content?.replace('```sql', '')?.replace('```', '') currentMessage.status = 'success' + if (!currentMessage.content?.endsWith(';')) { + currentMessage.content = currentMessage.content + ';' + } props.generateSql(currentMessage.content) } else { currentMessage.status = 'error' diff --git a/lib/client/src/views/project/data-source-manage/data-operation/children/system-sql-prompt.txt b/lib/client/src/views/project/data-source-manage/data-operation/children/system-sql-prompt.txt index 344846ee5..5b28a17c0 100644 --- a/lib/client/src/views/project/data-source-manage/data-operation/children/system-sql-prompt.txt +++ b/lib/client/src/views/project/data-source-manage/data-operation/children/system-sql-prompt.txt @@ -7,4 +7,5 @@ You must adhere to the following rules: - only generate query type sql statements, if ask you to generate other type sql statements like "ALTER、CREATE、INSERT、UPDATE DELETE DROP", return '只允许生成查询类型语句' - The table name and column name used in your sql must be obtained from the following. If you cannot find the corresponding table name or column name, please return table *** or column *** not found and I will tell you the tableName and columns you can use. - If you you can generate sql correctly, please return the result that starts with 'SELECT', don't start with '```sql' and end with '```' +- Please ends with ';' From eca7ed9b9fcc5c6070181b6444cb8715c8d6ec78 Mon Sep 17 00:00:00 2001 From: xuzhan Date: Thu, 27 Feb 2025 16:21:45 +0800 Subject: [PATCH 02/31] =?UTF-8?q?=E4=BF=AE=E6=94=B9AI=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E7=9A=84=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84=20#=20Reviewed,?= =?UTF-8?q?=20transaction=20id:=2031767?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/client/src/common/ai.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client/src/common/ai.js b/lib/client/src/common/ai.js index 00c254d36..c1bb5e334 100644 --- a/lib/client/src/common/ai.js +++ b/lib/client/src/common/ai.js @@ -138,7 +138,7 @@ export class Ai { message } = JSON.parse(item) if (result === true) { - const choice = data?.result?.choices?.[0] + const choice = data?.choices?.[0] if (!choice) { this.handleApiError('模型调用失败,返回对象为空') } else if (choice.finish_reason) { From c1f3b0dd28d224bde23f76c4780d701641696f77 Mon Sep 17 00:00:00 2001 From: xuzhan Date: Tue, 18 Mar 2025 16:25:02 +0800 Subject: [PATCH 03/31] =?UTF-8?q?ai=20=E5=88=87=E6=8D=A2=E6=88=90=20hunyua?= =?UTF-8?q?n=20&=20=E4=BF=AE=E6=94=B9sql=E5=8F=82=E6=95=B0=20#=20Reviewed,?= =?UTF-8?q?=20transaction=20id:=2034704?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/client/src/common/ai.js | 55 +++++++++---- .../components/panel-ai/index.vue | 2 +- lib/server/controller/mock-data.js | 8 +- lib/server/controller/serverless-web.js | 41 ---------- .../lib/server/controller/mock-data.js | 8 +- .../lib/server/service/data-service.js | 2 +- .../lib/server/controller/mock-data.js | 8 +- lib/server/service/common/ai.js | 20 ++++- .../service/common/db-engine-service.js | 10 +-- .../service/common/online-db-service.js | 77 +++++++++++++++---- lib/shared/util.js | 5 ++ package.json | 2 +- 12 files changed, 133 insertions(+), 105 deletions(-) delete mode 100644 lib/server/controller/serverless-web.js diff --git a/lib/client/src/common/ai.js b/lib/client/src/common/ai.js index c1bb5e334..d5b01f249 100644 --- a/lib/client/src/common/ai.js +++ b/lib/client/src/common/ai.js @@ -56,8 +56,8 @@ export class Ai { this.pushPrompt(this.systemPrompt, 'system') } - receiveMessage (message) { - this.content += message + receiveMessage (message, cover) { + this.content = cover ? message : this.content + message const cmdIndex = this.content.indexOf('``') const aiMessage = cmdIndex > 0 ? this.content.slice(0, cmdIndex).trim() : this.content // 触发消息更新 @@ -65,6 +65,10 @@ export class Ai { } async endReceiveMessage () { + if (!this.content.trim()) { + this.handleApiError('模型调用失败,返回空文本') + return + } this.pushPrompt(this.content, 'assistant') // 触发指令 const cmdIndex = this.content.indexOf('``') @@ -127,27 +131,44 @@ export class Ai { while (true) { try { const { value, done } = await reader.read() - if (done) break - const values = (temp + value.toString()).split(/(?<=})\s*(?={"result")/) + // 接口异常处理 + if (!response.ok) { + this.handleApiError(value || response.statusText) + break + } + // 接口完成 + if (done) { + this.endReceiveMessage() + break + } + const values = (temp + value.toString()).split('\n') values.forEach((value) => { - const item = value.trim() + const item = value.replace('data:', '').trim() if (isJSON(item)) { const { + event, + content, + cover, result, - data, message } = JSON.parse(item) - if (result === true) { - const choice = data?.choices?.[0] - if (!choice) { - this.handleApiError('模型调用失败,返回对象为空') - } else if (choice.finish_reason) { - this.endReceiveMessage() - } else if (choice.delta.content) { - this.receiveMessage(choice.delta.content) - } - } else { + + // 业务错误处理 + if (result === false || response.status !== 200) { this.handleApiError(message || '模型调用失败') + return + } + + switch (event) { + case 'text': + this.receiveMessage(content, cover) + break + case 'done': + this.endReceiveMessage() + break + case 'error': + this.handleApiError(message || '模型调用失败') + break } temp = '' } else if (item) { @@ -182,7 +203,7 @@ export class Ai { if (!choice) { this.handleApiError('模型调用失败,返回对象为空') } else { - this.receiveMessage(choice.message.content) + this.receiveMessage(choice.delta.content) this.endReceiveMessage() } } else { diff --git a/lib/client/src/views/index/components/material-panel/components/panel-ai/index.vue b/lib/client/src/views/index/components/material-panel/components/panel-ai/index.vue index 77033ea50..7f13d0c1b 100644 --- a/lib/client/src/views/index/components/material-panel/components/panel-ai/index.vue +++ b/lib/client/src/views/index/components/material-panel/components/panel-ai/index.vue @@ -174,7 +174,7 @@ pushMessage(RoleType.Assistant, `请提供以下信息:${result.join('、')}`, '') // 上下文携带完整对话 aiHelper.pushPrompt(userInput, 'user') - aiHelper.pushPrompt(`请提供以下信息:${result.join('、')}`, 'system') + aiHelper.pushPrompt(`请提供以下信息:${result.join('、')}`, 'assistant') } else { // 返回loading message currentMessage.value = pushMessage(RoleType.Assistant, '正在努力生成中,请稍等', MessageStatus.Loading) diff --git a/lib/server/controller/mock-data.js b/lib/server/controller/mock-data.js index 39c91970a..44d4a66e8 100644 --- a/lib/server/controller/mock-data.js +++ b/lib/server/controller/mock-data.js @@ -18,12 +18,6 @@ import { const METHODS_WITH_DATA = ['post', 'put', 'patch'] -function strToJson (str) { - // eslint-disable-next-line no-new-func - const json = (new Function('return ' + str))() - return json -} - const Data = { async getApiData (ctx) { try { @@ -49,7 +43,7 @@ const Data = { } // 请求携带的数据 - const httpData = typeof apiData === 'string' ? strToJson(apiData || '{}') : apiData + const httpData = apiData if (METHODS_WITH_DATA.includes(type)) { httpConf.data = httpData } else { diff --git a/lib/server/controller/serverless-web.js b/lib/server/controller/serverless-web.js deleted file mode 100644 index 81399d859..000000000 --- a/lib/server/controller/serverless-web.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community Edition) available. - * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved. - * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://opensource.org/licenses/MIT - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -import { getPreviewDataService } from '../service/business/preview-db-service' -import { - getAllGroupAndFunction -} from '../service/business/function' -import { Controller, All, Ctx } from '../decorator' - -// 用于 serverless web function 在预览环境使用 -@Controller('/api/serverless') -export default class ServerlessController { - @All('/projectId/:projectId/funcCode/:funcCode') - async serverlessWebApi (@Ctx() ctx) { - let dataService - try { - const { projectId, funcCode } = ctx.params || {} - dataService = await getPreviewDataService(projectId) - - const funcGroupList = await getAllGroupAndFunction(projectId) - const curFunc = funcGroupList.map(group => group.children).flat().find(func => func.funcCode === funcCode) - const funcBody = curFunc.funcBody || '' - - const Fn = Function - const asyncFunction = new Fn('return Object.getPrototypeOf(async function(){}).constructor')() - return await asyncFunction('ctx', 'dataService', funcBody)(ctx, dataService) - } catch (error) { - throw error - } finally { - if (dataService) await dataService.close() - } - } -} diff --git a/lib/server/project-template/vue2/project-init-code/lib/server/controller/mock-data.js b/lib/server/project-template/vue2/project-init-code/lib/server/controller/mock-data.js index e6346d56c..be682c291 100644 --- a/lib/server/project-template/vue2/project-init-code/lib/server/controller/mock-data.js +++ b/lib/server/project-template/vue2/project-init-code/lib/server/controller/mock-data.js @@ -9,12 +9,6 @@ import { const METHODS_WITH_DATA = ['post', 'put', 'patch'] -function strToJson (str) { - // eslint-disable-next-line no-new-func - const json = (new Function('return ' + str))() - return json -} - @Controller('/api/data') export default class DataController { @OutputJson({ decorator: false }) @@ -46,7 +40,7 @@ export default class DataController { } // 请求携带的数据 - const httpData = typeof apiData === 'string' ? strToJson(apiData || '{}') : apiData + const httpData = apiData if (METHODS_WITH_DATA.includes(type)) { httpConf.data = httpData } else { diff --git a/lib/server/project-template/vue2/project-init-code/lib/server/service/data-service.js b/lib/server/project-template/vue2/project-init-code/lib/server/service/data-service.js index c17753a20..1f879aac7 100644 --- a/lib/server/project-template/vue2/project-init-code/lib/server/service/data-service.js +++ b/lib/server/project-template/vue2/project-init-code/lib/server/service/data-service.js @@ -279,7 +279,7 @@ export function getDataService (name = 'default', customEntityMap) { * @returns 新增结果 */ async bulkAdd (tableFileName, dataList) { - dataList.forEach(item =>{ + dataList.forEach(item => { item.id = null }) const repository = getRepositoryByName(tableFileName) diff --git a/lib/server/project-template/vue3/project-init-code/lib/server/controller/mock-data.js b/lib/server/project-template/vue3/project-init-code/lib/server/controller/mock-data.js index e6346d56c..be682c291 100644 --- a/lib/server/project-template/vue3/project-init-code/lib/server/controller/mock-data.js +++ b/lib/server/project-template/vue3/project-init-code/lib/server/controller/mock-data.js @@ -9,12 +9,6 @@ import { const METHODS_WITH_DATA = ['post', 'put', 'patch'] -function strToJson (str) { - // eslint-disable-next-line no-new-func - const json = (new Function('return ' + str))() - return json -} - @Controller('/api/data') export default class DataController { @OutputJson({ decorator: false }) @@ -46,7 +40,7 @@ export default class DataController { } // 请求携带的数据 - const httpData = typeof apiData === 'string' ? strToJson(apiData || '{}') : apiData + const httpData = apiData if (METHODS_WITH_DATA.includes(type)) { httpConf.data = httpData } else { diff --git a/lib/server/service/common/ai.js b/lib/server/service/common/ai.js index 8eed5c6a7..e83487ea8 100644 --- a/lib/server/service/common/ai.js +++ b/lib/server/service/common/ai.js @@ -25,11 +25,17 @@ export const createChat = (bkToken) => { path: '/aidev/intelligence/chat_completion/', method: 'post', data: { + version: 'v2', session_id: 'lesscodeAiSession', variables: {}, session_history: [], llm: process.env.BK_LLM, app_collection: 'default_chat_completion' + }, + config: { + headers: { + 'aidev-ticket': bkToken + } } }) } @@ -42,6 +48,7 @@ export const createChat = (bkToken) => { */ export const prompt = async (bkToken, prompts) => { const data = { + version: 'v2', session_id: 'lesscodeAiSession', session_history: prompts.slice(0, -1), chat_prompts: prompts.slice(-1), @@ -59,7 +66,12 @@ export const prompt = async (bkToken, prompts) => { apiName: 'bkaidev', path: `/aidev/intelligence/chat_completion/${data.session_id}/`, method: 'patch', - data + data, + config: { + headers: { + 'aidev-ticket': bkToken + } + } }) } @@ -72,6 +84,7 @@ export const prompt = async (bkToken, prompts) => { */ export const streamPrompt = (bkToken, prompts) => { const data = { + version: 'v2', session_id: 'lesscodeAiSession', session_history: prompts.slice(0, -1), chat_prompts: prompts.slice(-1), @@ -94,7 +107,10 @@ export const streamPrompt = (bkToken, prompts) => { method: 'patch', data, config: { - responseType: 'stream' + responseType: 'stream', + headers: { + 'aidev-ticket': bkToken + } } }) } diff --git a/lib/server/service/common/db-engine-service.js b/lib/server/service/common/db-engine-service.js index f83cb0d52..047982092 100644 --- a/lib/server/service/common/db-engine-service.js +++ b/lib/server/service/common/db-engine-service.js @@ -48,9 +48,9 @@ class DBEngineService { * @param sql 传入sql会执行sql字符串 * @returns 执行结果 Array */ - async execSql (sql) { + async execSql (sql, params = []) { const pool = this.getPoolPromise() - const [res] = await pool.query(sql) + const [res] = await pool.query(sql, params) this.close() return res } @@ -60,7 +60,7 @@ class DBEngineService { * 自动嵌套事务 * @param sqls */ - async execMultSql (sqls) { + async execMultSql (sqls, params = []) { // build sql array const sqlArr = util.splitSql(sqls) // connect and exec sql @@ -69,8 +69,8 @@ class DBEngineService { try { // 嵌套事务 await pool.query('START TRANSACTION;') - for (const sql of sqlArr) { - const [execResult] = await pool.query(sql) + for (let i = 0; i < sqlArr.length; i++) { + const [execResult] = await pool.query(sqlArr[i], params[i] || []) res.push(execResult) } await pool.query('COMMIT;') diff --git a/lib/server/service/common/online-db-service.js b/lib/server/service/common/online-db-service.js index 2c65672b9..f383d8907 100644 --- a/lib/server/service/common/online-db-service.js +++ b/lib/server/service/common/online-db-service.js @@ -12,7 +12,7 @@ import { MIGRATION_TABLE_NAME } from '../../../shared/data-source' import v3Config from '../../conf/v3' import http from '../../utils/http' import { LCDataService, TABLE_FILE_NAME } from './data-service' - +import { escapeTableName } from '../../../shared/util' /** * 操作线上环境数据库服务 */ @@ -34,19 +34,20 @@ export default class OnlineService { // 展示表详情 async describeTables (tableNames, database) { - const nameSqlFilter = tableNames.join('\',\'') + const placeholders = tableNames.map(() => '?').join(',') + const escapedTableNames = tableNames.map(escapeTableName) const columnSql = ` SELECT * FROM INFORMATION_SCHEMA.TABLES t LEFT JOIN INFORMATION_SCHEMA.COLUMNS c ON t.TABLE_NAME = c.TABLE_NAME - WHERE t.TABLE_NAME IN ('${nameSqlFilter}') AND t.TABLE_SCHEMA = '${database}' AND c.TABLE_SCHEMA = '${database}' + WHERE t.TABLE_NAME IN (${placeholders}) AND t.TABLE_SCHEMA = ? AND c.TABLE_SCHEMA = ? ORDER BY t.CREATE_TIME DESC, c.ORDINAL_POSITION ASC; ` const indexSql = ` - SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME IN ('${nameSqlFilter}'); + SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME IN (${placeholders}); ` - const columnsList = await this.dbEngine.execSql(columnSql) - const indexList = await this.dbEngine.execSql(indexSql) + const columnsList = await this.dbEngine.execSql(columnSql, [escapedTableNames, database, database]) + const indexList = await this.dbEngine.execSql(indexSql, [escapedTableNames]) const tableList = columnsList.reduce((acc, cur) => { if (cur.TABLE_NAME === MIGRATION_TABLE_NAME) return acc @@ -124,12 +125,34 @@ export default class OnlineService { // 分页获取表数据 async getTableData (tableName, page, pageSize, filterKey, filterValue, sortKey, sortValue) { - const where = `${filterKey && filterValue ? `WHERE \`${filterKey}\` LIKE '%${filterValue}%'` : ''}` - const orderBy = `ORDER BY ${sortKey || 'id'} ${sortValue || 'DESC'}` - const [list, foundRows = []] = await this.dbEngine.execMultSql(` - SELECT SQL_CALC_FOUND_ROWS * FROM \`${tableName}\` ${where} ${orderBy} LIMIT ${(page - 1) * pageSize},${pageSize}; + const where = filterKey && filterValue ? 'WHERE ?? LIKE ?' : '' + const finalSortKey = sortKey || 'id' + const finalSortValue = (sortValue || 'DESC').toUpperCase() + const orderBy = `ORDER BY ?? ${finalSortValue}` + + if (!['ASC', 'DESC'].includes(finalSortValue)) { + throw new Error('Invalid sort direction') + } + + const sql = ` + SELECT SQL_CALC_FOUND_ROWS * FROM \`${escapeTableName(tableName)}\` ${where} ${orderBy} LIMIT ?, ?; SELECT FOUND_ROWS(); - `) + ` + const params = [ + // 第一条 SQL 的参数 + [ + ...(filterKey && filterValue ? [ + filterKey, // WHERE ?? 的参数 + `%${filterValue}%` // LIKE ? 的参数 + ] : []), + finalSortKey, // ORDER BY ?? 的参数 + (page - 1) * pageSize, // LIMIT 的第一个参数 + +pageSize // LIMIT 的第二个参数 + ], + // 第二条 SQL 不需要参数 + [] + ] + const [list, foundRows = []] = await this.dbEngine.execMultSql(sql, params) const rows = foundRows[0] || {} const count = Object.values(rows)[0] || 0 return { list, count } @@ -137,12 +160,34 @@ export default class OnlineService { // 获取表所有的数据 async getTableAllData (tableName, filterKey, filterValue, sortKey, sortValue) { - const where = `${filterKey && filterValue ? `WHERE \`${filterKey}\` LIKE '%${filterValue}%'` : ''}` - const orderBy = `ORDER BY ${sortKey || 'id'} ${sortValue || 'DESC'}` - const [list, foundRows = []] = await this.dbEngine.execMultSql(` - SELECT SQL_CALC_FOUND_ROWS * FROM \`${tableName}\` ${where} ${orderBy}; + const where = filterKey && filterValue ? 'WHERE ?? LIKE ?' : '' + const finalSortKey = sortKey || 'id' + const finalSortValue = (sortValue || 'DESC').toUpperCase() + const orderBy = `ORDER BY ?? ${finalSortValue}` + + if (!['ASC', 'DESC'].includes(finalSortValue)) { + throw new Error('Invalid sort direction') + } + + const sql = ` + SELECT SQL_CALC_FOUND_ROWS * FROM \`${escapeTableName(tableName)}\` ${where} ${orderBy}; SELECT FOUND_ROWS(); - `) + ` + + const params = [ + // 第一条 SQL 的参数 + [ + ...(filterKey && filterValue ? [ + filterKey, // WHERE ?? 的参数 + `%${filterValue}%` // LIKE ? 的参数 + ] : []), + finalSortKey // ORDER BY ?? 的参数 + ], + // 第二条 SQL 不需要参数 + [] + ] + + const [list, foundRows = []] = await this.dbEngine.execMultSql(sql, params) const rows = foundRows[0] || {} const count = Object.values(rows)[0] || 0 return { list, count } diff --git a/lib/shared/util.js b/lib/shared/util.js index 7d44c4744..4f52d4963 100644 --- a/lib/shared/util.js +++ b/lib/shared/util.js @@ -413,3 +413,8 @@ export const getLesscodeVarCode = (value) => { } return '' } + +// 过滤tablename的非法字符 +export function escapeTableName (tableName) { + return tableName.replace(/[^a-zA-Z0-9_-]/g, '') +} diff --git a/package.json b/package.json index b63332eab..d2c582f9d 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "@babel/plugin-proposal-class-properties": "7.18.6", "@babel/plugin-proposal-private-methods": "7.18.6", "@babel/plugin-proposal-private-property-in-object": "7.21.11", - "@bkui/apigateway-nodejs-sdk": "0.0.1-beta.52", + "@bkui/apigateway-nodejs-sdk": "0.0.2", "@blueking/ai-blueking": "^0.3.18", "@blueking/babel-preset-bk": "^2.1.0-beta.10", "@blueking/bkcharts": "^2.0.11-alpha.5", From 5e93b5128291bac25a94c86228791bc0c30ba0a5 Mon Sep 17 00:00:00 2001 From: xuzhan Date: Tue, 18 Mar 2025 19:34:40 +0800 Subject: [PATCH 04/31] =?UTF-8?q?=E4=BF=AE=E6=94=B9sql=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E7=9A=84=E5=86=99=E6=B3=95=20#=20Reviewed,=20transaction=20id:?= =?UTF-8?q?=2034770?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/server/service/common/online-db-service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server/service/common/online-db-service.js b/lib/server/service/common/online-db-service.js index f383d8907..0a4385c9a 100644 --- a/lib/server/service/common/online-db-service.js +++ b/lib/server/service/common/online-db-service.js @@ -46,8 +46,8 @@ export default class OnlineService { const indexSql = ` SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME IN (${placeholders}); ` - const columnsList = await this.dbEngine.execSql(columnSql, [escapedTableNames, database, database]) - const indexList = await this.dbEngine.execSql(indexSql, [escapedTableNames]) + const columnsList = await this.dbEngine.execSql(columnSql, [...escapedTableNames, database, database]) + const indexList = await this.dbEngine.execSql(indexSql, [...escapedTableNames]) const tableList = columnsList.reduce((acc, cur) => { if (cur.TABLE_NAME === MIGRATION_TABLE_NAME) return acc From 6e1b234e7721b525592d277d088957f242051a50 Mon Sep 17 00:00:00 2001 From: terlinhe Date: Tue, 18 Mar 2025 20:33:41 +0800 Subject: [PATCH 05/31] =?UTF-8?q?feat:=20=E7=94=9F=E6=88=90sql=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data-operation/children/ai-sql-input.vue | 23 ++++++++----------- .../data-operation/children/sql-query.vue | 2 +- .../children/system-sql-prompt.txt | 3 +-- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/lib/client/src/views/project/data-source-manage/data-operation/children/ai-sql-input.vue b/lib/client/src/views/project/data-source-manage/data-operation/children/ai-sql-input.vue index e44832db1..f556987fe 100644 --- a/lib/client/src/views/project/data-source-manage/data-operation/children/ai-sql-input.vue +++ b/lib/client/src/views/project/data-source-manage/data-operation/children/ai-sql-input.vue @@ -73,10 +73,11 @@ if (currentMessage.content?.includes('SELECT')) { currentMessage.content = currentMessage.content?.replace('```sql', '')?.replace('```', '') - currentMessage.status = 'success' + currentMessage.content = currentMessage.content?.trim() if (!currentMessage.content?.endsWith(';')) { currentMessage.content = currentMessage.content + ';' } + currentMessage.status = 'success' props.generateSql(currentMessage.content) } else { currentMessage.status = 'error' @@ -108,30 +109,21 @@ const getTableInfoPrompt = () => { let sysPromt = '' - sysPromt += getTableStructure() - sysPromt += '\n - If you can generate the sql, return the sql only, otherwise, you should return explanation in Chinese' - return sysPromt - } - - const addSqlPrompt = () => { const tables = props.tableData || [] if (tables.length === 0) { - const noDataPrompt = 'There are no data tables in the database, you should return "数据库中没有任何的数据表, 请先创建" now' - aiHelper.pushPrompt(noDataPrompt, 'system') + sysPromt = 'There are no data tables in the database, you should return "数据库中没有任何的数据表, 请先创建" now' } else { let tablePrompt = '' tablePrompt += getTableStructure() tablePrompt += '\n - If you can generate the sql, return the sql only, otherwise, you should return explanation in Chinese' - aiHelper.pushPrompt(tablePrompt, 'system') + sysPromt = tablePrompt } + return sysPromt } const handleUserInput = () => { if (isLoading.value || !content.value) return - // 根据数据库中的数据,动态添加prompt - addSqlPrompt() - // 记录输入的数据 const userInput = content.value pushMessage('user', userInput) @@ -145,12 +137,15 @@ aiHelper.chat(`help me solve this task:\n ${userInput}`) } + // 只能发送一条systemprompt + const tableInfoPrompt = getTableInfoPrompt() + const aiHelper = new Ai({ handlerStart, handleEnd, handleMessage, handleApiError, - systemPrompt, + systemPrompt: systemPrompt + tableInfoPrompt, type: 'sql' }) return { diff --git a/lib/client/src/views/project/data-source-manage/data-operation/children/sql-query.vue b/lib/client/src/views/project/data-source-manage/data-operation/children/sql-query.vue index d54f739f0..cf0608641 100644 --- a/lib/client/src/views/project/data-source-manage/data-operation/children/sql-query.vue +++ b/lib/client/src/views/project/data-source-manage/data-operation/children/sql-query.vue @@ -1,6 +1,6 @@ + diff --git a/lib/client/src/views/project/flow-manage/tpl-edit/config/index.vue b/lib/client/src/views/project/flow-manage/tpl-edit/config/index.vue index c66db25df..a23cb0ad0 100644 --- a/lib/client/src/views/project/flow-manage/tpl-edit/config/index.vue +++ b/lib/client/src/views/project/flow-manage/tpl-edit/config/index.vue @@ -19,6 +19,13 @@ :placeholder="$t('请输入流程模板名称')" :show-word-limit="true" /> + + +

@@ -50,15 +57,20 @@ {{ $t('取消') }} +

- diff --git a/lib/client/src/views/project/flow-manage/tpl-edit/canvas/node-detail/manual-node/form-binding-config/bound-detail.vue b/lib/client/src/views/project/flow-manage/tpl-edit/canvas/node-detail/manual-node/form-binding-config/bound-detail.vue index 037a0e359..4659a6297 100644 --- a/lib/client/src/views/project/flow-manage/tpl-edit/canvas/node-detail/manual-node/form-binding-config/bound-detail.vue +++ b/lib/client/src/views/project/flow-manage/tpl-edit/canvas/node-detail/manual-node/form-binding-config/bound-detail.vue @@ -37,6 +37,7 @@
{ - emit('updateContainerPages', { ...props.containerPages, [type]: val.slice() }) + const handleUpdateContainerPages = (type, { pages, refresh }) => { + emit('updateContainerPages', { + pages: { ...props.containerPages, [type]: pages.slice()}, + refresh + }) } const handleBtnClick = (action) => { diff --git a/lib/client/src/views/project/flow-manage/tpl-edit/canvas/node-detail/manual-node/form-binding-config/page-bound-editor.vue b/lib/client/src/views/project/flow-manage/tpl-edit/canvas/node-detail/manual-node/form-binding-config/page-bound-editor.vue index 1f74aa7d3..e0dc1d67c 100644 --- a/lib/client/src/views/project/flow-manage/tpl-edit/canvas/node-detail/manual-node/form-binding-config/page-bound-editor.vue +++ b/lib/client/src/views/project/flow-manage/tpl-edit/canvas/node-detail/manual-node/form-binding-config/page-bound-editor.vue @@ -1,7 +1,7 @@