clear: old code
This commit is contained in:
parent
d9fe68d3fe
commit
84edde385e
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,8 +2,6 @@ node_modules
|
||||
|
||||
dist
|
||||
|
||||
# dist/app.cjs
|
||||
|
||||
app.config.json5
|
||||
|
||||
apps.config.json
|
||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -1,6 +0,0 @@
|
||||
[submodule "packages/ai-graph"]
|
||||
path = packages/ai-graph
|
||||
url = git@git.xiongxiao.me:kevisual/kevisual-ai-graph.git
|
||||
[submodule "packages/var-proxy"]
|
||||
path = packages/var-proxy
|
||||
url = git@git.xiongxiao.me:kevisual/var-proxy.git
|
2
.npmrc
2
.npmrc
@ -1,3 +1 @@
|
||||
@abearxiong:registry=https://npm.pkg.github.com
|
||||
@build:registry=https://npm.xiongxiao.me
|
||||
@kevisual:registry=https://npm.xiongxiao.me
|
@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'codeflow', // 应用名称
|
||||
name: 'codecenter', // 应用名称
|
||||
script: './dist/app.mjs', // 入口文件
|
||||
// cwd: '.', // 设置当前工作目录
|
||||
output: './logs/codflow.log',
|
||||
|
54
package.json
54
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@build/code-flow",
|
||||
"version": "0.0.2",
|
||||
"description": "code的flow流程成图",
|
||||
"name": "@kevisual/code-center",
|
||||
"version": "0.0.1",
|
||||
"description": "code center",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"author": "abearxiong",
|
||||
@ -12,14 +12,14 @@
|
||||
"test": "tsx test/**/*.ts",
|
||||
"dev:watch": "cross-env NODE_ENV=development concurrently -n \"Watch,Dev\" -c \"green,blue\" \"npm run watch\" \"sleep 1 && npm run dev\" ",
|
||||
"build": "rimraf dist && rollup -c rollup.config.mjs",
|
||||
"deploy": "rsync -avz --delete ./dist/ --exclude='app.config.json5' light:~/apps/codeflow/backend",
|
||||
"deploy": "rsync -avz --delete ./dist/ --exclude='app.config.json5' light:~/apps/codecenter/dist",
|
||||
"clean": "rm -rf dist",
|
||||
"reload": "ssh light pm2 restart codeflow",
|
||||
"reload": "ssh light pm2 restart codecenter",
|
||||
"pub": "npm run build && npm run deploy && npm run reload",
|
||||
"deploy:nova": "rsync -avz --delete ./dist/ --exclude='app.config.json5' nova:~/apps/codeflow/backend",
|
||||
"deploy:nova": "rsync -avz --delete ./dist/ --exclude='app.config.json5' nova:~/apps/codecenter/dist",
|
||||
"apps:build": "rollup -c rollup.apps.config.mjs",
|
||||
"apps:watch": "rollup -c rollup.apps.config.mjs -w",
|
||||
"start": "pm2 start dist/app.mjs --name codeflow"
|
||||
"start": "pm2 start dist/app.mjs --name codecenter"
|
||||
},
|
||||
"keywords": [],
|
||||
"types": "types/index.d.ts",
|
||||
@ -30,41 +30,29 @@
|
||||
],
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.26.0",
|
||||
"@babel/preset-env": "^7.26.0",
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"@kevisual/ai-graph": "workspace:^",
|
||||
"@kevisual/ai-lang": "workspace:^",
|
||||
"@kevisual/auth": "1.0.5",
|
||||
"@kevisual/local-app-manager": "0.1.6",
|
||||
"@kevisual/router": "^0.0.6-alpha-5",
|
||||
"@types/semver": "^7.5.8",
|
||||
"archiver": "^7.0.1",
|
||||
"bullmq": "^5.34.6",
|
||||
"dayjs": "^1.11.13",
|
||||
"dts-bundle-generator": "^9.5.1",
|
||||
"formidable": "^3.5.2",
|
||||
"ioredis": "^5.4.2",
|
||||
"ioredis": "^5.5.0",
|
||||
"json5": "^2.2.3",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
"minio": "^8.0.3",
|
||||
"nanoid": "^5.0.9",
|
||||
"neo4j-driver": "^5.27.0",
|
||||
"neode": "^0.4.9",
|
||||
"minio": "^8.0.4",
|
||||
"nanoid": "^5.1.0",
|
||||
"node-fetch": "^3.3.2",
|
||||
"ollama": "^0.5.11",
|
||||
"p-queue": "^8.0.1",
|
||||
"pg": "^8.13.1",
|
||||
"rollup-plugin-esbuild": "^6.1.1",
|
||||
"semver": "^7.6.3",
|
||||
"p-queue": "^8.1.0",
|
||||
"pg": "^8.13.3",
|
||||
"rollup-plugin-esbuild": "^6.2.0",
|
||||
"semver": "^7.7.1",
|
||||
"sequelize": "^6.37.5",
|
||||
"socket.io": "^4.8.1",
|
||||
"sqlite3": "^5.1.7",
|
||||
"strip-ansi": "^7.1.0",
|
||||
"tar": "^7.4.3",
|
||||
"uuid": "^11.0.3",
|
||||
"zod": "^3.24.1"
|
||||
"uuid": "^11.0.5",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kevisual/use-config": "^1.0.7",
|
||||
@ -77,23 +65,21 @@
|
||||
"@types/archiver": "^6.0.3",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/formidable": "^3.4.5",
|
||||
"@types/jsonwebtoken": "^9.0.7",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^22.10.5",
|
||||
"@types/react": "^19.0.2",
|
||||
"@types/node": "^22.13.4",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"concurrently": "^9.1.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"nodemon": "^3.1.9",
|
||||
"pm2": "^5.4.3",
|
||||
"pm2-dev": "^5.4.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup": "^4.29.1",
|
||||
"rollup": "^4.34.8",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"rollup-plugin-dts": "^6.1.1",
|
||||
"tape": "^5.9.0",
|
||||
"tsx": "^4.19.2",
|
||||
"typescript": "^5.7.2"
|
||||
"typescript": "^5.7.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"inflight": "latest",
|
||||
|
@ -1 +0,0 @@
|
||||
Subproject commit f15a9035a5ad615e15d196c933e508968276cf58
|
@ -1,35 +0,0 @@
|
||||
{
|
||||
"name": "@kevisual/ai-lang",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main2": "dist/index.js",
|
||||
"main": "src/index.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"watch": "rollup -c -w"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@abearxiong/router": "0.0.1-alpha.36",
|
||||
"@abearxiong/use-config": "^0.0.2",
|
||||
"@langchain/core": "^0.3.3",
|
||||
"@langchain/langgraph": "^0.2.9",
|
||||
"@langchain/langgraph-checkpoint-mongodb": "^0.0.3",
|
||||
"@langchain/ollama": "^0.1.0",
|
||||
"@langchain/openai": "^0.3.2",
|
||||
"mongodb": "^6.9.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^28.0.0",
|
||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||
"@rollup/plugin-typescript": "^12.1.0",
|
||||
"@types/node": "^22.7.2",
|
||||
"rollup": "^4.22.4",
|
||||
"ts-lib": "^0.0.5"
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
// rollup.config.js
|
||||
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
/**
|
||||
* @type {import('rollup').RollupOptions}
|
||||
*/
|
||||
export default {
|
||||
input: 'src/index.ts', // TypeScript 入口文件
|
||||
output: {
|
||||
file: 'dist/index.js', // 输出文件
|
||||
format: 'es', // 输出格式设置为 ES 模块
|
||||
},
|
||||
plugins: [
|
||||
resolve(), // 使用 @rollup/plugin-node-resolve 解析 node_modules 中的模块
|
||||
// commonjs(),
|
||||
typescript({
|
||||
allowImportingTsExtensions: true,
|
||||
noEmit: true,
|
||||
}), // 使用 @rollup/plugin-typescript 处理 TypeScript 文件
|
||||
],
|
||||
external: ['ws']
|
||||
};
|
@ -1,11 +0,0 @@
|
||||
import { App } from '@abearxiong/router';
|
||||
import { useConfig } from '@abearxiong/use-config';
|
||||
|
||||
const config = useConfig();
|
||||
|
||||
export const app = new App({
|
||||
serverOptions: {
|
||||
path: '/api/lang',
|
||||
},
|
||||
});
|
||||
|
@ -1,4 +0,0 @@
|
||||
export * from './app.ts';
|
||||
import './routes/agent.ts';
|
||||
import { agentManger } from './module/agent.ts';
|
||||
export { agentManger };
|
@ -1,51 +0,0 @@
|
||||
import { AiAgent, AiAgentOpts } from './create-agent.ts';
|
||||
export enum AgentMangerStatus {
|
||||
init = 'i',
|
||||
ready = 'r',
|
||||
error = 'e',
|
||||
}
|
||||
export class AgentManger {
|
||||
agents: AiAgent[] = [];
|
||||
staus: AgentMangerStatus = AgentMangerStatus.init;
|
||||
constructor() {}
|
||||
addAgent(agent: AiAgent) {
|
||||
this.agents.push(agent);
|
||||
}
|
||||
getAgent(id: string) {
|
||||
const agent = this.agents.find((agent) => agent.id === id);
|
||||
return agent;
|
||||
}
|
||||
removeAgent(id: string) {
|
||||
this.agents = this.agents.filter((agent) => agent.id !== id);
|
||||
}
|
||||
createAgent(opts: AiAgentOpts) {
|
||||
if (!opts.id) {
|
||||
const agent = new AiAgent(opts);
|
||||
this.addAgent(agent);
|
||||
return agent;
|
||||
}
|
||||
const agent = this.agents.find((agent) => agent.id === opts.id);
|
||||
if (!agent) {
|
||||
const agent = new AiAgent(opts);
|
||||
this.addAgent(agent);
|
||||
return agent;
|
||||
}
|
||||
return agent;
|
||||
}
|
||||
/**
|
||||
* 临时创建一个agent
|
||||
* @param opts
|
||||
* @returns
|
||||
*/
|
||||
newAgent(opts: AiAgentOpts) {
|
||||
return new AiAgent(opts);
|
||||
}
|
||||
createAgentList(opts: AiAgentOpts[]) {
|
||||
if (this.staus === AgentMangerStatus.init) {
|
||||
return;
|
||||
}
|
||||
this.staus = AgentMangerStatus.ready;
|
||||
return opts.map((opt) => this.createAgent(opt));
|
||||
}
|
||||
}
|
||||
export const agentManger = new AgentManger();
|
@ -1,142 +0,0 @@
|
||||
import { createReactAgent } from '@langchain/langgraph/prebuilt';
|
||||
import { MemorySaver } from '@langchain/langgraph';
|
||||
import { ChatOllama } from '@langchain/ollama';
|
||||
import { ChatOpenAI } from '@langchain/openai';
|
||||
import { client } from './mongo.ts';
|
||||
import { MongoDBSaver } from '@langchain/langgraph-checkpoint-mongodb';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { HumanMessage } from '@langchain/core/messages';
|
||||
export { HumanMessage };
|
||||
|
||||
export const agentModelList = ['qwen2.5:14b', 'qwen2.5-coder:7b', 'llama3.1:8b', 'bakllava:latest', 'gpt-4o'] as const;
|
||||
export type AiAgentModel = (typeof agentModelList)[number];
|
||||
export type AiAgentCache = 'memory' | 'mongodb';
|
||||
export type AiAgentOpts = {
|
||||
id: string;
|
||||
type: 'ollama' | 'openai';
|
||||
model: AiAgentModel;
|
||||
baseUrl: string;
|
||||
apiKey?: string;
|
||||
temperature?: number;
|
||||
cache?: AiAgentCache;
|
||||
cacheName?: string;
|
||||
};
|
||||
export type AiAgentStatus = 'ready' | 'loading' | 'error';
|
||||
// export const CreateAgent = (opts: CreateAgentOptions) => {
|
||||
// const;
|
||||
// };
|
||||
|
||||
export class AiAgent {
|
||||
agent: ReturnType<typeof createReactAgent>;
|
||||
agentModel: ChatOllama | ChatOpenAI;
|
||||
memorySaver: MemorySaver | MongoDBSaver;
|
||||
id: string;
|
||||
baseUrl: string;
|
||||
type: 'ollama' | 'openai';
|
||||
model: AiAgentModel;
|
||||
apiKey: string;
|
||||
temperature = 0;
|
||||
cache?: AiAgentCache;
|
||||
cacheName?: string;
|
||||
status?: 'ready' | 'loading' | 'error';
|
||||
constructor(opts?: AiAgentOpts) {
|
||||
this.type = opts?.type || 'ollama';
|
||||
this.baseUrl = opts?.baseUrl || 'http://localhost:11434';
|
||||
this.model = opts?.model;
|
||||
this.apiKey = opts?.apiKey;
|
||||
this.temperature = opts?.temperature || 0;
|
||||
this.cache = opts?.cache || 'mongodb';
|
||||
this.cacheName = opts?.cacheName || 'checkpointer';
|
||||
this.id = opts?.id || nanoid(8);
|
||||
if (this.type === 'openai') {
|
||||
if (!this.apiKey) {
|
||||
throw new Error('apiKey is required for openai agent');
|
||||
}
|
||||
}
|
||||
this.status = 'loading';
|
||||
this.createAgent();
|
||||
}
|
||||
createAgent() {
|
||||
this.createAgentModel();
|
||||
this.createMemoerSaver();
|
||||
if (this.status === 'error') {
|
||||
return;
|
||||
}
|
||||
|
||||
const agentModel = this.agentModel;
|
||||
const memorySaver = this.memorySaver;
|
||||
this.agent = createReactAgent({
|
||||
llm: agentModel,
|
||||
tools: [],
|
||||
checkpointSaver: memorySaver,
|
||||
});
|
||||
this.status = 'ready';
|
||||
}
|
||||
createAgentModel() {
|
||||
const type = this.type;
|
||||
const model = this.model;
|
||||
const temperature = this.temperature;
|
||||
const apiKey = this.apiKey;
|
||||
const baseUrl = this.baseUrl;
|
||||
let agentModel;
|
||||
try {
|
||||
if (type === 'ollama') {
|
||||
agentModel = new ChatOllama({ temperature, model, baseUrl });
|
||||
} else if (type === 'openai') {
|
||||
agentModel = new ChatOpenAI(
|
||||
{ temperature, model, apiKey },
|
||||
{
|
||||
baseURL: baseUrl,
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('loading model error', e);
|
||||
this.status = 'error';
|
||||
return;
|
||||
}
|
||||
this.agentModel = agentModel;
|
||||
return this;
|
||||
}
|
||||
createMemoerSaver() {
|
||||
const cache = this.cache;
|
||||
const cacheName = this.cacheName;
|
||||
let memorySaver;
|
||||
try {
|
||||
if (cache === 'memory') {
|
||||
memorySaver = new MemorySaver();
|
||||
} else if (cache === 'mongodb') {
|
||||
memorySaver = new MongoDBSaver({ client, dbName: cacheName });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.status = 'error';
|
||||
return;
|
||||
}
|
||||
this.memorySaver = memorySaver;
|
||||
}
|
||||
sendHumanMessage(message: string, opts?: { thread_id: string }) {
|
||||
const mesage = new HumanMessage(message);
|
||||
return this.agent.invoke({ messages: [mesage] }, { configurable: { thread_id: 'test_human', ...opts } });
|
||||
}
|
||||
close() {
|
||||
// 清除 memory saver
|
||||
this.memorySaver = null;
|
||||
this.agentModel = null;
|
||||
this.agent = null;
|
||||
}
|
||||
async testQuery() {
|
||||
const id = this.id;
|
||||
try {
|
||||
const agent = this.agent;
|
||||
const message = new HumanMessage('你好');
|
||||
const res = await agent.invoke({ messages: [message] }, { configurable: { thread_id: 'test_ping' } });
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`test query [${id}]:`, e);
|
||||
this.status = 'error';
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
import { MongoClient } from 'mongodb';
|
||||
import { useConfig } from '@abearxiong/use-config';
|
||||
const { mongo } = useConfig<{ host: string; password: string; username: string }>();
|
||||
export const client = new MongoClient(`mongodb://${mongo.username}:${mongo.password}@${mongo.host}`, {});
|
||||
|
||||
// 当连接成功时,打印出连接成功的信息
|
||||
client
|
||||
.connect()
|
||||
.then(() => {
|
||||
console.log('mongo Connected successfully to server');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
@ -1,19 +0,0 @@
|
||||
import { CustomError } from '@abearxiong/router';
|
||||
import { app } from '../app.ts';
|
||||
// import { agent, HumanMessage } from '../agent/index.ts';
|
||||
import { agentManger } from '../module/agent.ts';
|
||||
app
|
||||
.route('ai', 'chat')
|
||||
.define(async (ctx) => {
|
||||
const { message, agentId, chatId } = ctx.query.data;
|
||||
// const response = await agent.invoke({ messages: [new HumanMessage(message)] }, { configurable: { thread_id: '44' } });
|
||||
// ctx.body = response;
|
||||
//
|
||||
const agent = agentManger.getAgent(agentId);
|
||||
if (!agent) {
|
||||
throw new CustomError('agent not found');
|
||||
}
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
// app.router.parse({})
|
@ -1,32 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "nodenext",
|
||||
"target": "esnext",
|
||||
"noImplicitAny": false,
|
||||
"outDir": "./dist",
|
||||
"sourceMap": false,
|
||||
"allowJs": true,
|
||||
"newLine": "LF",
|
||||
"baseUrl": "./",
|
||||
"typeRoots": [
|
||||
"node_modules/@types",
|
||||
],
|
||||
"declaration": true,
|
||||
"noEmit": false,
|
||||
"allowImportingTsExtensions": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"moduleResolution": "NodeNext",
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"esModuleInterop": true,
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
],
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [],
|
||||
}
|
@ -1 +0,0 @@
|
||||
Subproject commit 79a9568a87536aa8e467182f371c95b8a2f25b8b
|
4172
pnpm-lock.yaml
generated
4172
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,16 +0,0 @@
|
||||
import * as dts from 'dts-bundle';
|
||||
import path from 'path';
|
||||
|
||||
const currentPath = process.cwd();
|
||||
const packagePath = path.join(currentPath, 'script/package/package.json');
|
||||
|
||||
const mainPath = path.join(currentPath, 'src/type.ts');
|
||||
const outPath = path.join(currentPath, 'types/index.d.ts');
|
||||
// node script/dts/index.mjs
|
||||
dts.bundle({
|
||||
name: 'my-library',
|
||||
main: mainPath,
|
||||
out: outPath,
|
||||
removeSource: true,
|
||||
outputAsModuleFolder: true,
|
||||
});
|
@ -8,15 +8,23 @@ fs.writeFileSync(
|
||||
packagePath,
|
||||
JSON.stringify(
|
||||
{
|
||||
name: 'codeflow',
|
||||
name: 'codecenter',
|
||||
version: '1.0.0',
|
||||
devDependencies: {
|
||||
'@babel/core': '^7.24.7',
|
||||
'@babel/preset-env': '^7.24.7',
|
||||
'@babel/preset-typescript': '^7.24.7',
|
||||
sequelize: '^6.37.3',
|
||||
'socket.io': '^4.7.5',
|
||||
pg: '^8.12.0',
|
||||
scripts: {
|
||||
start: 'pm2 start dist/app.mjs --name codecenter',
|
||||
},
|
||||
dependencies: {
|
||||
'@kevisual/router': '^0.0.6-alpha-5',
|
||||
'@kevisual/use-config': '^1.0.7',
|
||||
ioredis: '^5.5.0',
|
||||
minio: '^8.0.4',
|
||||
pg: '^8.13.3',
|
||||
sequelize: '^6.37.5',
|
||||
sqlite3: '^5.1.7',
|
||||
'socket.io': '^4.8.1',
|
||||
'@msgpack/msgpack': '3.0.1',
|
||||
pino: '^9.6.0',
|
||||
'pino-pretty': '^13.0.0',
|
||||
},
|
||||
},
|
||||
null,
|
||||
|
@ -1,17 +1,20 @@
|
||||
{
|
||||
"name": "codeflow",
|
||||
"name": "codecenter",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"start": "pm2 start dist/app.mjs --name codecenter"
|
||||
},
|
||||
"dependencies": {
|
||||
"@kevisual/router": "^0.0.6-alpha-2",
|
||||
"@kevisual/use-config": "^1.0.3",
|
||||
"ioredis": "^5.4.1",
|
||||
"minio": "^8.0.2",
|
||||
"pg": "^8.13.1",
|
||||
"@kevisual/router": "^0.0.6-alpha-5",
|
||||
"@kevisual/use-config": "^1.0.7",
|
||||
"ioredis": "^5.5.0",
|
||||
"minio": "^8.0.4",
|
||||
"pg": "^8.13.3",
|
||||
"sequelize": "^6.37.5",
|
||||
"sqlite3": "^5.1.7",
|
||||
"socket.io": "^4.8.1",
|
||||
"@msgpack/msgpack": "3.0.0-beta2",
|
||||
"pino": "^9.5.0",
|
||||
"@msgpack/msgpack": "3.0.1",
|
||||
"pino": "^9.6.0",
|
||||
"pino-pretty": "^13.0.0"
|
||||
}
|
||||
}
|
@ -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 };
|
||||
|
41
types/index.d.ts
vendored
41
types/index.d.ts
vendored
@ -1,41 +0,0 @@
|
||||
// Generated by dts-bundle-generator v9.5.1
|
||||
|
||||
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;
|
||||
};
|
||||
declare enum RouterCodeType {
|
||||
route = "route",
|
||||
middleware = "middleware"
|
||||
}
|
||||
declare enum CodeStatus {
|
||||
running = "running",
|
||||
stop = "stop",
|
||||
fail = "fail"
|
||||
}
|
||||
export type CodeManager = {
|
||||
fn?: any;
|
||||
status?: CodeStatus;
|
||||
errorMsg?: string;
|
||||
lock?: boolean;
|
||||
} & Partial<RouterCode>;
|
||||
export interface ContainerData {
|
||||
style?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
className?: string;
|
||||
showChild?: boolean;
|
||||
shadowRoot?: boolean;
|
||||
}
|
||||
|
||||
export {};
|
Loading…
x
Reference in New Issue
Block a user