update
This commit is contained in:
@@ -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
19
src/auto/index.ts
Normal 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';
|
||||
40
src/chat.ts
40
src/chat.ts
@@ -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
57
src/modules/chat.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
28
src/route.ts
28
src/route.ts
@@ -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);
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import { parseXml } from './server/parse-xml.ts';
|
||||
|
||||
export { parseXml };
|
||||
59
src/sign.ts
59
src/sign.ts
@@ -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,
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { App } from '../app.ts'
|
||||
import { RouterChat } from '@/chat.ts';
|
||||
import { RouterChat } from '@/modules/chat.ts';
|
||||
|
||||
const app = new App();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user