feat: add neo4j

This commit is contained in:
xion 2024-09-25 14:02:45 +08:00
parent 4cbc72def7
commit 25c055b490
16 changed files with 554 additions and 113 deletions

View File

@ -31,11 +31,12 @@
],
"license": "ISC",
"dependencies": {
"@abearxiong/router": "0.0.1-alpha.33",
"@abearxiong/router": "0.0.1-alpha.34",
"@abearxiong/use-config": "^0.0.2",
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.4",
"@babel/preset-typescript": "^7.24.7",
"@kevisual/ai-graph": "workspace:^",
"@types/semver": "^7.5.8",
"dayjs": "^1.11.13",
"dts-bundle-generator": "^9.5.1",
@ -45,6 +46,9 @@
"lodash-es": "^4.17.21",
"minio": "^8.0.1",
"nanoid": "^5.0.7",
"neo4j-driver": "^5.24.1",
"neode": "^0.4.9",
"ollama": "^0.5.9",
"pg": "^8.13.0",
"semver": "^7.6.3",
"sequelize": "^6.37.3",
@ -58,9 +62,10 @@
"@types/jest": "^29.5.13",
"@types/jsonwebtoken": "^9.0.7",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.5.5",
"@types/node": "^22.6.1",
"@types/superagent": "^8.1.9",
"@types/supertest": "^6.0.2",
"@types/uuid": "^10.0.0",
"@types/webpack-env": "^1.18.5",
"concurrently": "^9.0.1",
"copy-webpack-plugin": "^12.0.2",
@ -79,5 +84,10 @@
"webpack-cli": "^5.1.4",
"webpack-node-externals": "^3.0.0"
},
"resolutions": {
"glob": "latest",
"inflight": "latest",
"rimraf": "latest"
},
"pnpm": {}
}

@ -1 +1 @@
Subproject commit 492d1b0e7ccfce266caa5ea38d6cc8162e4ef11f
Subproject commit d1e2306233fde79b510dec614a95f867d686243d

455
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

2
pnpm-workspace.yaml Normal file
View File

@ -0,0 +1,2 @@
packages:
- 'packages/*'

View File

