This commit is contained in:
2026-01-13 14:02:03 +08:00
parent 03f24318d2
commit 78af49906e
14 changed files with 338 additions and 323 deletions

View File

@@ -7,6 +7,7 @@ import { handleServer } from './server/handle-server.ts';
import { IncomingMessage, ServerResponse } from 'http';
import { isBun } from './utils/is-engine.ts';
import { BunServer } from './server/server-bun.ts';
import { nanoid } from 'nanoid';
type RouterHandle = (msg: { path: string;[key: string]: any }) => { code: string; data?: any; message?: string;[key: string]: any };
type AppOptions<T = {}> = {
@@ -16,6 +17,7 @@ type AppOptions<T = {}> = {
routerHandle?: RouterHandle;
routerContext?: RouteContext<T>;
serverOptions?: ServerNodeOpts;
appId?: string;
};
export type AppRouteContext<T = {}> = HandleCtx & RouteContext<T> & { app: App<T> };
@@ -25,6 +27,7 @@ export type AppRouteContext<T = {}> = HandleCtx & RouteContext<T> & { app: App<T
* U - Route Context的扩展类型
*/
export class App<U = {}> {
appId: string;
router: QueryRouter;
server: ServerType;
constructor(opts?: AppOptions<U>) {
@@ -42,6 +45,12 @@ export class App<U = {}> {
router.setContext({ needSerialize: true, ...opts?.routerContext });
this.router = router;
this.server = server;
if (opts?.appId) {
this.appId = opts.appId;
} else {
this.appId = nanoid(16);
}
router.appId = this.appId;
}
listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void): void;
listen(port: number, hostname?: string, listeningListener?: () => void): void;

19
src/auto/index.ts Normal file
View File

@@ -0,0 +1,19 @@
import { loadTS, getMatchFiles } from './load-ts.ts';
import { listenSocket } from './listen-sock.ts';
import { Route, QueryRouter, QueryRouterServer } from '../route.ts';
export { Route, QueryRouter, QueryRouterServer };
export const App = QueryRouterServer;
export { createSchema } from './../index.ts';
export type { Rule } from '../validator/rule.ts';
export type { RouteContext, RouteOpts } from '../route.ts';
export type { Run } from '../route.ts';
export { CustomError } from '../result/error.ts';
export { listenSocket, loadTS, getMatchFiles };
export { autoCall } from './call-sock.ts';

View File

@@ -1,40 +0,0 @@
import { QueryRouter } from "./route.ts";
type RouterChatOptions = {
router?: QueryRouter;
}
export class RouterChat {
router: QueryRouter;
prompt: string = '';
constructor(opts?: RouterChatOptions) {
this.router = opts?.router || new QueryRouter();
}
prefix(wrapperFn?: (routes: any[]) => string) {
if (this.prompt) {
return this.prompt;
}
let _prompt = `你是一个调用函数工具的助手,当用户询问时,如果拥有工具,请返回 JSON 数据,数据的值的内容是 id 和 payload 。如果有参数,请放到 payload 当中。
下面是你可以使用的工具列表:
`;
if (!wrapperFn) {
_prompt += this.router.routes.map(r => `工具名称: ${r.id}\n描述: ${r.description}\n`).join('\n');
} else {
_prompt += wrapperFn(this.router.exportRoutes());
}
_prompt += `当你需要使用工具时,请严格按照以下格式返回:
{
"id": "工具名称",
"payload": {
// 参数列表
}
}
如果你不需要使用工具,直接返回用户想要的内容即可,不要返回任何多余的信息。`;
return _prompt;
}
chat() {
const prompt = this.prefix();
return prompt;
}
}

57
src/modules/chat.ts Normal file
View File

@@ -0,0 +1,57 @@
import { QueryRouter } from "../route.ts";
import { filter } from '@kevisual/js-filter'
type RouterChatOptions = {
router?: QueryRouter;
}
export class RouterChat {
router: QueryRouter;
prompt: string = '';
constructor(opts?: RouterChatOptions) {
this.router = opts?.router || new QueryRouter();
}
prefix(opts?: { query?: string }) {
if (this.prompt) {
return this.prompt;
}
let _routes = this.router.routes;
if (opts?.query) {
_routes = filter(this.router.routes, opts.query);
}
const toolsList = _routes.map((r, index) =>
`${index + 1}. 工具名称: ${r.id}\n 描述: ${r.description}`
).join('\n\n');
const _prompt = `你是一个 AI 助手,你可以使用以下工具来帮助用户完成任务:
${toolsList}
## 回复规则
1. 如果用户的请求可以使用上述工具完成,请返回 JSON 格式数据
2. 如果没有合适的工具,请直接分析并回答用户问题
## JSON 数据格式
\`\`\`json
{
"id": "工具的id",
"payload": {
// 工具所需的参数(如果需要)
// 例如: "id": "xxx", "name": "xxx"
}
}
\`\`\`
注意:
- payload 中包含工具执行所需的所有参数
- 如果工具不需要参数payload 可以为空对象 {}
- 确保返回的 id 与上述工具列表中的工具名称完全匹配`
this.prompt = _prompt;
return _prompt;
}
recreate() {
this.prompt = '';
}
getChatPrompt() {
const prompt = this.prefix();
return prompt;
}
}

View File

@@ -5,6 +5,11 @@ import { listenProcess } from './utils/listen-process.ts';
export type RouterContextT = { code?: number;[key: string]: any };
export type RouteContext<T = { code?: number }, S = any> = {
/**
* 本地自己调用的时候使用,可以标识为当前自调用,那么 auth 就不许重复的校验
* 或者不需要登录的,直接调用
*/
appId?: string;
// run first
query?: { [key: string]: any };
// response body
@@ -217,6 +222,7 @@ export class Route<U = { [key: string]: any }, T extends SimpleObject = SimpleOb
}
export class QueryRouter {
appId: string = '';
routes: Route[];
maxNextRoute = 40;
context?: RouteContext = {}; // default context for call
@@ -555,6 +561,21 @@ export class QueryRouter {
hasRoute(path: string, key: string = '') {
return this.routes.find((r) => r.path === path && r.key === key);
}
findRoute(opts?: { path?: string; key?: string; id?: string }) {
const { path, key, id } = opts || {};
return this.routes.find((r) => {
if (id) {
return r.id === id;
}
if (path) {
if (key !== undefined) {
return r.path === path && r.key === key;
}
return r.path === path;
}
return false;
});
}
createRouteList(force: boolean = false, filter?: (route: Route) => boolean) {
const hasListRoute = this.hasRoute('router', 'list');
if (!hasListRoute || force) {
@@ -594,6 +615,7 @@ export class QueryRouter {
type QueryRouterServerOpts = {
handleFn?: HandleFn;
context?: RouteContext;
appId?: string;
};
interface HandleFn<T = any> {
(msg: { path: string;[key: string]: any }, ctx?: any): { code: string; data?: any; message?: string;[key: string]: any };
@@ -604,11 +626,17 @@ interface HandleFn<T = any> {
* @description 移除server相关的功能只保留router相关的功能和http.createServer不相关独立
*/
export class QueryRouterServer extends QueryRouter {
declare appId: string;
handle: any;
constructor(opts?: QueryRouterServerOpts) {
super();
this.handle = this.getHandle(this, opts?.handleFn, opts?.context);
this.setContext({ needSerialize: false, ...opts?.context });
if (opts?.appId) {
this.appId = opts.appId;
} else {
this.appId = nanoid(16);
}
}
setHandle(wrapperFn?: HandleFn, ctx?: RouteContext) {
this.handle = this.getHandle(this, wrapperFn, ctx);

View File

@@ -1,3 +0,0 @@
import { parseXml } from './server/parse-xml.ts';
export { parseXml };

View File

@@ -1,59 +0,0 @@
import { generate } from 'selfsigned';
export type Attributes = {
name: string;
value: string;
};
export type AltNames = {
type: number;
value?: string;
ip?: string;
};
export const createCert = async(attrs: Attributes[] = [], altNames: AltNames[] = []) => {
let attributes = [
{ name: 'countryName', value: 'CN' }, // 国家代码
{ name: 'stateOrProvinceName', value: 'ZheJiang' }, // 州名
{ name: 'localityName', value: 'HangZhou' }, // 城市名
{ name: 'organizationName', value: 'kevisual' }, // 组织名
{ name: 'organizationalUnitName', value: 'kevisual' }, // 组织单位
...attrs,
];
// attribute 根据name去重复, 后面的覆盖前面的
attributes = Object.values(
attributes.reduce(
(acc, attr) => ({
...acc,
[attr.name]: attr,
}),
{} as Record<string, Attributes>,
),
);
const options = {
days: 365, // 证书有效期(天)
extensions: [
{
name: 'subjectAltName',
altNames: [
{ type: 2, value: '*' }, // DNS 名称
{ type: 2, value: 'localhost' }, // DNS
{
type: 2,
value: '[::1]',
},
{
type: 7,
ip: 'fe80::1',
},
{ type: 7, ip: '127.0.0.1' }, // IP 地址
...altNames,
],
},
],
};
const pems = await generate(attributes, options);
return {
key: pems.private,
cert: pems.cert,
};
};

View File

@@ -1,5 +1,5 @@
import { App } from '../app.ts'
import { RouterChat } from '@/chat.ts';
import { RouterChat } from '@/modules/chat.ts';
const app = new App();