@axiom-lattice/gateway 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,233 @@
1
+ import { FastifyRequest, FastifyReply } from "fastify";
2
+ import * as agentService from "../services/agent_service";
3
+ import { CreateRunRequest } from "../types";
4
+ import { v4 } from "uuid";
5
+
6
+ // 创建运行
7
+ export const createRun = async (
8
+ request: FastifyRequest,
9
+ reply: FastifyReply
10
+ ): Promise<void> => {
11
+ try {
12
+ const {
13
+ assistant_id,
14
+ thread_id,
15
+ command,
16
+ streaming,
17
+ background,
18
+ ...input
19
+ } = request.body as CreateRunRequest;
20
+
21
+ const tenant_id = request.headers["x-tenant-id"] as string;
22
+ const x_request_id = (request.headers["x-request-id"] as string) || v4();
23
+
24
+ // 验证请求数据
25
+ if (!assistant_id) {
26
+ reply.status(400).send({
27
+ success: false,
28
+ error: "助手ID是必需的",
29
+ });
30
+ return;
31
+ }
32
+
33
+ // 如果请求streaming,则agent_stream
34
+ if (streaming) {
35
+ // 开始运行
36
+ const stream = await agentService.agent_stream({
37
+ assistant_id: assistant_id,
38
+ input: input,
39
+ thread_id: thread_id,
40
+ command,
41
+ tenant_id: tenant_id,
42
+ run_id: x_request_id,
43
+ });
44
+
45
+ // 设置 SSE 响应头
46
+ reply.raw.writeHead(200, {
47
+ "Content-Type": "text/event-stream",
48
+ "Cache-Control": "no-cache",
49
+ Connection: "keep-alive",
50
+ "Access-Control-Allow-Origin": "*",
51
+ });
52
+
53
+ try {
54
+ for await (const chunk of stream) {
55
+ reply.raw.write(`data: ${JSON.stringify(chunk)}\n\n`);
56
+ }
57
+ } catch (error) {
58
+ //console.error("Stream processing error:", error);
59
+ } finally {
60
+ reply.raw.end();
61
+ // 通知 Fastify 我们将手动处理响应
62
+ return reply.hijack();
63
+ }
64
+ } else {
65
+ // 后台运行的情况
66
+ const result = await agentService.agent_invoke({
67
+ assistant_id: assistant_id,
68
+ input: input,
69
+ command: command,
70
+ thread_id: thread_id,
71
+ tenant_id: tenant_id,
72
+ run_id: x_request_id,
73
+ });
74
+ reply.status(200).send(result);
75
+ }
76
+ } catch (error: any) {
77
+ reply.status(500).send({
78
+ success: false,
79
+ error: `创建运行时发生错误: ${error.message}`,
80
+ });
81
+ }
82
+ };
83
+
84
+ // // 获取运行
85
+ // export const getRun = async (
86
+ // request: FastifyRequest,
87
+ // reply: FastifyReply
88
+ // ): Promise<void> => {
89
+ // try {
90
+ // const { id } = request.params as { id: string };
91
+
92
+ // if (!id) {
93
+ // reply.status(400).send({
94
+ // success: false,
95
+ // error: "运行ID是必需的",
96
+ // });
97
+ // return;
98
+ // }
99
+
100
+ // const result = await runModel.getRun(id);
101
+
102
+ // if (!result.success) {
103
+ // reply.status(404).send(result);
104
+ // return;
105
+ // }
106
+
107
+ // reply.send(result);
108
+ // } catch (error: any) {
109
+ // reply.status(500).send({
110
+ // success: false,
111
+ // error: `获取运行时发生错误: ${error.message}`,
112
+ // });
113
+ // }
114
+ // };
115
+
116
+ // // 获取助手的所有运行
117
+ // export const getRunsByAssistant = async (
118
+ // request: FastifyRequest,
119
+ // reply: FastifyReply
120
+ // ): Promise<void> => {
121
+ // try {
122
+ // const { assistantId } = request.params as { assistantId: string };
123
+
124
+ // if (!assistantId) {
125
+ // reply.status(400).send({
126
+ // success: false,
127
+ // error: "助手ID是必需的",
128
+ // });
129
+ // return;
130
+ // }
131
+
132
+ // const result = await runModel.getRunsByAssistant(assistantId);
133
+
134
+ // reply.send(result);
135
+ // } catch (error: any) {
136
+ // reply.status(500).send({
137
+ // success: false,
138
+ // error: `获取助手运行时发生错误: ${error.message}`,
139
+ // });
140
+ // }
141
+ // };
142
+
143
+ // // 取消运行
144
+ // export const cancelRun = async (
145
+ // request: FastifyRequest,
146
+ // reply: FastifyReply
147
+ // ): Promise<void> => {
148
+ // try {
149
+ // const { id } = request.params as { id: string };
150
+
151
+ // if (!id) {
152
+ // reply.status(400).send({
153
+ // success: false,
154
+ // error: "运行ID是必需的",
155
+ // });
156
+ // return;
157
+ // }
158
+
159
+ // const result = await runModel.updateRunStatus(id, RunStatus.CANCELLED);
160
+
161
+ // if (!result.success) {
162
+ // reply.status(404).send(result);
163
+ // return;
164
+ // }
165
+
166
+ // reply.send(result);
167
+ // } catch (error: any) {
168
+ // reply.status(500).send({
169
+ // success: false,
170
+ // error: `取消运行时发生错误: ${error.message}`,
171
+ // });
172
+ // }
173
+ // };
174
+
175
+ // // 添加消息
176
+ // export const addMessage = async (
177
+ // request: FastifyRequest,
178
+ // reply: FastifyReply
179
+ // ): Promise<void> => {
180
+ // try {
181
+ // const data = request.body as AddMessageRequest;
182
+
183
+ // // 验证请求数据
184
+ // if (!data.runId || !data.role || !data.content) {
185
+ // reply.status(400).send({
186
+ // success: false,
187
+ // error: "运行ID、角色和内容是必需的",
188
+ // });
189
+ // return;
190
+ // }
191
+
192
+ // const result = await messageModel.addMessage(data);
193
+
194
+ // if (!result.success) {
195
+ // reply.status(500).send(result);
196
+ // return;
197
+ // }
198
+
199
+ // reply.status(201).send(result);
200
+ // } catch (error: any) {
201
+ // reply.status(500).send({
202
+ // success: false,
203
+ // error: `添加消息时发生错误: ${error.message}`,
204
+ // });
205
+ // }
206
+ // };
207
+
208
+ // // 获取运行的所有消息
209
+ // export const getMessages = async (
210
+ // request: FastifyRequest,
211
+ // reply: FastifyReply
212
+ // ): Promise<void> => {
213
+ // try {
214
+ // const { runId } = request.params as { runId: string };
215
+
216
+ // if (!runId) {
217
+ // reply.status(400).send({
218
+ // success: false,
219
+ // error: "运行ID是必需的",
220
+ // });
221
+ // return;
222
+ // }
223
+
224
+ // const result = await messageModel.getMessagesByRun(runId);
225
+
226
+ // reply.send(result);
227
+ // } catch (error: any) {
228
+ // reply.status(500).send({
229
+ // success: false,
230
+ // error: `获取消息时发生错误: ${error.message}`,
231
+ // });
232
+ // }
233
+ // };
package/src/index.ts ADDED
@@ -0,0 +1,101 @@
1
+ import fastify from "fastify";
2
+ import cors from "@fastify/cors";
3
+ import sensible from "@fastify/sensible";
4
+ import { registerRoutes } from "./routes";
5
+ // 导入自定义 Logger 类
6
+ import { Logger } from "./logger/Logger";
7
+
8
+ process.on("unhandledRejection", (reason, promise) => {
9
+ console.error("未处理的Promise拒绝:", reason);
10
+ // 可以在这里进行日志记录或其他处理
11
+ });
12
+
13
+ // 创建自定义日志记录器
14
+ const logger = new Logger({
15
+ serviceName: "lattice-gateway",
16
+ name: "fastify-server",
17
+ });
18
+
19
+ // 创建 Fastify 应用
20
+ const app = fastify({
21
+ logger: false, // 禁用内置日志记录器
22
+ });
23
+
24
+ // 添加自定义日志记录
25
+ app.addHook("onRequest", (request, reply, done) => {
26
+ const context = {
27
+ "x-tenant-id": request.headers["x-tenant-id"],
28
+ "x-request-id": request.headers["x-request-id"],
29
+ };
30
+ done();
31
+ });
32
+
33
+ app.addHook("onResponse", (request, reply, done) => {
34
+ const context = {
35
+ "x-tenant-id": request.headers["x-tenant-id"],
36
+ "x-request-id": request.headers["x-request-id"],
37
+ };
38
+ done();
39
+ });
40
+
41
+ // cors
42
+ app.register(cors, {
43
+ origin: true,
44
+ methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
45
+ allowedHeaders: [
46
+ "Content-Type",
47
+ "Authorization",
48
+ "X-Requested-With",
49
+ "x-tenant-id",
50
+ "x-request-id",
51
+ ],
52
+ exposedHeaders: ["Content-Type"],
53
+ credentials: true,
54
+ });
55
+ app.register(sensible);
56
+
57
+ // 错误处理
58
+ app.setErrorHandler((error, request, reply) => {
59
+ const context = {
60
+ "x-tenant-id": request.headers["x-tenant-id"],
61
+ "x-request-id": request.headers["x-request-id"],
62
+ };
63
+ logger.error(
64
+ `请求错误: ${request.method} ${request.url} error:${error.message}`,
65
+ {
66
+ ...context,
67
+ error: error.message,
68
+ stack: error.stack,
69
+ statusCode: error.statusCode || 500,
70
+ }
71
+ );
72
+ reply.status(error.statusCode || 500).send({
73
+ success: false,
74
+ error: error.message || "服务器内部错误",
75
+ });
76
+ });
77
+
78
+ // 将日志记录器添加到应用实例中,以便在路由中使用
79
+ app.decorate("logger", logger);
80
+
81
+ // 注册路由
82
+ registerRoutes(app);
83
+
84
+ // 启动服务器
85
+ const start = async ({ port }: { port: number }) => {
86
+ try {
87
+ const target_port = port || Number(process.env.PORT) || 4001;
88
+ await app.listen({ port: target_port, host: "0.0.0.0" });
89
+ logger.info(`Lattice Gateway is running on port: ${port}`);
90
+ } catch (err) {
91
+ logger.error("Server start failed", { error: err });
92
+ process.exit(1);
93
+ }
94
+ };
95
+
96
+ const LatticeGateway = {
97
+ startAsHttpEndpoint: start,
98
+ app,
99
+ };
100
+
101
+ export { LatticeGateway };
@@ -0,0 +1,186 @@
1
+ import pino from "pino";
2
+ import "pino-pretty";
3
+ import "pino-roll";
4
+ import { AsyncLocalStorage } from "async_hooks";
5
+
6
+ export interface LoggerContext {
7
+ "x-user-id"?: string;
8
+ "x-tenant-id"?: string;
9
+ "x-request-id"?: string;
10
+ "x-task-id"?: string;
11
+ "x-thread-id"?: string;
12
+ }
13
+
14
+ export interface LoggerOptions {
15
+ name?: string;
16
+ serviceName?: string;
17
+ context?: LoggerContext;
18
+ }
19
+
20
+ /**
21
+ * 单例的Pino日志工厂类,管理底层pino实例
22
+ */
23
+ class PinoLoggerFactory {
24
+ private static instance: PinoLoggerFactory;
25
+ private pinoLogger: pino.Logger;
26
+
27
+ private constructor() {
28
+ const isProd = process.env.NODE_ENV === "production";
29
+
30
+ const loggerConfig: pino.LoggerOptions = {
31
+ // 自定义时间戳格式
32
+ timestamp: () => `,"@timestamp":"${new Date().toISOString()}"`,
33
+
34
+ // 关闭默认的时间戳键
35
+ base: {
36
+ "@version": "1",
37
+ app_name: "lattice",
38
+ service_name: "lattice/graph-server",
39
+ thread_name: "main",
40
+ logger_name: "lattice-graph-logger",
41
+ },
42
+
43
+ formatters: {
44
+ level: (label, number) => {
45
+ return {
46
+ level: label.toUpperCase(),
47
+ level_value: number * 1000,
48
+ };
49
+ },
50
+ },
51
+ };
52
+
53
+ // 生产环境使用文件日志
54
+ if (isProd) {
55
+ try {
56
+ this.pinoLogger = pino(
57
+ loggerConfig,
58
+ pino.transport({
59
+ target: "pino-roll",
60
+ options: {
61
+ file: "./logs/fin_ai_graph_server",
62
+ frequency: "daily",
63
+ mkdir: true,
64
+ },
65
+ })
66
+ );
67
+ } catch (error) {
68
+ console.error(
69
+ "无法初始化 pino-roll 日志记录器,回退到控制台日志",
70
+ error
71
+ );
72
+ // 回退到开发环境的配置
73
+ this.pinoLogger = pino({
74
+ ...loggerConfig,
75
+ transport: {
76
+ target: "pino-pretty",
77
+ options: {
78
+ colorize: true,
79
+ },
80
+ },
81
+ });
82
+ }
83
+ } else {
84
+ // 开发环境使用格式化输出
85
+ this.pinoLogger = pino({
86
+ ...loggerConfig,
87
+ transport: {
88
+ target: "pino-pretty",
89
+ options: {
90
+ colorize: true,
91
+ },
92
+ },
93
+ });
94
+ }
95
+ }
96
+
97
+ public static getInstance(): PinoLoggerFactory {
98
+ if (!PinoLoggerFactory.instance) {
99
+ PinoLoggerFactory.instance = new PinoLoggerFactory();
100
+ }
101
+ return PinoLoggerFactory.instance;
102
+ }
103
+
104
+ public getPinoLogger(): pino.Logger {
105
+ return this.pinoLogger;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Logger类,可以创建多个实例,每个实例有自己的上下文
111
+ */
112
+ export class Logger {
113
+ private context: LoggerContext;
114
+ private name: string;
115
+ private serviceName: string;
116
+
117
+ constructor(options?: LoggerOptions) {
118
+ this.context = options?.context || {};
119
+ this.name = options?.name || "lattice-graph-logger";
120
+ this.serviceName = options?.serviceName || "lattice/graph-server";
121
+ }
122
+
123
+ /**
124
+ * 获取合并了上下文的日志对象
125
+ * @param additionalContext 额外的上下文数据
126
+ * @returns 带有上下文的pino日志对象
127
+ */
128
+ private getContextualLogger(additionalContext?: object): pino.Logger {
129
+ const pinoLogger = PinoLoggerFactory.getInstance().getPinoLogger();
130
+
131
+ // 合并Logger实例的上下文和额外上下文
132
+ const contextObj = {
133
+ "x-user-id": this.context["x-user-id"] || "",
134
+ "x-tenant-id": this.context["x-tenant-id"] || "",
135
+ "x-request-id": this.context["x-request-id"] || "",
136
+ "x-task-id": this.context["x-task-id"] || "",
137
+ "x-thread-id": this.context["x-thread-id"] || "",
138
+ service_name: this.serviceName,
139
+ logger_name: this.name,
140
+ ...additionalContext,
141
+ };
142
+
143
+ // 创建带有上下文的子日志记录器
144
+ return pinoLogger.child(contextObj);
145
+ }
146
+
147
+ info(msg: string, obj?: object): void {
148
+ this.getContextualLogger(obj).info(msg);
149
+ }
150
+
151
+ error(msg: string, obj?: object | Error): void {
152
+ this.getContextualLogger(obj).error(msg);
153
+ }
154
+
155
+ warn(msg: string, obj?: object): void {
156
+ this.getContextualLogger(obj).warn(msg);
157
+ }
158
+
159
+ debug(msg: string, obj?: object): void {
160
+ this.getContextualLogger(obj).debug(msg);
161
+ }
162
+
163
+ /**
164
+ * 更新Logger实例的上下文
165
+ */
166
+ updateContext(context: Partial<LoggerContext>): void {
167
+ this.context = {
168
+ ...this.context,
169
+ ...context,
170
+ };
171
+ }
172
+
173
+ /**
174
+ * 创建一个新的Logger实例,继承当前Logger的上下文
175
+ */
176
+ child(options: Partial<LoggerOptions>): Logger {
177
+ return new Logger({
178
+ name: options.name || this.name,
179
+ serviceName: options.serviceName || this.serviceName,
180
+ context: {
181
+ ...this.context,
182
+ ...options.context,
183
+ },
184
+ });
185
+ }
186
+ }
@@ -0,0 +1,70 @@
1
+ import { FastifyInstance } from "fastify";
2
+ //import * as assistantController from "../controllers/assistant";
3
+ import * as runController from "../controllers/run";
4
+ import * as memoryController from "../controllers/memory";
5
+ import * as graphController from "../controllers/assistant";
6
+
7
+ export const registerRoutes = (app: FastifyInstance): void => {
8
+ // 运行路由
9
+ app.post<{
10
+ Body: any;
11
+ }>("/api/runs", runController.createRun);
12
+
13
+ // app.get<{
14
+ // Params: { id: string };
15
+ // }>("/api/runs/:id", runController.getRun);
16
+
17
+ // app.get<{
18
+ // Params: { assistantId: string };
19
+ // }>("/api/assistants/:assistantId/runs", runController.getRunsByAssistant);
20
+
21
+ // app.post<{
22
+ // Params: { id: string };
23
+ // }>("/api/runs/:id/cancel", runController.cancelRun);
24
+
25
+ // 内存路由
26
+ app.get<{
27
+ Params: { assistantId: string; thread_id: string };
28
+ }>(
29
+ "/api/assistants/:assistantId/:thread_id/memory",
30
+ memoryController.getAllMemoryItems
31
+ );
32
+
33
+ app.get<{
34
+ Params: { assistantId: string; thread_id: string };
35
+ }>(
36
+ "/api/assistants/:assistantId/:thread_id/state",
37
+ memoryController.getAgentState
38
+ );
39
+
40
+ app.get<{
41
+ Params: { assistantId: string; key: string };
42
+ }>(
43
+ "/api/assistants/:assistantId/memory/:key",
44
+ memoryController.getMemoryItem
45
+ );
46
+
47
+ app.put<{
48
+ Params: { assistantId: string; key: string };
49
+ Body: any;
50
+ }>(
51
+ "/api/assistants/:assistantId/memory/:key",
52
+ memoryController.setMemoryItem
53
+ );
54
+
55
+ app.delete<{
56
+ Params: { assistantId: string; key: string };
57
+ }>(
58
+ "/api/assistants/:assistantId/memory/:key",
59
+ memoryController.deleteMemoryItem
60
+ );
61
+
62
+ app.delete<{
63
+ Params: { assistantId: string };
64
+ }>("/api/assistants/:assistantId/memory", memoryController.clearMemory);
65
+
66
+ // 图表路由
67
+ app.get<{
68
+ Params: { assistantId: string };
69
+ }>("/api/assistants/:assistantId/graph", graphController.getAgentGraph);
70
+ };
@@ -0,0 +1,42 @@
1
+ import { AGENT_TASK_EVENT } from "./agent_task_types";
2
+ import eventBus from "./event_bus";
3
+
4
+ export class AgentManager {
5
+ private static instance: AgentManager;
6
+ private constructor() {}
7
+ private agents: { name: string; agent: any }[] = [];
8
+ public static getInstance(): AgentManager {
9
+ if (!AgentManager.instance) {
10
+ AgentManager.instance = new AgentManager();
11
+ }
12
+ return AgentManager.instance;
13
+ }
14
+ callAgentInQueue(queue: {
15
+ assistant_id: string;
16
+ input: any;
17
+ thread_id: string;
18
+ "x-tenant-id": number;
19
+ }) {
20
+ return new Promise((resolve, reject) => {
21
+ const callback_event = `${queue.assistant_id}::${queue.thread_id}`;
22
+ eventBus.subscribeOnce(callback_event, (data) => {
23
+ if (data.success) {
24
+ console.log("AgentManager callAgentInQueue success", data);
25
+ resolve(data.result);
26
+ } else {
27
+ console.log("AgentManager callAgentInQueue error", data);
28
+ reject(data.error);
29
+ }
30
+ });
31
+ eventBus.publish(
32
+ AGENT_TASK_EVENT,
33
+ {
34
+ ...queue,
35
+ thread_id: callback_event,
36
+ callback_event,
37
+ },
38
+ true
39
+ );
40
+ });
41
+ }
42
+ }