clear: old code
This commit is contained in:
@@ -1,24 +0,0 @@
|
||||
import { App } from '@kevisual/router';
|
||||
import { sequelize } from './modules/sequelize.ts';
|
||||
import { emitter } from './modules/event.ts';
|
||||
export { sequelize };
|
||||
export const app = new App();
|
||||
|
||||
export const runAppRouterFn = async (key: string, params: any) => {
|
||||
emitter.emit(`router.fn`, key, params);
|
||||
};
|
||||
const runListener = async (app: App) => {
|
||||
emitter.on('router.fn', (key, params) => {
|
||||
if (!app.router[key]) {
|
||||
console.error('router key not found:', key);
|
||||
} else {
|
||||
app.router[key](params);
|
||||
}
|
||||
});
|
||||
};
|
||||
runListener(app);
|
||||
|
||||
export const appendTo = (realApp: App) => {
|
||||
realApp.importApp(app);
|
||||
runListener(realApp);
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
import { app } from './app.ts';
|
||||
|
||||
new app.Route('admin', 'getRouteList')
|
||||
.define(async (ctx) => {
|
||||
const list = app.router.getList();
|
||||
// ctx.body = list.filter((r) => !r.path.startsWith('admin'));
|
||||
ctx.body = list.map((r) => {
|
||||
return {
|
||||
path: r.path,
|
||||
key: r.key,
|
||||
};
|
||||
});
|
||||
return ctx;
|
||||
})
|
||||
.addTo(app);
|
||||
@@ -1 +0,0 @@
|
||||
export { manager } from './manager.ts';
|
||||
@@ -1,122 +0,0 @@
|
||||
// import { router } from '../../modules/router.ts';
|
||||
import { runAppRouterFn } from '../app.ts';
|
||||
|
||||
import { Route } from '@kevisual/router';
|
||||
import { RouterCodeModel, RouterCode } from '../models/code.ts';
|
||||
|
||||
export enum CodeStatus {
|
||||
running = 'running',
|
||||
stop = 'stop',
|
||||
fail = 'fail',
|
||||
}
|
||||
|
||||
export type CodeManager = {
|
||||
fn?: any;
|
||||
status?: CodeStatus;
|
||||
errorMsg?: string;
|
||||
lock?: boolean; // 是否锁定
|
||||
} & Partial<RouterCode>;
|
||||
|
||||
const codeDemoRun = `async function run(ctx) {
|
||||
ctx.body = 'test js';
|
||||
return ctx;
|
||||
}`;
|
||||
const templateFn = (codeStr: string) => {
|
||||
return `
|
||||
${codeStr}
|
||||
if(run) {
|
||||
return run(ctx);
|
||||
}
|
||||
if(main) {
|
||||
return main(ctx);
|
||||
}
|
||||
return 'no run or main function';
|
||||
`;
|
||||
};
|
||||
export const loadOne = async (item: RouterCodeModel) => {
|
||||
const { path, key, id, code, exec, project, middleware } = item.toJSON();
|
||||
const codeStr = exec || code;
|
||||
try {
|
||||
const fn: any = new Function('ctx', templateFn(codeStr));
|
||||
// run code
|
||||
const codeRunRoute = new Route(path, key, { id });
|
||||
codeRunRoute.run = fn;
|
||||
codeRunRoute.middleware = middleware;
|
||||
runAppRouterFn('removeById', id);
|
||||
runAppRouterFn('add', codeRunRoute);
|
||||
return {
|
||||
...item.toJSON(),
|
||||
path,
|
||||
key,
|
||||
id,
|
||||
project,
|
||||
fn,
|
||||
status: CodeStatus.running,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('error id:', id, '\n', e);
|
||||
return {
|
||||
path,
|
||||
key,
|
||||
id,
|
||||
project,
|
||||
status: CodeStatus.fail,
|
||||
errorMsg: e.message.toString(),
|
||||
};
|
||||
}
|
||||
};
|
||||
export const load = async function () {
|
||||
const codes = await RouterCodeModel.findAll({
|
||||
logging: (sql, timing) => {
|
||||
console.log('manager load database router codeModel');
|
||||
},
|
||||
});
|
||||
const codeManager: CodeManager[] = codes.map((item) => {
|
||||
const { path, key, id, code, exec, project, active, middleware } = item.toJSON();
|
||||
if (!active) {
|
||||
return {
|
||||
...item.toJSON(),
|
||||
path,
|
||||
key,
|
||||
id,
|
||||
code,
|
||||
project,
|
||||
middleware,
|
||||
status: CodeStatus.stop,
|
||||
};
|
||||
}
|
||||
try {
|
||||
const codeStr = exec || code;
|
||||
const fn: any = new Function('ctx', templateFn(codeStr));
|
||||
// run code
|
||||
const codeRunRoute = new Route(path, key, { id });
|
||||
codeRunRoute.run = fn;
|
||||
codeRunRoute.middleware = middleware;
|
||||
runAppRouterFn('add', codeRunRoute);
|
||||
return {
|
||||
...item.toJSON(),
|
||||
path,
|
||||
key,
|
||||
id,
|
||||
code,
|
||||
project,
|
||||
type: item.type,
|
||||
fn,
|
||||
status: CodeStatus.running,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('error id:', id, '\n', e);
|
||||
return {
|
||||
path,
|
||||
key,
|
||||
id,
|
||||
code,
|
||||
project,
|
||||
type: item.type,
|
||||
status: CodeStatus.fail,
|
||||
errorMsg: e.message.toString(),
|
||||
};
|
||||
}
|
||||
});
|
||||
return codeManager;
|
||||
};
|
||||
@@ -1,86 +0,0 @@
|
||||
import stream from 'stream'; // 默认导入整个模块
|
||||
const { once } = stream; // 从中解构出 EventEmitter
|
||||
|
||||
import { load, CodeManager, CodeStatus, loadOne } from './load.ts';
|
||||
import { RouterCodeModel, TableIsExist } from '../models/code.ts';
|
||||
import { emitter } from '../modules/event.ts';
|
||||
|
||||
export enum LoadStatus {
|
||||
LOADING = 'loading',
|
||||
LOADED = 'loaded',
|
||||
ERROR = 'error',
|
||||
}
|
||||
export const manager = {
|
||||
loaded: LoadStatus.LOADING, // 是否已经加载
|
||||
list: [] as CodeManager[],
|
||||
shareLocalList: [] as CodeManager[],
|
||||
};
|
||||
|
||||
// 更新
|
||||
export const updateNewCode = (code: CodeManager) => {
|
||||
const index = manager.list.findIndex((item) => item.id === code.id);
|
||||
if (index === -1) {
|
||||
manager.list.push(code);
|
||||
} else {
|
||||
manager.list[index] = code;
|
||||
}
|
||||
};
|
||||
// 删除
|
||||
export const removeCode = (id: string) => {
|
||||
const index = manager.list.findIndex((item) => item.id === id);
|
||||
if (index !== -1) {
|
||||
manager.list.splice(index, 1);
|
||||
}
|
||||
};
|
||||
export const stopCode = (id: string) => {
|
||||
const index = manager.list.findIndex((item) => item.id === id);
|
||||
if (index !== -1) {
|
||||
manager.list[index].status = CodeStatus.stop;
|
||||
}
|
||||
};
|
||||
export const startCode = async (code: RouterCodeModel) => {
|
||||
const index = manager.list.findIndex((item) => item.id === code.id);
|
||||
console.log('index', index, code.toJSON());
|
||||
if (index !== -1) {
|
||||
manager.list[index] = await loadOne(code);
|
||||
} else {
|
||||
const codeManger = await loadOne(code);
|
||||
manager.list.push(codeManger);
|
||||
}
|
||||
};
|
||||
|
||||
once(emitter, 'loaded')
|
||||
.then(() => {
|
||||
manager.loaded = LoadStatus.LOADED;
|
||||
console.log('manager loaded');
|
||||
})
|
||||
.catch((e) => {
|
||||
manager.loaded = LoadStatus.ERROR;
|
||||
console.error('manager loaded error', e);
|
||||
});
|
||||
|
||||
const init = async function () {
|
||||
const r = await load();
|
||||
manager.list = r;
|
||||
|
||||
emitter.emit('loaded');
|
||||
};
|
||||
TableIsExist().then(async (res) => {
|
||||
if (res) {
|
||||
init();
|
||||
} else {
|
||||
console.log('TableIsExist not exist, waiting create');
|
||||
// 3s后再次检测
|
||||
setTimeout(() => {
|
||||
TableIsExist().then(async (res) => {
|
||||
if (res) {
|
||||
init();
|
||||
} else {
|
||||
console.error('TableIsExist not exist, create error');
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
}, 3000);
|
||||
}
|
||||
});
|
||||
// init();
|
||||
@@ -1,7 +0,0 @@
|
||||
import './router.ts';
|
||||
import './manager.ts';
|
||||
import './npm.ts';
|
||||
import './core.ts';
|
||||
import { app, appendTo } from './app.ts';
|
||||
|
||||
export { app, appendTo };
|
||||
@@ -1,58 +0,0 @@
|
||||
// admin 需要最后运行,并在route中进行过滤。
|
||||
import { Route } from '@kevisual/router';
|
||||
import { app } from './app.ts';
|
||||
import { manager, updateNewCode, removeCode, stopCode, startCode } from './dashboard/manager.ts';
|
||||
// get manager status
|
||||
export const managerRouter = new Route('admin', 'getManagerStatus');
|
||||
managerRouter.run = async (ctx) => {
|
||||
ctx.body = {
|
||||
status: manager.loaded,
|
||||
msg: 'system is running, and load manager success.',
|
||||
};
|
||||
return ctx;
|
||||
};
|
||||
managerRouter.addTo(app);
|
||||
|
||||
// get manager list
|
||||
app
|
||||
.route({
|
||||
path: 'admin',
|
||||
key: 'getManagerList',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
ctx.body = manager.list;
|
||||
// TODO: routerList 可以不暴露
|
||||
const routerList = ctx.queryRouter.getList().filter((r) => !r.path.startsWith('admin'));
|
||||
ctx.body = {
|
||||
list: manager.list,
|
||||
routerList,
|
||||
};
|
||||
return ctx;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
// get manager one
|
||||
export const managerOne = new Route('admin', 'getManagerOne');
|
||||
managerOne.run = async (ctx) => {
|
||||
const verfiy = ctx.currentRoute.verify(ctx);
|
||||
if (verfiy) return;
|
||||
|
||||
const { id } = ctx.query;
|
||||
const code = manager.list.find((c) => c.id === id);
|
||||
if (code) {
|
||||
ctx.body = code;
|
||||
} else {
|
||||
ctx.body = 'not found';
|
||||
ctx.code = 404;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
};
|
||||
managerOne.validator = {
|
||||
id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
|
||||
managerOne.addTo(app);
|
||||
@@ -1,121 +0,0 @@
|
||||
import { sequelize } from '../modules/sequelize.ts';
|
||||
import { DataTypes, Model } from 'sequelize';
|
||||
|
||||
export type RouterCode = {
|
||||
id: string;
|
||||
path: string;
|
||||
key: string;
|
||||
active: boolean;
|
||||
project: string;
|
||||
code: string;
|
||||
exec: string;
|
||||
type: RouterCodeType;
|
||||
middleware: string[];
|
||||
next: string;
|
||||
data: any;
|
||||
validator: any;
|
||||
};
|
||||
|
||||
export enum RouterCodeType {
|
||||
route = 'route',
|
||||
middleware = 'middleware',
|
||||
}
|
||||
|
||||
export class RouterCodeModel extends Model {
|
||||
declare id: string;
|
||||
declare path: string;
|
||||
declare key: string;
|
||||
declare active: boolean;
|
||||
declare project: string;
|
||||
declare code: string;
|
||||
declare exec: string;
|
||||
declare type: RouterCodeType;
|
||||
declare middleware: string[];
|
||||
declare next: string; // 如果是中间件,不存在
|
||||
declare data: any; // 内容
|
||||
declare validator: any;
|
||||
}
|
||||
RouterCodeModel.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
comment: 'code id',
|
||||
},
|
||||
path: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
key: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
active: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false,
|
||||
},
|
||||
project: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: 'default',
|
||||
},
|
||||
code: {
|
||||
type: DataTypes.TEXT,
|
||||
defaultValue: '',
|
||||
},
|
||||
exec: {
|
||||
type: DataTypes.TEXT, // 对代码进行编译后的代码
|
||||
defaultValue: '',
|
||||
},
|
||||
type: {
|
||||
type: DataTypes.ENUM(RouterCodeType.route, RouterCodeType.middleware),
|
||||
defaultValue: RouterCodeType.route,
|
||||
},
|
||||
middleware: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: [],
|
||||
},
|
||||
next: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: {},
|
||||
},
|
||||
data: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: {},
|
||||
},
|
||||
validator: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: 'cf_router_code', // container flow router code
|
||||
paranoid: true,
|
||||
},
|
||||
);
|
||||
// RouterCodeModel 检测不存在,不存在则创建
|
||||
RouterCodeModel.sync({ alter: true, logging: false })
|
||||
.then((res) => {
|
||||
console.log('RouterCodeModel sync', res);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('RouterCodeModel sync', e.message);
|
||||
if (!TableIsExist()) {
|
||||
RouterCodeModel.sync({ force: true })
|
||||
.then((res) => {
|
||||
console.log('RouterCodeModel force sync', res);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('RouterCodeModel force sync error');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const TableIsExist = async () => {
|
||||
// 判断cf_router_code表是否存在
|
||||
const tableIsExist = await sequelize.getQueryInterface().showAllTables({
|
||||
logging: false,
|
||||
});
|
||||
return tableIsExist.includes('cf_router_code');
|
||||
};
|
||||
@@ -1,66 +0,0 @@
|
||||
import { sequelize } from '../modules/sequelize.ts';
|
||||
import { DataTypes, Model } from 'sequelize';
|
||||
|
||||
type PageNodeData = {
|
||||
id: string;
|
||||
type: string;
|
||||
data: {
|
||||
label?: string; // 容器 开始 结束
|
||||
// 容器上的属性
|
||||
cid?: string; // 容器code id
|
||||
};
|
||||
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export interface RouterGraphData {
|
||||
edges: any[];
|
||||
nodes: PageNodeData[];
|
||||
viewport: any;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面数据
|
||||
*/
|
||||
export class RouterGraphModel extends Model {
|
||||
declare id: string;
|
||||
declare title: string;
|
||||
declare description: string;
|
||||
declare type: string;
|
||||
declare data: RouterGraphData;
|
||||
}
|
||||
RouterGraphModel.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
comment: 'id',
|
||||
},
|
||||
title: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: '',
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.TEXT,
|
||||
defaultValue: '',
|
||||
},
|
||||
type: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: '',
|
||||
},
|
||||
data: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: 'cf_router_graph',
|
||||
paranoid: true,
|
||||
},
|
||||
);
|
||||
RouterGraphModel.sync({ alter: true, logging: false }).catch((e) => {
|
||||
console.error('RouterGraphModel sync', e);
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import stream from 'stream'; // 默认导入整个模块
|
||||
const { EventEmitter, once } = stream; // 从中解构出 EventEmitter
|
||||
|
||||
// 事件
|
||||
export const emitter = new EventEmitter();
|
||||
type EmitterType = typeof emitter;
|
||||
// 异步触发事件 demo
|
||||
export const asyncEmit = (emitter: EmitterType, eventName: string) => {
|
||||
return new Promise((resolve) => {
|
||||
emitter.once(eventName, (value: any) => resolve(value));
|
||||
});
|
||||
};
|
||||
|
||||
async function main() {
|
||||
setTimeout(() => {
|
||||
emitter.emit('asyncEvent', '监听器中的值');
|
||||
}, 1000);
|
||||
asyncEmit(emitter, 'asyncEvent').then((result) => {
|
||||
console.log(result); // 监听器中的值
|
||||
});
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { useConfig } from '@kevisual/use-config';
|
||||
import { Sequelize } from 'sequelize';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import fs from 'fs';
|
||||
|
||||
const checkFileExistsSync = (filePath: string) => {
|
||||
try {
|
||||
fs.accessSync(filePath, fs.constants.F_OK);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const config = useConfig<{ flowPath: string }>();
|
||||
export const envisionPath = path.join(os.homedir(), '.config', 'envision');
|
||||
const configPath = path.join(os.homedir(), '.config', 'envision', 'db.sqlite');
|
||||
|
||||
if (!checkFileExistsSync(envisionPath)) {
|
||||
fs.mkdirSync(envisionPath, { recursive: true });
|
||||
}
|
||||
let flowPath = config.flowPath || configPath;
|
||||
if (!path.isAbsolute(flowPath)) {
|
||||
flowPath = path.join(process.cwd(), flowPath);
|
||||
}
|
||||
if (!flowPath.endsWith('.sqlite')) {
|
||||
flowPath = path.join(flowPath, 'db.sqlite');
|
||||
}
|
||||
// connect to db
|
||||
export const sequelize = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: flowPath,
|
||||
// logging: false,
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Route } from '@kevisual/router';
|
||||
import { app } from './app.ts';
|
||||
import { getPackage, installPackage } from '../lib/npm.ts';
|
||||
|
||||
const install = new Route('admin', 'install');
|
||||
install.run = async (ctx) => {
|
||||
const { packageName } = ctx.query;
|
||||
const data = await installPackage(packageName);
|
||||
ctx.body = data;
|
||||
return ctx;
|
||||
};
|
||||
install.validator = {
|
||||
packageName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
install.addTo(app);
|
||||
|
||||
const getNpm = new Route('admin', 'getNpm');
|
||||
getNpm.run = async (ctx) => {
|
||||
const data = await getPackage();
|
||||
ctx.body = data['dependencies'];
|
||||
return ctx;
|
||||
};
|
||||
getNpm.addTo(app);
|
||||
@@ -1,201 +0,0 @@
|
||||
// admin router manger
|
||||
import { CustomError, Route } from '@kevisual/router';
|
||||
import { app, runAppRouterFn } from './app.ts';
|
||||
import { manager, updateNewCode, removeCode, stopCode, startCode } from './dashboard/manager.ts';
|
||||
import { loadOne } from './dashboard/load.ts';
|
||||
import { RouterCodeModel } from './models/code.ts';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { convertTsToJs as transform } from '../lib/ts2js.ts';
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'admin',
|
||||
key: 'getRouterList',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
// TODO: routerList 可以不暴露
|
||||
ctx.body = ctx.queryRouter.getList().filter((r) => !r.path.startsWith('admin'));
|
||||
// ctx.body = router.getList().filter((r) => r.path.startsWith('admin'));
|
||||
return ctx;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
// remove router
|
||||
export const removeRouter = new Route('admin', 'removeRouter');
|
||||
removeRouter.run = async (ctx) => {
|
||||
const { path, key } = ctx.query;
|
||||
runAppRouterFn('remove', { path, key });
|
||||
const routerCode = await RouterCodeModel.findOne({ where: { path, key } });
|
||||
if (routerCode) {
|
||||
const id = routerCode.id;
|
||||
removeCode(id);
|
||||
await RouterCodeModel.destroy({ where: { id } });
|
||||
}
|
||||
ctx.body = 'success';
|
||||
return ctx;
|
||||
};
|
||||
removeRouter.validator = {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
key: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
removeRouter.addTo(app);
|
||||
// remove router by id
|
||||
export const removeRouterById = new Route('admin', 'removeRouterById');
|
||||
removeRouterById.run = async (ctx) => {
|
||||
const { id } = ctx.query;
|
||||
app.router.removeById(id);
|
||||
runAppRouterFn('removeById', id);
|
||||
removeCode(id);
|
||||
await RouterCodeModel.destroy({ where: { id } });
|
||||
ctx.body = 'success';
|
||||
return ctx;
|
||||
};
|
||||
removeRouterById.validator = {
|
||||
id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
removeRouterById.addTo(app);
|
||||
// stop router by id
|
||||
export const stopRouterById = new Route('admin', 'stopRouterById');
|
||||
stopRouterById.run = async (ctx) => {
|
||||
const { id } = ctx.query;
|
||||
runAppRouterFn('removeById', id);
|
||||
const routerCode = await RouterCodeModel.findByPk(id);
|
||||
if (routerCode) {
|
||||
routerCode.active = false;
|
||||
await routerCode.save();
|
||||
}
|
||||
stopCode(id);
|
||||
ctx.body = 'success';
|
||||
return ctx;
|
||||
};
|
||||
stopRouterById.validator = {
|
||||
id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
stopRouterById.addTo(app);
|
||||
|
||||
// start router by id
|
||||
export const startRouterById = new Route('admin', 'startRouterById');
|
||||
startRouterById.run = async (ctx) => {
|
||||
const { id } = ctx.query;
|
||||
const routerCode = await RouterCodeModel.findByPk(id);
|
||||
console.log('routerCode', id, routerCode.toJSON());
|
||||
if (routerCode) {
|
||||
routerCode.active = true;
|
||||
await routerCode.save();
|
||||
startCode(routerCode);
|
||||
}
|
||||
ctx.body = 'success';
|
||||
return ctx;
|
||||
};
|
||||
startRouterById.validator = {
|
||||
id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
startRouterById.addTo(app);
|
||||
|
||||
// add or update router
|
||||
export const updateRouter = new Route('admin', 'updateRouter');
|
||||
updateRouter.run = async (ctx) => {
|
||||
let { path, key, id, code, middleware, type = 'route' } = ctx.query;
|
||||
if (!path && !key) {
|
||||
ctx.body = 'path and key is required';
|
||||
ctx.code = 500;
|
||||
return ctx;
|
||||
}
|
||||
let codeRouter: RouterCodeModel | null = null;
|
||||
const codeRouteCheck = await RouterCodeModel.findOne({ where: { path, key } }); // 检查是否存在
|
||||
if (codeRouteCheck && codeRouteCheck.id !== id) {
|
||||
key = `${key}-${nanoid(6)}`;
|
||||
}
|
||||
|
||||
if (id) {
|
||||
codeRouter = await RouterCodeModel.findByPk(id);
|
||||
codeRouter.path = path;
|
||||
codeRouter.key = key;
|
||||
codeRouter.code = code;
|
||||
codeRouter.middleware = middleware;
|
||||
try {
|
||||
codeRouter.exec = await transform(code);
|
||||
} catch (e) {
|
||||
ctx.body = e.message.toString();
|
||||
ctx.code = 500;
|
||||
return ctx;
|
||||
}
|
||||
codeRouter.type = type;
|
||||
await codeRouter.save();
|
||||
} else {
|
||||
try {
|
||||
const exec = await transform(code);
|
||||
const newCodeRouter = new RouterCodeModel({ path, key, code, exec, type, middleware });
|
||||
await newCodeRouter.save();
|
||||
codeRouter = newCodeRouter;
|
||||
} catch (e) {
|
||||
console.error('updateRouter', e);
|
||||
throw new CustomError(e.message.toString());
|
||||
}
|
||||
}
|
||||
|
||||
const codeOne = await loadOne(codeRouter);
|
||||
updateNewCode(codeOne);
|
||||
|
||||
ctx.body = 'success';
|
||||
return ctx;
|
||||
};
|
||||
updateRouter.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'admin',
|
||||
key: 'getRouterApi',
|
||||
description: 'get all router api list, and you can use this api to get router detail by path and key',
|
||||
validator: {
|
||||
origin: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { origin = 'http://localhost:4000' } = ctx.query;
|
||||
// const routerList = router.getList();
|
||||
// TODO: routerList 可以不暴露
|
||||
const routerList = ctx.queryRouter.getList();
|
||||
const apiList = routerList.map((item: any) => {
|
||||
return {
|
||||
path: item.path,
|
||||
key: item.key,
|
||||
query: `${origin}/api/router?path=${item.path}&key=${item.key}`,
|
||||
description: item.description,
|
||||
validator: item.validator,
|
||||
};
|
||||
});
|
||||
const apiKeyObject = apiList.reduce((pre: any, cur: any) => {
|
||||
pre[cur.key] = {
|
||||
path: cur.path,
|
||||
key: cur.key,
|
||||
description: cur.description || '',
|
||||
validator: cur.validator || {},
|
||||
};
|
||||
return pre;
|
||||
}, {});
|
||||
ctx.body = {
|
||||
list: apiList,
|
||||
keyObject: apiKeyObject,
|
||||
};
|
||||
return ctx;
|
||||
})
|
||||
.addTo(app);
|
||||
@@ -1 +0,0 @@
|
||||
export * from './ts2js.ts';
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import * as babel from '@babel/core';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
/**
|
||||
* 转换 ts 代码为 js 代码
|
||||
* @param tsCode
|
||||
* @returns
|
||||
*/
|
||||
export const convertTsToJs = async (tsCode: string) => {
|
||||
const presetEnv = await import('@babel/preset-env');
|
||||
const presetTypescript = await import('@babel/preset-typescript');
|
||||
|
||||
try {
|
||||
const result = babel.transformSync(tsCode, {
|
||||
presets: [
|
||||
presetTypescript.default,
|
||||
[
|
||||
presetEnv.default,
|
||||
{
|
||||
targets: {
|
||||
node: 20,
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
filename: 'temp.ts',
|
||||
});
|
||||
return result.code;
|
||||
} catch (e) {
|
||||
const message = e.message.split('temp.ts:')[1] || e.message;
|
||||
throw new Error(stripAnsi(message));
|
||||
}
|
||||
};
|
||||
@@ -1,94 +0,0 @@
|
||||
import { sequelize } from '@/modules/sequelize.ts';
|
||||
import { DataTypes, Model } from 'sequelize';
|
||||
|
||||
export class AiAgent extends Model {
|
||||
declare id: string;
|
||||
declare type: string;
|
||||
declare model: string;
|
||||
declare baseUrl: string;
|
||||
declare apiKey: string;
|
||||
declare temperature: number;
|
||||
declare cache: string;
|
||||
declare cacheName: string;
|
||||
declare status: string;
|
||||
declare data: any;
|
||||
declare description: string;
|
||||
declare key: string;
|
||||
}
|
||||
|
||||
// 获取AIAgent的属性
|
||||
export type AiProperties = {
|
||||
id: string;
|
||||
type: string;
|
||||
model: string;
|
||||
baseUrl: string;
|
||||
apiKey?: string;
|
||||
temperature?: number;
|
||||
cache?: string;
|
||||
cacheName?: string;
|
||||
data?: any;
|
||||
description?: string;
|
||||
};
|
||||
AiAgent.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
},
|
||||
type: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: 'open',
|
||||
},
|
||||
model: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
baseUrl: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
apiKey: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
key: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
},
|
||||
temperature: {
|
||||
type: DataTypes.FLOAT,
|
||||
allowNull: true,
|
||||
},
|
||||
cache: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
cacheName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
data: {
|
||||
type: DataTypes.JSON,
|
||||
allowNull: true,
|
||||
defaultValue: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: 'ai_agent',
|
||||
paranoid: true,
|
||||
},
|
||||
);
|
||||
AiAgent.sync({ alter: true, logging: false }).catch((e) => {
|
||||
console.error('AiAgent sync error', e);
|
||||
});
|
||||
@@ -1,66 +0,0 @@
|
||||
import { chat } from '@/modules/ollama.ts';
|
||||
import { sequelize } from '../modules/sequelize.ts';
|
||||
import { DataTypes, Model } from 'sequelize';
|
||||
|
||||
/**
|
||||
* chat 回话记录
|
||||
* 有一些内容是预置的。
|
||||
*/
|
||||
export class ChatHistory extends Model {
|
||||
declare id: string;
|
||||
declare data: string;
|
||||
declare root: boolean;
|
||||
declare show: boolean;
|
||||
declare uid: string;
|
||||
declare chatId: string;
|
||||
declare chatPromptId: string;
|
||||
declare role: string;
|
||||
}
|
||||
|
||||
ChatHistory.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
},
|
||||
data: {
|
||||
type: DataTypes.JSON,
|
||||
allowNull: true,
|
||||
},
|
||||
chatId: {
|
||||
type: DataTypes.UUID, // 历史属于哪一条会话
|
||||
allowNull: true,
|
||||
},
|
||||
chatPromptId: {
|
||||
type: DataTypes.UUID, // 属于哪一个prompt
|
||||
allowNull: true,
|
||||
},
|
||||
root: {
|
||||
type: DataTypes.BOOLEAN, // 是否是根节点
|
||||
defaultValue: false,
|
||||
},
|
||||
role: {
|
||||
type: DataTypes.STRING, // 角色
|
||||
allowNull: true,
|
||||
defaultValue: 'user',
|
||||
},
|
||||
show: {
|
||||
type: DataTypes.BOOLEAN, // 当创建返回的时候,配置是否显示
|
||||
defaultValue: true,
|
||||
},
|
||||
uid: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize, // 传入 Sequelize 实例
|
||||
modelName: 'chat_history', // 模型名称
|
||||
},
|
||||
);
|
||||
|
||||
// force 只能run一次,否则会删除表
|
||||
ChatHistory.sync({ alter: true, logging: false }).catch((e) => {
|
||||
console.error('History sync error', e);
|
||||
});
|
||||
@@ -1,64 +0,0 @@
|
||||
import { sequelize } from '../modules/sequelize.ts';
|
||||
import { DataTypes, Model } from 'sequelize';
|
||||
import { Variable } from '@kevisual/ai-graph';
|
||||
|
||||
export type ChatPromptData = {
|
||||
// 使用那个agent, 必须要有
|
||||
aiAgentId: string;
|
||||
// 使用那个初始化的prompt,如果不存在则纯粹的白对话。
|
||||
promptId?: string;
|
||||
chainPromptId?: string;
|
||||
};
|
||||
/**
|
||||
* chat绑定就的agent和prompt
|
||||
* 有一些内容是预置的。
|
||||
*/
|
||||
export class ChatPrompt extends Model {
|
||||
declare id: string;
|
||||
declare title: string;
|
||||
declare description: string;
|
||||
declare uid: string;
|
||||
declare key: string;
|
||||
declare data: ChatPromptData;
|
||||
}
|
||||
|
||||
ChatPrompt.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
},
|
||||
title: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
},
|
||||
data: {
|
||||
type: DataTypes.JSON,
|
||||
allowNull: true,
|
||||
},
|
||||
key: {
|
||||
type: DataTypes.STRING, // 页面属于 /container/edit/list
|
||||
allowNull: false,
|
||||
defaultValue: '',
|
||||
},
|
||||
uid: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize, // 传入 Sequelize 实例
|
||||
modelName: 'chat_prompt', // 模型名称
|
||||
paranoid: true,
|
||||
},
|
||||
);
|
||||
|
||||
// force 只能run一次,否则会删除表
|
||||
ChatPrompt.sync({ alter: true, logging: false }).catch((e) => {
|
||||
console.error('Prompt sync error', e);
|
||||
});
|
||||
@@ -1,61 +0,0 @@
|
||||
import { sequelize } from '../modules/sequelize.ts';
|
||||
import { DataTypes, Model } from 'sequelize';
|
||||
|
||||
/**
|
||||
* chat 回话记录
|
||||
* 有一些内容是预置的。
|
||||
*/
|
||||
export class ChatSession extends Model {
|
||||
declare id: string;
|
||||
declare data: string;
|
||||
declare chatPromptId: string;
|
||||
declare type: string;
|
||||
declare key: string;
|
||||
declare title: string;
|
||||
declare uid: string;
|
||||
}
|
||||
|
||||
ChatSession.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
},
|
||||
data: {
|
||||
type: DataTypes.JSON,
|
||||
allowNull: true,
|
||||
defaultValue: {},
|
||||
},
|
||||
chatPromptId: {
|
||||
type: DataTypes.UUID, // 属于哪一个prompt
|
||||
allowNull: true,
|
||||
},
|
||||
type: {
|
||||
type: DataTypes.STRING, // 属于测试的,还是正式的
|
||||
defaultValue: 'production',
|
||||
},
|
||||
title: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: '',
|
||||
},
|
||||
key: {
|
||||
type: DataTypes.STRING, // 页面属于 /container/edit/list
|
||||
allowNull: true,
|
||||
},
|
||||
uid: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize, // 传入 Sequelize 实例
|
||||
modelName: 'chat_session', // 模型名称
|
||||
},
|
||||
);
|
||||
|
||||
// force 只能run一次,否则会删除表
|
||||
ChatSession.sync({ alter: true, logging: false }).catch((e) => {
|
||||
console.error('Sessuib sync error', e);
|
||||
});
|
||||
@@ -1,121 +0,0 @@
|
||||
import { sequelize } from '../modules/sequelize.ts';
|
||||
import { DataTypes, Model } from 'sequelize';
|
||||
|
||||
export type RouterCode = {
|
||||
id: string;
|
||||
path: string;
|
||||
key: string;
|
||||
active: boolean;
|
||||
project: string;
|
||||
code: string;
|
||||
exec: string;
|
||||
type: RouterCodeType;
|
||||
middleware: string[];
|
||||
next: string;
|
||||
data: any;
|
||||
validator: any;
|
||||
};
|
||||
|
||||
export enum RouterCodeType {
|
||||
route = 'route',
|
||||
middleware = 'middleware',
|
||||
}
|
||||
|
||||
export class RouterCodeModel extends Model {
|
||||
declare id: string;
|
||||
declare path: string;
|
||||
declare key: string;
|
||||
declare active: boolean;
|
||||
declare project: string;
|
||||
declare code: string;
|
||||
declare exec: string;
|
||||
declare type: RouterCodeType;
|
||||
declare middleware: string[];
|
||||
declare next: string; // 如果是中间件,不存在
|
||||
declare data: any; // 内容
|
||||
declare validator: any;
|
||||
}
|
||||
RouterCodeModel.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
comment: 'code id',
|
||||
},
|
||||
path: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
key: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
active: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false,
|
||||
},
|
||||
project: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: 'default',
|
||||
},
|
||||
code: {
|
||||
type: DataTypes.TEXT,
|
||||
defaultValue: '',
|
||||
},
|
||||
exec: {
|
||||
type: DataTypes.TEXT, // 对代码进行编译后的代码
|
||||
defaultValue: '',
|
||||
},
|
||||
type: {
|
||||
type: DataTypes.ENUM(RouterCodeType.route, RouterCodeType.middleware),
|
||||
defaultValue: RouterCodeType.route,
|
||||
},
|
||||
middleware: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: [],
|
||||
},
|
||||
next: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: {},
|
||||
},
|
||||
data: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: {},
|
||||
},
|
||||
validator: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: 'cf_router_code', // container flow router code
|
||||
paranoid: true,
|
||||
},
|
||||
);
|
||||
// RouterCodeModel 检测不存在,不存在则创建
|
||||
RouterCodeModel.sync({ alter: true, logging: false })
|
||||
.then((res) => {
|
||||
console.log('RouterCodeModel sync', res);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('RouterCodeModel sync', e.message);
|
||||
if (!TableIsExist()) {
|
||||
RouterCodeModel.sync({ force: true })
|
||||
.then((res) => {
|
||||
console.log('RouterCodeModel force sync', res);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('RouterCodeModel force sync error');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const TableIsExist = async () => {
|
||||
// 判断cf_router_code表是否存在
|
||||
const tableIsExist = await sequelize.getQueryInterface().showAllTables({
|
||||
logging: false,
|
||||
});
|
||||
return tableIsExist.includes('cf_router_code');
|
||||
};
|
||||
@@ -1,83 +0,0 @@
|
||||
import { neode } from '@/modules/neo4j.ts';
|
||||
import { getSession } from '@/modules/neo4j.ts';
|
||||
import Neode from 'neode';
|
||||
|
||||
export const PromptNeo = neode.model('Prompt', {
|
||||
id: {
|
||||
type: 'uuid',
|
||||
primary: true,
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
},
|
||||
description: 'string',
|
||||
// profile: { type: 'object', optional: true }, // 用于存储 JSON 对象
|
||||
prompt: 'string',
|
||||
// inputVariables: { type: 'array', item },
|
||||
// tags: { type: 'array', items: 'string', optional: true } // 定义字符串数组
|
||||
inputVariables: { type: 'string', default: JSON.stringify([]) },
|
||||
localVariables: { type: 'string', default: JSON.stringify([]) },
|
||||
|
||||
// 定义可单向或双向的关系
|
||||
relatedPrompts: {
|
||||
type: 'relationship',
|
||||
relationship: 'RELATED_TO',
|
||||
target: 'Prompt', // 指向自身
|
||||
direction: 'out', // 默认是单向的
|
||||
properties: {
|
||||
created_at: 'datetime',
|
||||
bidirectional: 'boolean', // 用来标记该关系是否为双向
|
||||
},
|
||||
eager: true, // 自动加载相关的 Prompts
|
||||
},
|
||||
});
|
||||
export async function createRelationship(promptA: Neode.Node<unknown>, promptB: Neode.Node<unknown>, isBidirectional = false) {
|
||||
// 创建单向关系
|
||||
await promptA.relateTo(promptB, 'RELATED_TO', { created_at: new Date(), bidirectional: isBidirectional });
|
||||
|
||||
// 如果是双向关系,创建反向关系
|
||||
if (isBidirectional) {
|
||||
await promptB.relateTo(promptA, 'RELATED_TO', { created_at: new Date(), bidirectional: true });
|
||||
}
|
||||
}
|
||||
export async function createRelationship2(promptId1, promptId2, isBidirectional = false) {
|
||||
const query = `
|
||||
MATCH (p1:Prompt {id: $id1}), (p2:Prompt {id: $id2})
|
||||
CREATE (p1)-[r:RELATED_TO {created_at: $createdAt, bidirectional: $bidirectional}]->(p2)
|
||||
RETURN r
|
||||
`;
|
||||
|
||||
const result = await getSession().run(query, {
|
||||
id1: promptId1,
|
||||
id2: promptId2,
|
||||
createdAt: new Date().toISOString(),
|
||||
bidirectional: isBidirectional,
|
||||
});
|
||||
|
||||
return result.records[0].get('r');
|
||||
}
|
||||
export async function createPrompt(promptData) {
|
||||
const session = getSession();
|
||||
const query = `
|
||||
CREATE (p:Prompt {
|
||||
id: $id,
|
||||
title: $title,
|
||||
description: $description,
|
||||
prompt: $prompt,
|
||||
inputVariables: $inputVariables,
|
||||
localVariables: $localVariables
|
||||
})
|
||||
RETURN p
|
||||
`;
|
||||
|
||||
const result = await session.run(query, {
|
||||
id: promptData.id,
|
||||
title: promptData.title,
|
||||
description: promptData.description,
|
||||
prompt: promptData.prompt,
|
||||
inputVariables: JSON.stringify(promptData.inputVariables || []),
|
||||
localVariables: JSON.stringify(promptData.localVariables || []),
|
||||
});
|
||||
|
||||
return result.records[0].get('p');
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import { sequelize } from '../modules/sequelize.ts';
|
||||
import { DataTypes, Model } from 'sequelize';
|
||||
import { Variable } from '@kevisual/ai-graph';
|
||||
|
||||
/**
|
||||
* 预设数据,定义了请求的内容和验证器
|
||||
*/
|
||||
export type PresetData = {
|
||||
// 参数
|
||||
validator: {
|
||||
[key: string]: any; // 请求的内容的验证器
|
||||
};
|
||||
data: {
|
||||
prompt?: string; // 提前预设值
|
||||
inputs: Variable & { operate?: string }[]; // 请求内容的变量和内容
|
||||
};
|
||||
};
|
||||
export class Prompt extends Model {
|
||||
declare id: string;
|
||||
declare title: string;
|
||||
declare description: string;
|
||||
declare presetData: PresetData;
|
||||
}
|
||||
|
||||
Prompt.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
},
|
||||
title: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
},
|
||||
presetData: {
|
||||
type: DataTypes.JSON,
|
||||
},
|
||||
key: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
},
|
||||
// inputVariables: {
|
||||
// type: DataTypes.JSON,
|
||||
// defaultValue: [],
|
||||
// },
|
||||
// localVariables: {
|
||||
// type: DataTypes.JSON,
|
||||
// defaultValue: [],
|
||||
// },
|
||||
},
|
||||
{
|
||||
sequelize, // 传入 Sequelize 实例
|
||||
modelName: 'prompt', // 模型名称
|
||||
paranoid: true,
|
||||
},
|
||||
);
|
||||
|
||||
Prompt.sync({ alter: true, force: false, logging: false }).catch((e) => {
|
||||
console.error('Prompt sync error', e);
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Queue } from 'bullmq';
|
||||
import { useConfig } from '@kevisual/use-config';
|
||||
const config = useConfig();
|
||||
|
||||
export const connection = {
|
||||
host: config.redis?.host || 'localhost',
|
||||
port: config.redis?.port || 6379,
|
||||
};
|
||||
|
||||
export const quene = new Queue('test', {
|
||||
connection: connection,
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
import Neode from 'neode';
|
||||
import { useConfig } from '@kevisual/use-config';
|
||||
import neo4j from 'neo4j-driver';
|
||||
|
||||
type NeodeConfig = {
|
||||
uri: string;
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
const { neo4j: neo4jConfig } = useConfig<{ neo4j: NeodeConfig }>('neo4j');
|
||||
|
||||
const { uri, username, password } = neo4jConfig;
|
||||
// 设置连接配置
|
||||
// const neode = new Neode('bolt://localhost:7687', 'neo4j', 'your_password');
|
||||
export const neode = new Neode(uri, username, password);
|
||||
// 创建与 Neo4j 数据库的连接
|
||||
export const neoDriver = neo4j.driver(
|
||||
uri, // 数据库地址
|
||||
neo4j.auth.basic(username, password), // 用户名和密码
|
||||
);
|
||||
export const getSession = () => {
|
||||
return neoDriver.session();
|
||||
};
|
||||
|
||||
const testConnect = async () => {
|
||||
// 连接成功
|
||||
// 尝试执行简单的 Cypher 查询以测试连接
|
||||
neode
|
||||
.cypher('RETURN 1', {})
|
||||
.then(() => {
|
||||
console.log('connect neo4j success');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Failed to connect:', err);
|
||||
});
|
||||
};
|
||||
|
||||
testConnect();
|
||||
@@ -1,30 +0,0 @@
|
||||
import { useConfig } from '@kevisual/use-config';
|
||||
import { Ollama, Message, ChatRequest } from 'ollama';
|
||||
|
||||
const config = useConfig<{ ollama: Ollama['config'] & { model: string } }>();
|
||||
|
||||
const { host, model } = config.ollama;
|
||||
|
||||
export const ollama = new Ollama({ host });
|
||||
|
||||
export type ChatMessage = {
|
||||
content: string;
|
||||
} & Message;
|
||||
|
||||
type ChatOpts = {
|
||||
model?: string;
|
||||
messages?: ChatMessage[];
|
||||
options?: ChatRequest['options'];
|
||||
} & ChatRequest;
|
||||
export const chat = (messages: ChatMessage[], chatOpts?: ChatOpts) => {
|
||||
const { options, stream, ...rest } = chatOpts || {};
|
||||
return ollama.chat({
|
||||
messages,
|
||||
model: model,
|
||||
options: {
|
||||
temperature: 0,
|
||||
...chatOpts?.options,
|
||||
},
|
||||
...rest,
|
||||
});
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
import './list.ts';
|
||||
@@ -1,137 +0,0 @@
|
||||
import { app } from '@/app.ts';
|
||||
import { AiAgent, AiProperties } from '@/models/agent.ts';
|
||||
import { CustomError } from '@kevisual/router';
|
||||
// import { agentManger } from '@kevisual/ai-lang';
|
||||
import { v4 } from 'uuid';
|
||||
app
|
||||
.route({
|
||||
path: 'agent',
|
||||
key: 'list',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const agents = await AiAgent.findAll({
|
||||
order: [['updatedAt', 'DESC']],
|
||||
// 返回的内容,不包含apiKey的字段
|
||||
attributes: { exclude: ['apiKey'] },
|
||||
});
|
||||
ctx.body = agents;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route('agent', 'get')
|
||||
.define(async (ctx) => {
|
||||
const id = ctx.query.id;
|
||||
if (!id) {
|
||||
throw new CustomError('id is required');
|
||||
}
|
||||
ctx.body = await AiAgent.findByPk(id, {
|
||||
attributes: { exclude: ['apiKey'] },
|
||||
});
|
||||
return ctx;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route('agent', 'update')
|
||||
.define(async (ctx) => {
|
||||
const { id, ...rest } = ctx.query.data;
|
||||
let agent = await AiAgent.findByPk(id);
|
||||
if (!agent) {
|
||||
agent = await AiAgent.create(rest);
|
||||
ctx.body = agent;
|
||||
return ctx;
|
||||
}
|
||||
await agent.update(rest);
|
||||
ctx.body = agent;
|
||||
return ctx;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route('agent', 'delete')
|
||||
.define(async (ctx) => {
|
||||
const id = ctx.query.id;
|
||||
if (!id) {
|
||||
throw new CustomError('id is required');
|
||||
}
|
||||
const agent = await AiAgent.findByPk(id);
|
||||
if (!agent) {
|
||||
throw new CustomError('agent not found');
|
||||
}
|
||||
await agent.destroy();
|
||||
ctx.body = agent;
|
||||
return ctx;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
// app
|
||||
// .route('agent', 'test')
|
||||
// .define(async (ctx) => {
|
||||
// const { message } = ctx.query;
|
||||
// const data: AiProperties = {
|
||||
// type: 'ollama',
|
||||
// id: 'test',
|
||||
// model: 'qwen2.5:14b',
|
||||
// baseUrl: 'http://mz.zxj.im:11434',
|
||||
// cache: 'memory',
|
||||
// };
|
||||
// const agent = agentManger.createAgent(data as any);
|
||||
// const res = await agent.sendHumanMessage(message);
|
||||
// // agent.close();
|
||||
// agentManger.removeAgent(agent.id);
|
||||
// ctx.body = res;
|
||||
// return ctx;
|
||||
// })
|
||||
// .addTo(app);
|
||||
|
||||
export const agentModelList = ['qwen2.5:14b', 'qwen2.5-coder:7b', 'llama3.1:8b', 'bakllava:latest'] as const;
|
||||
export const openAiModels = ['gpt-4o'];
|
||||
const demoData: AiProperties[] = [
|
||||
{
|
||||
id: v4(),
|
||||
type: 'openai',
|
||||
model: 'gpt-4o',
|
||||
baseUrl: 'https://oneapi.on-ai.ai/v1',
|
||||
apiKey: 'sk-GJE6I8OJWDr2ErFBD4C4706a65Ad4cD9B596Cf7c76943e45',
|
||||
},
|
||||
...agentModelList.map((item) => {
|
||||
return {
|
||||
id: v4(),
|
||||
type: 'ollama',
|
||||
model: item,
|
||||
baseUrl: 'http://mz.zxj.im:11434',
|
||||
apiKey: 'sk-GJE6I8OJWDr2ErFBD4C4706a65Ad4cD9B596Cf7c76943e45',
|
||||
};
|
||||
}),
|
||||
];
|
||||
|
||||
// AiAgent.bulkCreate(demoData, { ignoreDuplicates: true }).then(() => {
|
||||
// console.log('create demo data success');
|
||||
// });
|
||||
const initManager = async () => {
|
||||
// const list = await AiAgent.findAll();
|
||||
const list = await AiAgent.findAll({
|
||||
where: {
|
||||
status: 'open',
|
||||
},
|
||||
logging: false,
|
||||
});
|
||||
const data = list.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
type: item.type as any,
|
||||
model: item.model as any,
|
||||
baseUrl: item.baseUrl,
|
||||
apiKey: item.apiKey,
|
||||
temperature: item.temperature,
|
||||
cache: item.cache as any,
|
||||
cacheName: item.cacheName,
|
||||
};
|
||||
});
|
||||
// agentManger.createAgentList(data);
|
||||
};
|
||||
// setTimeout(() => {
|
||||
// initManager();
|
||||
// }, 1000);
|
||||
@@ -1,201 +0,0 @@
|
||||
import { app } from '@/app.ts';
|
||||
import { AiAgent } from '@/models/agent.ts';
|
||||
import { ChatPrompt } from '@/models/chat-prompt.ts';
|
||||
import { ChatSession } from '@/models/chat-session.ts';
|
||||
import { Prompt } from '@/models/prompt.ts';
|
||||
import { agentManger } from '@kevisual/ai-lang';
|
||||
import { PromptTemplate } from '@kevisual/ai-graph';
|
||||
import { v4 } from 'uuid';
|
||||
import { ChatHistory } from '@/models/chat-history.ts';
|
||||
import { User } from '@/models/user.ts';
|
||||
const clients = [];
|
||||
export const compotedToken = () => {
|
||||
// 计算token消耗
|
||||
};
|
||||
export const getConfigByKey = async (key) => {
|
||||
const chatPrompt = await ChatPrompt.findOne({ where: { key } });
|
||||
const { promptId, aiAgentId } = chatPrompt.data;
|
||||
const prompt = await Prompt.findByPk(promptId);
|
||||
let aiAgent = agentManger.getAgent(aiAgentId);
|
||||
if (!aiAgent) {
|
||||
// throw new Error('aiAgent not found');
|
||||
const aiAgnetModel = await AiAgent.findByPk(aiAgentId);
|
||||
aiAgent = agentManger.createAgent({
|
||||
id: aiAgnetModel.id,
|
||||
type: aiAgnetModel.type as any,
|
||||
model: aiAgnetModel.model as any,
|
||||
baseUrl: aiAgnetModel.baseUrl,
|
||||
apiKey: aiAgnetModel.apiKey,
|
||||
temperature: aiAgnetModel.temperature,
|
||||
cache: aiAgnetModel.cache as any,
|
||||
cacheName: aiAgnetModel.cacheName,
|
||||
});
|
||||
}
|
||||
return { chatPrompt, prompt, aiAgent };
|
||||
};
|
||||
export const getTemplate = async ({ data, inputs }) => {
|
||||
const promptTemplate = new PromptTemplate({
|
||||
prompt: data.prompt,
|
||||
inputVariables: inputs.map((item) => {
|
||||
return {
|
||||
key: item.key,
|
||||
value: item.value,
|
||||
};
|
||||
}),
|
||||
localVariables: [],
|
||||
}); // 传入参数
|
||||
return await promptTemplate.getTemplate();
|
||||
};
|
||||
const onMessage = async ({ data, end, ws }) => {
|
||||
// messages的 data
|
||||
const client = clients.find((client) => client.ws === ws);
|
||||
if (!client) {
|
||||
end({ code: 404, data: {}, message: 'client not found' });
|
||||
return;
|
||||
}
|
||||
const { uid, id, key } = client.data;
|
||||
const {
|
||||
inputs,
|
||||
message: sendMessage,
|
||||
data: {},
|
||||
} = data;
|
||||
let root = data.root || false;
|
||||
let chatSession = await ChatSession.findByPk(id);
|
||||
const config = await getConfigByKey(key);
|
||||
const { prompt, aiAgent, chatPrompt } = config;
|
||||
let userQuestion = sendMessage;
|
||||
if (!chatSession) {
|
||||
chatSession = await ChatSession.create({ key, id, data: data, uid, chatPromptId: chatPrompt.id });
|
||||
root = true;
|
||||
} else {
|
||||
// 更新session context的值
|
||||
const newData = JSON.parse(data);
|
||||
if (newData !== '{}' && JSON.stringify(chatSession.data) !== JSON.stringify(data)) {
|
||||
await chatSession.update({ data: data });
|
||||
}
|
||||
if (root) {
|
||||
const chatHistory = await ChatHistory.findAll({ where: { chatId: id }, logging: false });
|
||||
chatHistory.forEach((item) => {
|
||||
end({ code: 200, data: item, message: 'success', type: 'messages' });
|
||||
});
|
||||
// return;
|
||||
}
|
||||
root = false;
|
||||
}
|
||||
if (!userQuestion) {
|
||||
if (!prompt?.presetData) {
|
||||
end({ code: 404, data: {}, message: 'prompt not set, need presetData' });
|
||||
return;
|
||||
}
|
||||
const template = await getTemplate({ data: prompt.presetData.data, inputs });
|
||||
if (!template) {
|
||||
end({ code: 404, data: {}, message: 'template not found' });
|
||||
return;
|
||||
}
|
||||
userQuestion = template;
|
||||
}
|
||||
// 保存到数据库
|
||||
const roleUser = await ChatHistory.create({
|
||||
data: {
|
||||
message: userQuestion,
|
||||
},
|
||||
chatId: id,
|
||||
chatPromptId: chatPrompt.id,
|
||||
root: root,
|
||||
uid: uid,
|
||||
show: true,
|
||||
role: 'user',
|
||||
});
|
||||
end({ code: 200, data: roleUser, message: 'success', type: 'messages' });
|
||||
const result = await aiAgent.sendHumanMessage(userQuestion, { thread_id: id });
|
||||
const lastMessage = result.messages[result.messages.length - 1];
|
||||
const message = result.messages[result.messages.length - 1].content;
|
||||
// 根据key找到对应的prompt
|
||||
// 保存到数据库
|
||||
const roleAi = await ChatHistory.create({
|
||||
data: {
|
||||
message,
|
||||
result: lastMessage,
|
||||
},
|
||||
chatId: id,
|
||||
chatPromptId: chatPrompt.id,
|
||||
root: false,
|
||||
uid: uid,
|
||||
show: true,
|
||||
role: 'ai',
|
||||
});
|
||||
end({ code: 200, data: roleAi, message: 'success', type: 'messages' });
|
||||
};
|
||||
const getHistory = async (id: string, { data, end, ws }) => {
|
||||
const chatHistory = await ChatHistory.findAll({ where: { chatId: id }, logging: false });
|
||||
chatHistory.forEach((item) => {
|
||||
end({ code: 200, data: item, message: 'success', type: 'messages' });
|
||||
});
|
||||
};
|
||||
app.io.addListener('chat', async ({ data, end, ws }) => {
|
||||
const { type } = data || {};
|
||||
if (type === 'subscribe') {
|
||||
const token = data?.token;
|
||||
if (!token) {
|
||||
end({ code: 401, data: {}, message: 'need token' });
|
||||
return;
|
||||
}
|
||||
let tokenUesr;
|
||||
try {
|
||||
tokenUesr = await User.verifyToken(token);
|
||||
} catch (e) {
|
||||
end({ code: 401, data: {}, message: 'token is invaild' });
|
||||
return;
|
||||
}
|
||||
const uid = tokenUesr.id;
|
||||
const id = v4();
|
||||
const clientData = { ...data?.data, uid };
|
||||
if (!clientData.id) {
|
||||
clientData.id = id;
|
||||
}
|
||||
const client = clients.find((client) => client.ws === ws);
|
||||
if (!client) {
|
||||
clients.push({ ws, data: clientData }); // 拆包,里面包含的type信息,去掉
|
||||
}
|
||||
end({ code: 200, data: clientData, message: 'subscribe success' });
|
||||
} else if (type === 'unsubscribe') {
|
||||
// 需要手动取消订阅
|
||||
const index = clients.findIndex((client) => client.ws === ws);
|
||||
if (index > -1) {
|
||||
const data = clients[index]?.data;
|
||||
clients.splice(index, 1);
|
||||
end({ code: 200, data, message: 'unsubscribe success' });
|
||||
return;
|
||||
}
|
||||
end({ code: 200, data: {}, message: 'unsubscribe success' });
|
||||
return;
|
||||
} else if (type === 'messages') {
|
||||
try {
|
||||
await onMessage({ data: data.data, end, ws });
|
||||
} catch (e) {
|
||||
console.error('onMessage error', e);
|
||||
end({ code: 500, data: {}, message: 'onMessage error' });
|
||||
}
|
||||
return;
|
||||
} else if (type === 'changeSession') {
|
||||
// 修改client的session的id
|
||||
const client = clients.find((client) => client.ws === ws);
|
||||
if (!client) {
|
||||
end({ code: 404, data: {}, message: 'client not found' });
|
||||
return;
|
||||
}
|
||||
const { id } = data?.data;
|
||||
client.data.id = id || v4();
|
||||
// 返回修改后的history的内容
|
||||
end({ code: 200, data: client.data, message: 'changeSession success' });
|
||||
getHistory(id, { data, end, ws });
|
||||
return;
|
||||
} else {
|
||||
end({ code: 404, data: {}, message: 'subscribe fail' });
|
||||
return;
|
||||
}
|
||||
ws.on('close', () => {
|
||||
const index = clients.findIndex((client) => client.ws === ws);
|
||||
if (index > -1) clients.splice(index, 1);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
import './list.ts'
|
||||
import './session-list.ts'
|
||||
import './chat-io.ts'
|
||||
@@ -1,34 +0,0 @@
|
||||
import { app } from '@/app.ts';
|
||||
import { ChatHistory } from '@/models/chat-history.ts';
|
||||
import { CustomError } from '@kevisual/router';
|
||||
|
||||
// Admin only
|
||||
app
|
||||
.route({
|
||||
path: 'chat-history',
|
||||
key: 'list',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const chatPrompt = await ChatHistory.findAll({
|
||||
order: [['updatedAt', 'DESC']],
|
||||
});
|
||||
ctx.body = chatPrompt;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'chat-history',
|
||||
key: 'delete',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { id } = ctx.query;
|
||||
const chatHistory = await ChatHistory.findByPk(id);
|
||||
if (!chatHistory) {
|
||||
throw new CustomError('ChatHistory not found');
|
||||
}
|
||||
await chatHistory.destroy();
|
||||
ctx.body = chatHistory;
|
||||
})
|
||||
.addTo(app);
|
||||
@@ -1,84 +0,0 @@
|
||||
import { app } from '@/app.ts';
|
||||
import { ChatSession } from '@/models/chat-session.ts';
|
||||
import { ChatPrompt } from '@/models/chat-prompt.ts';
|
||||
import { CustomError } from '@kevisual/router';
|
||||
app
|
||||
.route({
|
||||
path: 'chat-session',
|
||||
key: 'list',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const chatSession = await ChatSession.findAll({
|
||||
order: [['updatedAt', 'DESC']],
|
||||
});
|
||||
ctx.body = chatSession;
|
||||
})
|
||||
.addTo(app);
|
||||
// Admin only
|
||||
app
|
||||
.route({
|
||||
path: 'chat-session',
|
||||
key: 'list-history',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const data = ctx.query.data;
|
||||
const chatPrompt = await ChatPrompt.findOne({
|
||||
where: {
|
||||
key: data.key,
|
||||
},
|
||||
});
|
||||
if (!chatPrompt) {
|
||||
throw new CustomError('ChatPrompt not found');
|
||||
}
|
||||
console.log('chatPrompt', chatPrompt.id);
|
||||
const chatSession = await ChatSession.findAll({
|
||||
order: [['updatedAt', 'DESC']],
|
||||
where: {
|
||||
chatPromptId: chatPrompt.id,
|
||||
},
|
||||
limit: data.limit || 10,
|
||||
});
|
||||
ctx.body = chatSession;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'chat-session',
|
||||
key: 'update',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const tokenUser = ctx.state.tokenUser;
|
||||
const uid = tokenUser.id;
|
||||
const { id, ...data } = ctx.query.data;
|
||||
if (id) {
|
||||
const session = await ChatSession.findByPk(id);
|
||||
if (session) {
|
||||
await session.update(data);
|
||||
} else {
|
||||
throw new CustomError('Session not found');
|
||||
}
|
||||
ctx.body = session;
|
||||
return;
|
||||
}
|
||||
const session = await ChatSession.create({ ...data, uid });
|
||||
ctx.body = session;
|
||||
})
|
||||
.addTo(app);
|
||||
app
|
||||
.route({
|
||||
path: 'chat-session',
|
||||
key: 'delete',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { id } = ctx.query;
|
||||
const session = await ChatSession.findByPk(id);
|
||||
if (!session) {
|
||||
throw new CustomError('Session not found');
|
||||
}
|
||||
await session.destroy();
|
||||
ctx.body = session;
|
||||
})
|
||||
.addTo(app);
|
||||
@@ -1 +0,0 @@
|
||||
import './list.ts';
|
||||
@@ -1,131 +0,0 @@
|
||||
import { app } from '@/app.ts';
|
||||
import { AiAgent } from '@/models/agent.ts';
|
||||
import { ChatPrompt } from '@/models/chat-prompt.ts';
|
||||
import { Prompt } from '@/models/prompt.ts';
|
||||
import { CustomError } from '@kevisual/router';
|
||||
|
||||
// Admin only
|
||||
app
|
||||
.route({
|
||||
path: 'chat-prompt',
|
||||
key: 'list',
|
||||
// middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const chatPrompt = await ChatPrompt.findAll({
|
||||
order: [['updatedAt', 'DESC']],
|
||||
// 列出被删除的
|
||||
// paranoid: false,
|
||||
});
|
||||
ctx.body = chatPrompt;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'chat-prompt',
|
||||
key: 'get',
|
||||
validator: {
|
||||
id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { id } = ctx.query;
|
||||
const chatPrompt = await ChatPrompt.findByPk(id);
|
||||
if (!chatPrompt) {
|
||||
throw new CustomError('ChatPrompt not found');
|
||||
}
|
||||
ctx.body = chatPrompt;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'chat-prompt',
|
||||
key: 'update',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const tokenUser = ctx.state.tokenUser;
|
||||
const uid = tokenUser.id;
|
||||
const { data } = ctx.query;
|
||||
const { id, ...rest } = data;
|
||||
if (id) {
|
||||
const page = await ChatPrompt.findByPk(id);
|
||||
if (page) {
|
||||
if (rest.data) {
|
||||
rest.data = { ...page.data, ...rest.data };
|
||||
}
|
||||
const newPage = await page.update(rest);
|
||||
ctx.body = newPage;
|
||||
} else {
|
||||
throw new CustomError('page not found');
|
||||
}
|
||||
} else if (data) {
|
||||
const page = await ChatPrompt.create({ ...rest, uid });
|
||||
ctx.body = page;
|
||||
}
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'chat-prompt',
|
||||
key: 'delete',
|
||||
validator: {
|
||||
id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const id = ctx.query.id;
|
||||
const chatPrompt = await ChatPrompt.findByPk(id);
|
||||
if (!chatPrompt) {
|
||||
throw new CustomError('chatPrompt not found');
|
||||
}
|
||||
await chatPrompt.destroy();
|
||||
ctx.body = 'success';
|
||||
})
|
||||
.addTo(app);
|
||||
app
|
||||
.route({
|
||||
path: 'chat-prompt',
|
||||
key: 'getByKey',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { key } = ctx.query.data || {};
|
||||
if (!key) {
|
||||
throw new CustomError('key is required');
|
||||
}
|
||||
const chatPrompt = await ChatPrompt.findOne({
|
||||
where: { key },
|
||||
});
|
||||
if (!chatPrompt) {
|
||||
throw new CustomError('chatPrompt not found');
|
||||
}
|
||||
const { promptId, aiAgentId } = chatPrompt.data;
|
||||
if (!aiAgentId) {
|
||||
throw new CustomError('promptId is required');
|
||||
}
|
||||
const aiAgent = await AiAgent.findByPk(aiAgentId, {
|
||||
// 只获取 id 和description 字段
|
||||
attributes: ['id', 'description', 'key'],
|
||||
});
|
||||
if (!aiAgent) {
|
||||
throw new CustomError('aiAgent not found');
|
||||
}
|
||||
const prompt = await Prompt.findByPk(promptId);
|
||||
ctx.body = {
|
||||
chatPrompt: chatPrompt,
|
||||
aiAgent,
|
||||
prompt,
|
||||
};
|
||||
})
|
||||
.addTo(app);
|
||||
@@ -4,16 +4,8 @@ import './page/index.ts';
|
||||
|
||||
import './resource/index.ts';
|
||||
|
||||
import './prompt-graph/index.ts';
|
||||
|
||||
import './agent/index.ts';
|
||||
|
||||
import './user/index.ts';
|
||||
|
||||
import './chat-prompt/index.ts';
|
||||
|
||||
import './chat-history/index.ts';
|
||||
|
||||
import './github/index.ts';
|
||||
|
||||
import './app-manager/index.ts';
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
import { app } from '@/app.ts';
|
||||
import { Prompt } from '@/models/prompt.ts';
|
||||
import { chat } from '@/modules/ollama.ts';
|
||||
import { CustomError } from '@kevisual/router';
|
||||
import { PromptTemplate } from '@kevisual/ai-graph';
|
||||
import { v4 } from 'uuid';
|
||||
app
|
||||
.route('ai', 'run', { nextRoute: { id: 'runOllama' } })
|
||||
.define({
|
||||
validator: {
|
||||
data: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
key: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: 'Prompt key is required',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
// ctx.currentRoute?.verify(ctx, true);
|
||||
|
||||
const { key, inputs = [] } = ctx.query.data || {};
|
||||
if (!key) {
|
||||
throw new CustomError('Prompt key is required');
|
||||
}
|
||||
const prompt = await Prompt.findOne({ where: { key } });
|
||||
console.log('prompt', 'key', key, prompt);
|
||||
if (!prompt) {
|
||||
throw new CustomError('Prompt not found');
|
||||
}
|
||||
const { presetData } = prompt;
|
||||
const { data, validator } = presetData || {};
|
||||
// const { inputs = [] } = data;
|
||||
// TODO: 获取validator和inputs的内容
|
||||
const promptTemplate = new PromptTemplate({
|
||||
prompt: data.prompt,
|
||||
inputVariables: inputs.map((item) => {
|
||||
return {
|
||||
key: item.key,
|
||||
value: item.value,
|
||||
};
|
||||
}),
|
||||
localVariables: [],
|
||||
});
|
||||
const result = await promptTemplate.getTemplate();
|
||||
ctx.state = {
|
||||
prompt: result,
|
||||
};
|
||||
ctx.body = result;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route('ai', 'runOllama', {
|
||||
id: 'runOllama',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const prompt = ctx.state.prompt;
|
||||
const uuid = v4();
|
||||
if (!prompt) {
|
||||
throw new CustomError('Prompt Template not found');
|
||||
}
|
||||
const res = await chat([
|
||||
{
|
||||
role: 'user',
|
||||
content: prompt,
|
||||
},
|
||||
]);
|
||||
ctx.body = { id: uuid, ...res };
|
||||
})
|
||||
.addTo(app);
|
||||
@@ -1,73 +0,0 @@
|
||||
import { getSession } from '@/modules/neo4j.ts';
|
||||
export async function fetchData() {
|
||||
const session = getSession();
|
||||
try {
|
||||
const query = `MATCH (n)
|
||||
OPTIONAL MATCH (n)-[r]->(m)
|
||||
RETURN n, r, m`;
|
||||
const queryConnect = 'MATCH (n)-[r]->(m) RETURN n, r, m LIMIT 25';
|
||||
const result = await session.run(query);
|
||||
|
||||
const graphData = { nodes: [], links: [] };
|
||||
const nodeMap = new Map();
|
||||
// n和n的关系用 relatedPrompts 进行关联
|
||||
result.records.forEach((record) => {
|
||||
const node = record.get('n');
|
||||
const relation = record.get('r');
|
||||
const target = record.get('m');
|
||||
if (!nodeMap.has(node.identity)) {
|
||||
nodeMap.set(node.identity, {
|
||||
id: node.identity.toString(),
|
||||
label: node.labels[0],
|
||||
properties: node.properties,
|
||||
});
|
||||
graphData.nodes.push(nodeMap.get(node.identity));
|
||||
}
|
||||
|
||||
if (relation && !nodeMap.has(relation.identity)) {
|
||||
nodeMap.set(relation.identity, {
|
||||
id: relation.identity.toString(),
|
||||
label: relation.type,
|
||||
properties: relation.properties,
|
||||
});
|
||||
graphData.nodes.push(nodeMap.get(relation.identity));
|
||||
}
|
||||
|
||||
if (target && !nodeMap.has(target.identity)) {
|
||||
nodeMap.set(target.identity, {
|
||||
id: target.identity.toString(),
|
||||
label: target.labels[0],
|
||||
properties: target.properties,
|
||||
});
|
||||
graphData.nodes.push(nodeMap.get(target.identity));
|
||||
}
|
||||
|
||||
if (relation) {
|
||||
graphData.links.push({
|
||||
source: node.identity.toString(),
|
||||
target: relation.identity.toString(),
|
||||
type: relation.type,
|
||||
properties: relation.properties,
|
||||
});
|
||||
}
|
||||
|
||||
if (target) {
|
||||
graphData.links.push({
|
||||
source: node.identity.toString(),
|
||||
target: target.identity.toString(),
|
||||
type: 'RELATED_TO',
|
||||
properties: {},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return graphData;
|
||||
} finally {
|
||||
await session.close();
|
||||
}
|
||||
}
|
||||
|
||||
// fetchData().then((graphData) => {
|
||||
// console.log(graphData); // 用于验证获取的数据
|
||||
// drawGraph(graphData); // 调用 D3 绘制函数
|
||||
// });
|
||||
@@ -1,3 +0,0 @@
|
||||
import './list-graph.ts';
|
||||
import './list.ts';
|
||||
import './ai.ts';
|
||||
@@ -1,97 +0,0 @@
|
||||
import { PromptNeo, createRelationship, createRelationship2 } from '@/models/prompt-graph.ts';
|
||||
import { app } from '@/app.ts';
|
||||
import { v4 } from 'uuid';
|
||||
import { fetchData } from './d3/get-graph.ts';
|
||||
app
|
||||
.route('prompt-graph', 'list')
|
||||
.define(async (ctx) => {
|
||||
const prompts = await PromptNeo.all();
|
||||
const json = await prompts.toJson();
|
||||
// console.log('json', json);
|
||||
ctx.body = json;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route('prompt-graph', 'update')
|
||||
.define(async (ctx) => {
|
||||
const { id, title, description, prompt, inputVariables, localVariables } = ctx.query;
|
||||
const promptNode = await PromptNeo.first('id', id);
|
||||
|
||||
if (!promptNode) {
|
||||
const promptData = {
|
||||
id: v4(),
|
||||
title,
|
||||
description,
|
||||
prompt,
|
||||
inputVariables: JSON.stringify(inputVariables),
|
||||
localVariables: JSON.stringify(localVariables),
|
||||
};
|
||||
const _prompt = await PromptNeo.create(promptData);
|
||||
ctx.body = await _prompt.toJson();
|
||||
return;
|
||||
}
|
||||
|
||||
await promptNode.update({ title, description, prompt, inputVariables, localVariables });
|
||||
ctx.body = await promptNode.toJson();
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route('prompt-graph', 'delete')
|
||||
.define(async (ctx) => {
|
||||
const { id, title } = ctx.query;
|
||||
const promptNode = await PromptNeo.first('id', id);
|
||||
if (!promptNode) {
|
||||
ctx.body = 'prompt not found';
|
||||
return;
|
||||
}
|
||||
await promptNode.delete();
|
||||
ctx.body = 'delete success';
|
||||
})
|
||||
.addTo(app);
|
||||
app
|
||||
.route('prompt-graph', 'deleteAll')
|
||||
.define(async (ctx) => {
|
||||
const prompts = await PromptNeo.all();
|
||||
for (const prompt of prompts) {
|
||||
await prompt.delete();
|
||||
}
|
||||
ctx.body = 'delete all success';
|
||||
})
|
||||
.addTo(app);
|
||||
app
|
||||
.route('prompt-graph', 'createDemo')
|
||||
.define(async (ctx) => {
|
||||
const promptData = {
|
||||
id: v4(),
|
||||
title: 'test-' + v4(),
|
||||
description: '这是测试保存prompt的数据',
|
||||
prompt: '这是测试保存prompt的数据',
|
||||
inputVariables: JSON.stringify([{ key: 'test', value: 'test' }]),
|
||||
localVariables: JSON.stringify([{ key: 'test', value: 'test' }]),
|
||||
};
|
||||
const f = await PromptNeo.first('id', 'f5288cdb-bfca-4a65-b629-cae590ede719');
|
||||
if (!f) {
|
||||
ctx.body = 'not found f';
|
||||
return;
|
||||
}
|
||||
const prompt = await PromptNeo.create({ ...promptData });
|
||||
// await prompt.relateTo(f, 'RELATED_TO', { createdAt: new Date().toISOString() });
|
||||
// f.relateTo(prompt, 'RELATED_TO', { createdAt: new Date().toISOString() });
|
||||
// await createRelationship(f, prompt);
|
||||
const fj = await f.toJson() as any;
|
||||
const pj = await prompt.toJson() as any;
|
||||
|
||||
await createRelationship2(fj.id, pj.id);
|
||||
ctx.body = await prompt.toJson();
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route('prompt-graph', 'getD3')
|
||||
.define(async (ctx) => {
|
||||
const value = await fetchData();
|
||||
ctx.body = value;
|
||||
})
|
||||
.addTo(app);
|
||||
@@ -1,63 +0,0 @@
|
||||
import { Prompt } from '@/models/prompt.ts';
|
||||
|
||||
import { app } from '@/app.ts';
|
||||
import { CustomError } from '@kevisual/router';
|
||||
|
||||
app
|
||||
.route('prompt', 'list')
|
||||
.define(async (ctx) => {
|
||||
const prompts = await Prompt.findAll({
|
||||
order: [['updatedAt', 'DESC']],
|
||||
});
|
||||
ctx.body = prompts;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route('prompt', 'update')
|
||||
.define(async (ctx) => {
|
||||
const { id, title, description, presetData, key } = ctx.query.data || {};
|
||||
if (!key) {
|
||||
throw new CustomError('Prompt key is required');
|
||||
}
|
||||
const isEdit = !!id;
|
||||
const promptKey = await Prompt.findOne({ where: { key } });
|
||||
if (promptKey && promptKey.id !== id) {
|
||||
throw new CustomError(`Prompt key is already exist, use by ${promptKey.id}`);
|
||||
}
|
||||
if (!isEdit) {
|
||||
const prompt = new Prompt({
|
||||
title,
|
||||
key,
|
||||
description,
|
||||
presetData,
|
||||
});
|
||||
await prompt.save();
|
||||
ctx.body = prompt;
|
||||
return;
|
||||
}
|
||||
|
||||
const prompt = await Prompt.findByPk(id);
|
||||
if (!prompt) {
|
||||
throw new CustomError('Prompt not found');
|
||||
}
|
||||
await prompt.update({ title, description, presetData, key });
|
||||
ctx.body = prompt;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route('prompt', 'delete')
|
||||
.define(async (ctx) => {
|
||||
const { id } = ctx.query || {};
|
||||
if (!id) {
|
||||
throw new CustomError('Prompt id is required');
|
||||
}
|
||||
const prompt = await Prompt.findByPk(id);
|
||||
if (!prompt) {
|
||||
throw new CustomError('Prompt not found');
|
||||
}
|
||||
await prompt.destroy();
|
||||
ctx.body = 'delete success';
|
||||
})
|
||||
.addTo(app);
|
||||
@@ -1 +0,0 @@
|
||||
import './list.ts'
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Snippet } from '@/routes/snippet/snippet.ts';
|
||||
import { app } from '@/app.ts';
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'snippet',
|
||||
key: 'list',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
// 获取所有的snippet
|
||||
})
|
||||
.addTo(app);
|
||||
@@ -1,54 +0,0 @@
|
||||
import { sequelize } from '@/modules/sequelize.ts';
|
||||
import { DataTypes, Model } from 'sequelize';
|
||||
|
||||
export class Snippet extends Model {
|
||||
declare id: string;
|
||||
declare title: string;
|
||||
declare description: string;
|
||||
declare snippet: string;
|
||||
declare keyword: string;
|
||||
declare user_id: string;
|
||||
declare data: any;
|
||||
}
|
||||
|
||||
Snippet.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
},
|
||||
title: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
},
|
||||
snippet: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
},
|
||||
keyword: {
|
||||
type: DataTypes.STRING,
|
||||
},
|
||||
user_id: {
|
||||
type: DataTypes.UUID,
|
||||
},
|
||||
data: {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
defaultValue: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: 'snippet',
|
||||
paranoid: true,
|
||||
},
|
||||
);
|
||||
// 当
|
||||
// Snippet.sync({ alter: true, logging: false }).catch((e) => {
|
||||
// console.error('Snippet sync error', e);
|
||||
// });
|
||||
@@ -1,35 +0,0 @@
|
||||
import { AiAgent } from '@/models/agent.ts';
|
||||
import { RouterCodeModel } from '@/models/code.ts';
|
||||
import { Prompt } from '@/models/prompt.ts';
|
||||
|
||||
import { User } from '@/models/user.ts';
|
||||
import { ContainerModel } from '@/routes/container/models/index.ts';
|
||||
import { PageModel } from '@/routes/page/models/index.ts';
|
||||
import { ResourceModel } from '@/routes/resource/models/index.ts';
|
||||
|
||||
// declare uid: string;
|
||||
// uid: {
|
||||
// type: DataTypes.UUID,
|
||||
// allowNull: true,
|
||||
// },
|
||||
// 系统表
|
||||
export const stystemTables = [AiAgent, RouterCodeModel, Prompt];
|
||||
|
||||
export const userTables = [ContainerModel, PageModel, ResourceModel];
|
||||
|
||||
const rootUid = '14206305-8b5c-44cc-b177-766cfe2e452f';
|
||||
|
||||
const updateUser = async () => {
|
||||
const updateTables = [...userTables] as any[];
|
||||
for (let Table of updateTables) {
|
||||
// const res = await ContainerModel.update({ uid: rootUid }, { where: { uid: null } });
|
||||
try {
|
||||
const list = await Table.update({ uid: rootUid }, { where: { uid: null } });
|
||||
console.log('update--', list.length);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// updateUser();
|
||||
@@ -1,47 +0,0 @@
|
||||
import { ContainerModel } from '@/routes/container/models/index.ts';
|
||||
import { ChatPrompt } from '@/models/chat-prompt.ts';
|
||||
const recoverData = async () => {
|
||||
const data = {
|
||||
id: '868970a4-8cab-4141-a73c-cc185fd17508',
|
||||
title: '测试es6每次导入的变量,运行一次+1,并打印',
|
||||
description: '',
|
||||
tags: [],
|
||||
type: '',
|
||||
code: "let a = 1\n\nexport const main = () => {\n console.log('current a', a);\n return a++\n}",
|
||||
source: '',
|
||||
sourceType: '',
|
||||
data: {
|
||||
className: '',
|
||||
style: {},
|
||||
showChild: true,
|
||||
shadowRoot: false,
|
||||
},
|
||||
publish: {},
|
||||
uid: '14206305-8b5c-44cc-b177-766cfe2e452f',
|
||||
createdAt: '2024-09-19T13:27:58.796Z',
|
||||
updatedAt: '2024-09-28T05:27:05.381Z',
|
||||
};
|
||||
const r = await ContainerModel.create(data);
|
||||
};
|
||||
|
||||
// recoverData();
|
||||
|
||||
const revoverId = async () => {
|
||||
const id = 'e235576e-eb48-4b5c-8385-9b8ada4a137f';
|
||||
// const cp = await ChatPrompt.findByPk(id);
|
||||
const cp = await ChatPrompt.findAll({
|
||||
paranoid: false,
|
||||
});
|
||||
console.log(
|
||||
cp.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
// @ts-ignore
|
||||
deletedAt: item.deletedAt,
|
||||
};
|
||||
}),
|
||||
);
|
||||
// cp 被删除了,复原
|
||||
await ChatPrompt.restore({ where: { id } });
|
||||
};
|
||||
revoverId();
|
||||
@@ -1,5 +1,3 @@
|
||||
import { CodeManager } from './admin/dashboard/load.ts';
|
||||
import { ContainerData } from './routes/types.ts';
|
||||
|
||||
export { CodeManager };
|
||||
export { ContainerData };
|
||||
|
||||
Reference in New Issue
Block a user