"feat: 增加助手应用配置管理功能与服务器守护进程支持"

This commit is contained in:
2025-04-27 00:35:44 +08:00
parent bcc12209e0
commit f2abfbf17c
27 changed files with 658 additions and 102 deletions

View File

@@ -15,8 +15,10 @@ const Init = new Command('init')
} else if (opts.path) {
opts.path = path.resolve(opts.path);
}
const configDir = AssistantInit.detectConfigDir(opts.path);
console.log('configDir', configDir);
const assistantInit = new AssistantInit({
path: opts.path,
path: configDir,
});
assistantInit.init();
});

View File

@@ -1,5 +1,5 @@
import { program, runProgram } from '@/program.ts';
import './command/init/index.ts';
import './command/config-manager/index.ts';
import './command/app-manager/index.ts';
/**

View File

@@ -37,7 +37,7 @@ export const initConfig = (configRootPath: string) => {
*/
appsDir,
/**
* 服务配置文件路径 assistant-service-config.json
* 服务配置文件路径 assistant-apps-config.json
*/
appsConfigPath,
/**
@@ -65,6 +65,15 @@ type AssistantConfigData = {
proxy?: ProxyInfo[];
apiProxyList?: ProxyInfo[];
description?: string;
/**
* 首页
*/
home?: string;
ai?: {
provider?: string | 'DeepSeek' | 'SiliconFlow';
apiKey?: string;
model?: string;
};
};
let assistantConfig: AssistantConfigData;
type AssistantConfigOptions = {
@@ -142,7 +151,7 @@ export class AssistantConfig {
fs.writeFileSync(pageConfigPath, JSON.stringify(_saveConfig, null, 2));
return _saveConfig;
}
assAppConfig(app: any) {
addAppConfig(app: any) {
const config = this.getPageConfig();
const assistantConfig = this.getCacheAssistantConfig();
const _apps = config.list;

View File

@@ -0,0 +1,3 @@
import { runServer } from "./server.ts";
runServer();

View File

@@ -1,10 +1,71 @@
import { app } from './app.ts';
import { proxyRoute, proxyWs } from './services/proxy/proxy-page-index.ts';
import getPort, { portNumbers } from 'get-port';
import { program } from 'commander';
import { spawnSync } from 'child_process';
export const runServer = async (port?: number) => {
let _port: number | undefined;
if (port) {
_port = await getPort({ port });
if (_port !== port) {
console.log(`Port ${port} is not available`);
_port = undefined;
}
}
if (!_port) {
// 检车端口可用性
const isPortAvailable = await getPort({ port: portNumbers(51015, 52000) });
if (!isPortAvailable) {
console.log(`Port ${isPortAvailable} is not available`);
process.exit(1);
}
_port = isPortAvailable;
}
app.listen(_port, () => {
console.log(`Server is running on https://localhost:${_port}`);
});
app.server.on(proxyRoute);
proxyWs();
return {
app,
port: _port,
};
};
program
.description('启动服务')
.option('-d, --daemon', '是否以守护进程方式运行')
.option('-n, --name <name>', '服务名称', 'assistant-server')
.option('-p, --port <port>', '服务端口')
.option('-s, --start', '是否启动服务')
.action(async (options) => {
// console.log('当前执行路径:', execPath, inte);
if (options.daemon) {
const [_interpreter, execPath] = process.argv;
const name = options.name;
const port = options.port;
let pm2Command = `pm2 start ${execPath} --name ${name} -- -s `;
if (port) {
pm2Command += ` -p ${port}`;
}
const result = spawnSync(pm2Command, {
shell: true,
stdio: 'inherit',
});
if (result.error) {
console.error('Error starting server:', result.error);
process.exit(1);
}
console.log('以守护进程方式运行');
} else if (options.start) {
console.log('启动服务');
const server = await runServer(options.port);
}
});
app.listen(51015, () => {
console.log('Server is running on http://localhost:51015');
});
app.server.on(proxyRoute);
proxyWs();
export const runParser = async (argv: string[]) => {
try {
program.parse(argv);
} catch (error) {
console.error('执行错误:', error.message);
}
};

View File

@@ -4,6 +4,7 @@ import { checkFileExists, AssistantConfig } from '@/module/assistant/index.ts';
import { chalk } from '@/module/chalk.ts';
export type AssistantInitOptions = {
path?: string;
init?: boolean;
};
/**
* 助手初始化类
@@ -14,6 +15,7 @@ export class AssistantInit extends AssistantConfig {
const configDir = opts?.path || process.cwd();
super({
configDir,
init: opts?.init ?? false,
});
}
@@ -30,21 +32,30 @@ export class AssistantInit extends AssistantConfig {
}
}
checkConfigPath() {
const assistantPath = path.join(this.configDir, 'assistant-config.json');
const assistantPath = path.join(this.configDir, 'assistant-app', 'assistant-config.json');
return checkFileExists(assistantPath);
}
createAssistantConfig() {
const assistantPath = this.configPath?.configPath;
// 创建助手配置文件 assistant-config.json
if (!checkFileExists(assistantPath, true)) {
this.setConfig({
description: '助手配置文件',
});
console.log(chalk.green('助手配置文件创建成功'));
console.log(chalk.green('助手配置文件assistant-config.json创建成功'));
}
const env = this.configPath?.envConfigPath;
// 创建助手环境配置文件 env
if (!checkFileExists(env, true)) {
fs.writeFileSync(env, '# 环境配置文件\n');
console.log(chalk.green('助手环境配置文件创建成功'));
console.log(chalk.green('助手环境配置.env文件创建成功'));
}
const appsConfig = this.configPath?.appsConfigPath;
// 创建助手应用配置文件 apps
if (!checkFileExists(appsConfig, true)) {
fs.writeFileSync(appsConfig, JSON.stringify({ description: 'apps manager.', list: [] }));
console.log(chalk.green('助手应用配置文件apps.json创建成功'));
}
}
}

View File

@@ -11,6 +11,10 @@ export const proxyRoute = async (req: http.IncomingMessage, res: http.ServerResp
const appDir = assistantConfig.configPath?.pageDir;
const url = new URL(req.url, 'http://localhost');
const pathname = url.pathname;
if (pathname === '/' && _assistantConfig?.home) {
res.writeHead(302, { Location: `${_assistantConfig?.home}/` });
return res.end();
}
if (pathname.startsWith('/favicon.ico')) {
res.statusCode = 404;
res.end('Not Found Favicon');

View File

@@ -0,0 +1,53 @@
// import { AssistantConfig } from '@/module/assistant/index.ts';
import { assistantConfig } from '../config.ts';
import { ProviderManager, SiliconFlowProvider } from '@kevisual/ai-center';
const config = assistantConfig.getConfig();
console.log('aiConfig', config.ai);
const providerManager = new ProviderManager({
provider: config.ai.provider,
apiKey: config.ai.apiKey!,
model: config.ai.model!,
});
const chatTest = async (text: string) => {
const result = await providerManager.provider.chat([
{
role: 'user',
content: text,
},
]);
console.log('result', result);
return result.choices[0].message.content;
};
chatTest('你好').then((res) => {
console.log('chatTest', res);
});
// providerManager.provider.test().then((res) => {
// console.log('test', res);
// });
// const siliconflow = providerManager.provider as any;
const siliconflow = new SiliconFlowProvider({
apiKey: config.ai.apiKey!,
model: config.ai.model!,
});
const main = async () => {
const usage = await siliconflow.getUsageInfo();
console.log(usage);
};
// main();
const test = async () => {
const result = await siliconflow.chat([
{
role: 'user',
content: '你好',
},
]);
console.log('result', result, 'result.choices[0].message.content', result.choices[0].message.content);
return result.choices[0].message.content;
};
// test();

View File

@@ -0,0 +1,22 @@
import { ProviderManager, SiliconFlowProvider, ModelScopeProvider } from '@kevisual/ai-center';
import { config } from 'dotenv';
config();
const providerTest = async () => {
const providerConfig = { provider: 'ModelScope', model: 'Qwen/Qwen2.5-Coder-32B-Instruct', apiKey: process.env.MODEL_SCOPE_API_KEY };
const provider = await ProviderManager.createProvider(providerConfig);
const result = await provider.chat([{ role: 'user', content: '你好' }]);
console.log(result);
};
providerTest();
const modelScopeTest = async () => {
const provider = new ModelScopeProvider({
apiKey: process.env.MODEL_SCOPE_API_KEY,
model: 'Qwen/Qwen2.5-Coder-32B-Instruct',
});
const result = await provider.chat([{ role: 'user', content: '你好' }]);
console.log(result);
};
// modelScopeTest();