This commit is contained in:
2025-12-04 14:22:53 +08:00
parent 37d78c742b
commit 7d8123a76d
12 changed files with 201 additions and 64 deletions

View File

@@ -1,12 +1,11 @@
{
"name": "@kevisual/router-template-server",
"name": "@kevisual/noco-auto-backend",
"version": "0.0.1",
"description": "",
"main": "index.js",
"basename": "/root/router-template-server",
"basename": "/root/noco-auto-backend",
"app": {
"type": "system-app",
"key": "router-template-server",
"entry": "app.js",
"runtime": [
"server"
@@ -15,10 +14,8 @@
"scripts": {
"dev": "bun --watch src/main.ts ",
"build": "pnpm run clean && bun run bun.config.mjs",
"postbuild": "ev pack",
"compile": "bun build --compile ./src/main.ts --outfile router-template",
"compile:win": "bun build --compile ./src/main.ts --target=bun-windows-x64 --outfile router-template.exe",
"clean": "rm -rf dist && rimraf pack-dist",
"prepub": "pnpm build",
"pub": "envision pack -p -u"
},
"files": [
@@ -42,7 +39,7 @@
"nanoid": "^5.1.6"
},
"devDependencies": {
"@kevisual/ai": "^0.0.12",
"@kevisual/ai": "^0.0.15",
"@kevisual/types": "^0.0.10",
"@types/archiver": "^7.0.0",
"@types/bun": "^1.3.3",

View File

@@ -1,9 +1,8 @@
import { app } from '@/app.ts'
import { NocoLifeService } from './services/life.ts';
import { useContextKey } from '@kevisual/context';
import { BailianProvider } from '@kevisual/ai';
import { createLunarDate, LunarDate, toGregorian } from 'lunar';
import dayjs from 'dayjs';
import { useContextKey } from '@kevisual/context';
import { BaseChat, AIUtils } from '@kevisual/ai';
app.route({
path: 'noco-life',
key: 'config-update',
@@ -11,12 +10,25 @@ app.route({
`,
middleware: ['auth']
}).define(async (ctx) => {
const data = ctx.query.data || {};
const token = ctx.query.token || '';
const question = ctx.query.question || '';
let data = ctx.query.data || {};
if (question) {
const ai: BaseChat = useContextKey('ai');
await ai.chat([
{ role: 'system', content: `你是一个多维表格配置助理,你的任务是帮助用户解析多维表格的配置信息。用户会提供配置信息,你需要从中提取出 baseURL, token, baseId, tableId 等字段,并以 JSON 格式返回。如果某个字段缺失,可以不返回该字段。请确保返回的 JSON 格式正确且易于解析。` },
{ role: 'user', content: question }
])
let msg = AIUtils.extractJsonFromMarkdown(ai.responseText || '');
if (msg == null) {
ctx.throw(500, 'AI 返回结果解析失败');
}
data = msg;
}
if (!data?.baseURL || !data?.token || !data?.baseId) {
ctx.throw(400, '缺少参数 baseURL, token, baseId, tableId');
}
const token = ctx.query.token || '';
const nocoLifeService = new NocoLifeService({ token });
const config = await nocoLifeService.getLifeConfig()
if (data.baseURL) {
@@ -36,7 +48,7 @@ app.route({
if (res.code !== 200) {
ctx.throw(500, '保存配置失败');
}
ctx.body = '配置更新成功';
ctx.body = { content: '配置更新成功' };
}).addTo(app);
@@ -50,5 +62,12 @@ app.route({
const token = ctx.query.token || '';
const nocoLifeService = new NocoLifeService({ token });
const config = await nocoLifeService.getLifeConfig()
ctx.body = config;
ctx.body = {
data: config,
content: `当前多维表格配置如下:
Base URL: ${config.baseURL}
Token: ${config.token}
Base ID: ${config.baseId}
Table ID: ${config.tableId || '未设置'}`
};
}).addTo(app);

View File

@@ -1,8 +1,9 @@
import { app } from '@/app.ts'
import { NocoLifeService } from './services/life.ts';
import { useContextKey } from '@kevisual/context';
import { BailianProvider } from '@kevisual/ai';
import { createLunarDate, LunarDate, toGregorian } from 'lunar';
import { BaseChat } from '@kevisual/ai';
import { AIUtils } from '@kevisual/ai';
import { createLunarDate, toGregorian } from 'lunar';
import dayjs from 'dayjs';
app.route({
path: 'noco-life',
@@ -15,27 +16,43 @@ app.route({
ctx.throw(400, '缺少参数 question');
}
const token = ctx.query.token || '';
if (question.startsWith('配置多维表格')) {
const res = await ctx.call({
path: 'noco-life',
key: 'config-update',
token: token,
payload: { question }
})
ctx.body = res.body;
return;
}
const nocoLifeService = new NocoLifeService({ token });
await nocoLifeService.initConfig()
const routes = ctx.queryRouter.getList().filter(r => r.path.startsWith('noco-life') && r.key !== 'chat');
const v = `${routes.map((r, index) => `${index + 1}工具名称: ${r.id}\n描述: ${r.description}\n`).join('\n')}\n\n当用户询问时如果拥有工具请返回 JSON 数据不存在工具则返回分析判断数据JSON数据类型是{id,payload}外面的id是工具的id。如果工具有参数在 payload 当中默认不需要参数如果工具内部需要id在payload当中。`
const ai = useContextKey('ai');
const ai: BaseChat = useContextKey('ai');
const answer = await ai.chat([
{ role: 'system', content: `你是一个多维表格助理,你的任务是帮助用户操作和查询多维表格的数据。你可以使用以下工具来完成任务:\n\n${v}` },
{ role: 'user', content: question }
])
console.log('answer', ai.responseText);
let msg: any;
try {
msg = JSON.parse(ai.responseText || '{}');
} catch (e) {
let msg = AIUtils.extractJsonFromMarkdown(ai.responseText || '');
if (msg == null) {
ctx.throw(500, 'AI 返回结果解析失败');
}
console.log('msg', msg);
const res = await ctx.call(msg);
const route = routes.find(r => r.id === msg.id || r.key === msg.id);
console.log('route============', route.id, route.path, route.key);
const res = await ctx.call({
...msg,
token: token
});
if (res.code !== 200) {
ctx.throw(500, '调用工具失败');
console.log('调用工具失败', res.message);
ctx.throw(500, res.message || '调用工具失败');
}
console.log('con=============', res?.data);
console.log('res', res.code, res.body?.content);
ctx.body = res.body;
}).addTo(app);
@@ -50,16 +67,30 @@ app.route({
const nocoLifeService = new NocoLifeService({ token });
await nocoLifeService.initConfig()
const life = nocoLifeService.life;
const tomorrow = dayjs().add(1, 'day').startOf('day').toISOString();
const tomorrowDate = dayjs(tomorrow).format('YYYY-MM-DD');
const res = await life.getList({
fields: ['Id', '标题', '总结', '启动时间', '标签', '任务'],
where: "(任务,eq,运行中)~and(启动时间,lt,today)",
where: `(任务,eq,运行中)~and(启动时间,lt,exactDate,${tomorrowDate})`,
// where: "(任务,eq,运行中)~and(启动时间,le,today)",
// where: "(任务,eq,运行中)~and(启动时间,le,daysAgo,-1)",
sort: '启动时间',
});
console.log('today res', res.data?.list?.map(i => i['标题']));
if (res.code === 200) {
const list = res.data.list || []
ctx.body = list;
ctx.body = {
list,
content: list.map(item => {
return `任务: ${item['标题']}[${item['Id']}], 启动时间: ${dayjs(item['启动时间']).format('YYYY-MM-DD HH:mm:ss')}, 标签: ${item['标签'] || '无'} \n总结: ${item['总结'] || '无'}`;
}).join('\n')
};
if (list.length === 0) {
ctx.message = '今天没有需要做的事情,休息一下吧';
ctx.body = {
list,
content: '今天没有需要做的事情了,休息一下吧'
}
}
return;
}
@@ -76,6 +107,7 @@ app.route({
if (!id) {
ctx.throw(400, '缺少参数 id');
}
console.log('id', id);
const token = ctx.query.token || '';
const nocoLifeService = new NocoLifeService({ token });
await nocoLifeService.initConfig()
@@ -148,7 +180,8 @@ ${prompt ? `这是我给你的提示词,帮你更好地理解我的需求:${
ctx.body = {
id,
nextTime,
showCNTime: dayjs(nextTime).format('YYYY-MM-DD HH:mm:ss')
showCNTime: dayjs(nextTime).format('YYYY-MM-DD HH:mm:ss'),
content: `任务 "${record['标题']}" 已标记为完成。下一次运行时间是 ${dayjs(nextTime).format('YYYY-MM-DD HH:mm:ss')}`
};
}).addTo(app);

View File

@@ -25,7 +25,7 @@ export class NocoLifeService {
}
initEnv() {
const config = new QueryConfig({
query: new Query({ url: "https://kevisual.xiongxiao.me/api/router" })
query: new Query({ url: "https://kevisual.cn/api/router" })
});
this.queryConfig = config;
}

11
backend/test/extract.ts Normal file
View File

@@ -0,0 +1,11 @@
import { ai } from './common.ts';
import { AIUtils } from '@kevisual/ai';
const text = `\`\`\`\`json
{
"id": "7y6FHnPSzwhDJGyizsTQU",
"payload": {}
}
\`\`\``
const json = AIUtils.extractJsonFromMarkdown(text);