@ -12,7 +12,7 @@ export type CodeManager = {
fn?: any;
status?: CodeStatus;
errorMsg?: string;
lock?: boolean; // 是否锁定
lock?: boolean; // 是否锁定
} & Partial<RouterCode>;
const codeDemoRun = `async function run(ctx) {
@ -64,7 +64,11 @@ export const loadOne = async (item: RouterCodeModel) => {
}
};
export const load = async function () {
const codes = await RouterCodeModel.findAll();
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) {

View File

@ -1,6 +1,6 @@
// admin router manger
import { Route } from '@abearxiong/router';
import { CustomError, Route } from '@abearxiong/router';
import { router } from '../modules/router.ts';
import { manager, updateNewCode, removeCode, stopCode, startCode } from './dashboard/manager.ts';
import { loadOne } from './dashboard/load.ts';
@ -143,9 +143,8 @@ updateRouter.run = async (ctx) => {
await newCodeRouter.save();
codeRouter = newCodeRouter;
} catch (e) {
ctx.body = e.message.toString();
ctx.code = 500;
return ctx;
console.error('updateRouter', e);
throw new CustomError(e.message.toString());
}
}

View File

@ -1,12 +1,15 @@
import { App } from '@abearxiong/router';
import { useConfig } from '@abearxiong/use-config';
import { dynamicImport } from './lib/dynamic-import.ts';
import { redisPublisher, redisSubscriber } from './modules/redis.ts';
import { redisPublisher, redisSubscriber, redis } from './modules/redis.ts';
import { neode } from './modules/neo4j.ts';
import { minioClient } from './modules/minio.ts';
import { sequelize } from './modules/sequelize.ts';
useConfig();
export const emit = (channel: string, message?: any) => {
redisPublisher.publish(channel, JSON.stringify(message));
};
export { neode, redis, minioClient, sequelize };
export const app = new App<{ import: any; emit: typeof emit }>({
serverOptions: {

View File

@ -41,7 +41,7 @@ RouterCodeModel.init(
type: DataTypes.UUID,
primaryKey: true,
defaultValue: DataTypes.UUIDV4,
comment: '用户id',
comment: '用户code id',
},
path: {
type: DataTypes.STRING,
@ -60,11 +60,11 @@ RouterCodeModel.init(
defaultValue: 'default',
},
code: {
type: DataTypes.STRING,
type: DataTypes.TEXT,
defaultValue: '',
},
exec: {
type: DataTypes.STRING, // 对代码进行编译后的代码
type: DataTypes.TEXT, // 对代码进行编译后的代码
defaultValue: '',
},
type: {
@ -93,5 +93,7 @@ RouterCodeModel.init(
tableName: 'cf_router_code',
},
);
// RouterCodeModel.sync({ alter: true });
RouterCodeModel.sync({ alter: true, logging: false }).catch((e) => {
console.error('RouterCodeModel sync', e);
});
// RouterCodeModel.sync({force: true});

18
src/models/prompt.ts Normal file
View File

@ -0,0 +1,18 @@
import { neode } from '@/app.ts';
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([]) },
});

29
src/modules/neo4j.ts Normal file
View File

@ -0,0 +1,29 @@
import Neode from 'neode';
import { useConfig } from '@abearxiong/use-config';
type NeodeConfig = {
uri: string;
username: string;
password: string;
};
const { neo4j } = useConfig<{ neo4j: NeodeConfig }>('neo4j');
const { uri, username, password } = neo4j;
// 设置连接配置
// const neode = new Neode('bolt://localhost:7687', 'neo4j', 'your_password');
export const neode = new Neode(uri, username, password);
const testConnect = async () => {
// 连接成功
// 尝试执行简单的 Cypher 查询以测试连接
neode
.cypher('RETURN 1', {})
.then(() => {
console.log('connect neo4j success');
})
.catch((err) => {
console.error('Failed to connect:', err);
});
};
testConnect();

30
src/modules/ollama.ts Normal file
View File

@ -0,0 +1,30 @@
import { useConfig } from '@abearxiong/use-config';
import { Ollama, Message, ChatRequest } from 'ollama';
const config = useConfig<{ ollama: Ollama['config'] & { model: string } }>();
const { host } = 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: config.model,
options: {
temperature: 0,
...chatOpts?.options,
},
...rest,
});
};

View File

@ -5,7 +5,7 @@ const config = useConfig<{
redis: ConstructorParameters<typeof Redis>;
}>();
// 配置 Redis 连接
const redis = new Redis({
export const redis = new Redis({
host: 'localhost', // Redis 服务器的主机名或 IP 地址
port: 6379, // Redis 服务器的端口号
// password: 'your_password', // Redis 的密码 (如果有)

View File

@ -26,10 +26,6 @@ export class ContainerModel extends Model {
declare data: ContainerData;
declare publish: ContainerPublish;
declare uid: string;
// timestamps
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
}
ContainerModel.init(
{

View File

@ -3,3 +3,5 @@ import './container/index.ts';
import './page/index.ts';
import './resource/index.ts';
import './prompt-graph/index.ts';

View File

@ -0,0 +1 @@
import './list.ts'

View File

@ -0,0 +1,76 @@
import { PromptNeo } from '@/models/prompt.ts';
import { app } from '@/app.ts';
import { v4 } from 'uuid';
app
.route('prompt', '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', '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', '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', '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', 'createDemo')
.define(async (ctx) => {
const promptData = {
id: v4(),
title: 'test',
description: '这是测试保存prompt的数据',
prompt: '这是测试保存prompt的数据',
inputVariables: JSON.stringify([{ key: 'test', value: 'test' }]),
localVariables: JSON.stringify([{ key: 'test', value: 'test' }]),
};
const prompt = await PromptNeo.create(promptData);
ctx.body = await prompt.toJson();
})
.addTo(app);