@axiom-lattice/gateway 1.0.20

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.
package/dist/index.mjs ADDED
@@ -0,0 +1,686 @@
1
+ // src/index.ts
2
+ import fastify from "fastify";
3
+ import cors from "@fastify/cors";
4
+ import sensible from "@fastify/sensible";
5
+
6
+ // src/config/index.ts
7
+ import dotenv from "dotenv";
8
+ import path from "path";
9
+ dotenv.config({ path: path.resolve(__dirname, "../../.env") });
10
+ var config = {
11
+ server: {
12
+ port: process.env.PORT || 4001,
13
+ env: process.env.NODE_ENV || "development"
14
+ },
15
+ supabase: {
16
+ url: process.env.SUPABASE_URL || "",
17
+ key: process.env.SUPABASE_KEY || ""
18
+ },
19
+ // langgraph: {
20
+ // apiKey: process.env.LANGGRAPH_API_KEY || "dummy_key",
21
+ // },
22
+ webhook: {
23
+ url: process.env.WEBHOOK_URL || ""
24
+ },
25
+ // java_api_url: process.env.JAVA_API_URL || "",
26
+ // common_api_url: process.env.COMMON_API_URL || "",
27
+ // azure_openai_api_key: process.env.AZURE_OPENAI_API_KEY || "",
28
+ database_url: process.env.DATABASE_URL || ""
29
+ };
30
+ var validateConfig = () => {
31
+ };
32
+
33
+ // src/services/agent_service.ts
34
+ import {
35
+ filterMessages,
36
+ HumanMessage
37
+ } from "@langchain/core/messages";
38
+ import { Command } from "@langchain/langgraph";
39
+ import { v4 } from "uuid";
40
+ import { getAgentClient, getAgentLattice } from "@axiom-lattice/core";
41
+ async function agent_invoke({
42
+ input,
43
+ thread_id,
44
+ assistant_id,
45
+ tenant_id,
46
+ command,
47
+ run_id
48
+ }) {
49
+ const runnable_agent = getAgentLattice(assistant_id)?.client;
50
+ const { files, message, ...rest } = input;
51
+ const humanMessage = new HumanMessage(message || "");
52
+ humanMessage.additional_kwargs = { files };
53
+ const messages = [humanMessage];
54
+ if (!runnable_agent) {
55
+ throw new Error(`Agent ${assistant_id} not found`);
56
+ }
57
+ const result = await runnable_agent.invoke(
58
+ command ? new Command(command) : { ...rest, messages, "x-tenant-id": tenant_id },
59
+ {
60
+ configurable: {
61
+ thread_id,
62
+ run_id: run_id || v4(),
63
+ recursionLimit: 200,
64
+ "x-tenant-id": tenant_id,
65
+ "x-request-id": run_id,
66
+ "x-thread-id": thread_id
67
+ }
68
+ }
69
+ );
70
+ return result;
71
+ }
72
+ async function agent_stream({
73
+ input,
74
+ thread_id,
75
+ command,
76
+ tenant_id,
77
+ assistant_id,
78
+ run_id
79
+ }) {
80
+ const runnable_agent = getAgentClient(assistant_id);
81
+ const { files, message, ...rest } = input;
82
+ let messages = [];
83
+ if (!command) {
84
+ const humanMessage = new HumanMessage(message);
85
+ humanMessage.additional_kwargs = { files };
86
+ messages = [humanMessage];
87
+ }
88
+ try {
89
+ if (!runnable_agent) {
90
+ throw new Error(`Agent ${assistant_id} not found`);
91
+ }
92
+ const agentStream = await runnable_agent.stream(
93
+ command ? new Command(command) : {
94
+ ...rest,
95
+ messages,
96
+ "x-tenant-id": tenant_id
97
+ },
98
+ {
99
+ configurable: {
100
+ thread_id,
101
+ run_id: run_id || v4(),
102
+ "x-tenant-id": tenant_id,
103
+ "x-request-id": run_id,
104
+ "x-thread-id": thread_id
105
+ },
106
+ streamMode: ["updates", "messages"],
107
+ subgraphs: false
108
+ }
109
+ );
110
+ return {
111
+ [Symbol.asyncIterator]: async function* () {
112
+ try {
113
+ for await (const chunk of agentStream) {
114
+ let data;
115
+ if (chunk[0] === "messages") {
116
+ const messages2 = chunk[1];
117
+ data = messages2?.[0]?.toDict();
118
+ }
119
+ if (chunk?.[1]?.__interrupt__) {
120
+ data = chunk?.[1]?.[0]?.toDict();
121
+ }
122
+ if (data) {
123
+ console.log(data);
124
+ yield data;
125
+ }
126
+ }
127
+ } catch (error) {
128
+ console.error("Stream error:", error);
129
+ throw error;
130
+ }
131
+ }
132
+ };
133
+ } catch (error) {
134
+ throw error;
135
+ }
136
+ }
137
+ async function agent_state({
138
+ thread_id,
139
+ assistant_id
140
+ }) {
141
+ const runnable_agent = getAgentClient(assistant_id);
142
+ if (!runnable_agent) {
143
+ throw new Error(`Agent ${assistant_id} not found`);
144
+ }
145
+ const state = await runnable_agent.getState({
146
+ configurable: { thread_id, subgraphs: false }
147
+ });
148
+ return state;
149
+ }
150
+ async function agent_messages({
151
+ thread_id,
152
+ tenant_id,
153
+ assistant_id
154
+ }) {
155
+ const runnable_agent = getAgentClient(assistant_id);
156
+ if (!runnable_agent) {
157
+ throw new Error(`Agent ${assistant_id} not found`);
158
+ }
159
+ const state = await runnable_agent.getState({
160
+ configurable: { thread_id, subgraphs: false }
161
+ });
162
+ const messages = state.values.messages || [];
163
+ const filteredMessages = filterMessages(messages, {
164
+ includeTypes: ["ai", "human", "tool"]
165
+ //["human", "ai", "tool"],
166
+ });
167
+ let messagesArray = filteredMessages.map((message) => ({
168
+ id: message.id,
169
+ role: message.getType(),
170
+ content: message.content,
171
+ files: message.additional_kwargs.files,
172
+ ...message.lc_kwargs
173
+ }));
174
+ const action_messages = state.tasks.flatMap((task) => {
175
+ return task.interrupts.map((interrupt) => {
176
+ return {
177
+ role: "ai",
178
+ content: interrupt.value,
179
+ type: "action"
180
+ };
181
+ });
182
+ });
183
+ const new_messages = [...messagesArray, ...action_messages];
184
+ return new_messages;
185
+ }
186
+ async function draw_graph(assistant_id) {
187
+ const runnable_agent = getAgentClient(assistant_id);
188
+ if (!runnable_agent) {
189
+ throw new Error(`Agent ${assistant_id} not found`);
190
+ }
191
+ const drawableGraph = await runnable_agent.getGraphAsync();
192
+ const image = await drawableGraph.drawMermaid();
193
+ return image;
194
+ }
195
+
196
+ // src/controllers/run.ts
197
+ import { v4 as v42 } from "uuid";
198
+ var createRun = async (request, reply) => {
199
+ try {
200
+ const {
201
+ assistant_id,
202
+ thread_id,
203
+ command,
204
+ streaming,
205
+ background,
206
+ ...input
207
+ } = request.body;
208
+ const tenant_id = request.headers["x-tenant-id"];
209
+ const x_request_id = request.headers["x-request-id"] || v42();
210
+ if (!assistant_id) {
211
+ reply.status(400).send({
212
+ success: false,
213
+ error: "\u52A9\u624BID\u662F\u5FC5\u9700\u7684"
214
+ });
215
+ return;
216
+ }
217
+ if (streaming) {
218
+ const stream = await agent_stream({
219
+ assistant_id,
220
+ input,
221
+ thread_id,
222
+ command,
223
+ tenant_id,
224
+ run_id: x_request_id
225
+ });
226
+ reply.raw.writeHead(200, {
227
+ "Content-Type": "text/event-stream",
228
+ "Cache-Control": "no-cache",
229
+ Connection: "keep-alive",
230
+ "Access-Control-Allow-Origin": "*"
231
+ });
232
+ try {
233
+ for await (const chunk of stream) {
234
+ reply.raw.write(`data: ${JSON.stringify(chunk)}
235
+
236
+ `);
237
+ }
238
+ } catch (error) {
239
+ } finally {
240
+ reply.raw.end();
241
+ return reply.hijack();
242
+ }
243
+ } else {
244
+ const result = await agent_invoke({
245
+ assistant_id,
246
+ input,
247
+ command,
248
+ thread_id,
249
+ tenant_id,
250
+ run_id: x_request_id
251
+ });
252
+ reply.status(200).send(result);
253
+ }
254
+ } catch (error) {
255
+ reply.status(500).send({
256
+ success: false,
257
+ error: `\u521B\u5EFA\u8FD0\u884C\u65F6\u53D1\u751F\u9519\u8BEF: ${error.message}`
258
+ });
259
+ }
260
+ };
261
+
262
+ // src/controllers/memory.ts
263
+ var setMemoryItem = async (request, reply) => {
264
+ try {
265
+ const { assistantId, key } = request.params;
266
+ const value = request.body;
267
+ if (!assistantId || !key) {
268
+ reply.status(400).send({
269
+ success: false,
270
+ error: "\u52A9\u624BID\u548C\u952E\u662F\u5FC5\u9700\u7684"
271
+ });
272
+ return;
273
+ }
274
+ if (value === void 0) {
275
+ reply.status(400).send({
276
+ success: false,
277
+ error: "\u503C\u662F\u5FC5\u9700\u7684"
278
+ });
279
+ return;
280
+ }
281
+ reply.status(500).send();
282
+ return;
283
+ } catch (error) {
284
+ reply.status(500).send({
285
+ success: false,
286
+ error: `\u8BBE\u7F6E\u5185\u5B58\u9879\u65F6\u53D1\u751F\u9519\u8BEF: ${error.message}`
287
+ });
288
+ }
289
+ };
290
+ var getMemoryItem = async (request, reply) => {
291
+ try {
292
+ const { assistantId, key } = request.params;
293
+ if (!assistantId || !key) {
294
+ reply.status(400).send({
295
+ success: false,
296
+ error: "\u52A9\u624BID\u548C\u952E\u662F\u5FC5\u9700\u7684"
297
+ });
298
+ return;
299
+ }
300
+ reply.status(404).send();
301
+ return;
302
+ } catch (error) {
303
+ reply.status(500).send({
304
+ success: false,
305
+ error: `\u83B7\u53D6\u5185\u5B58\u9879\u65F6\u53D1\u751F\u9519\u8BEF: ${error.message}`
306
+ });
307
+ }
308
+ };
309
+ var getAllMemoryItems = async (request, reply) => {
310
+ try {
311
+ const { assistantId, thread_id } = request.params;
312
+ const tenant_id = request.headers["x-tenant-id"];
313
+ if (!thread_id) {
314
+ reply.status(400).send({
315
+ success: false,
316
+ error: "\u7EBF\u7A0BID\u662F\u5FC5\u9700\u7684"
317
+ });
318
+ return;
319
+ }
320
+ if (!assistantId) {
321
+ reply.status(400).send({
322
+ success: false,
323
+ error: "\u52A9\u624BID\u662F\u5FC5\u9700\u7684"
324
+ });
325
+ return;
326
+ }
327
+ const result = await agent_messages({
328
+ assistant_id: assistantId,
329
+ thread_id,
330
+ tenant_id
331
+ });
332
+ if (!result) {
333
+ reply.status(500).send(result);
334
+ return;
335
+ }
336
+ reply.send(result);
337
+ } catch (error) {
338
+ reply.status(500).send({
339
+ success: false,
340
+ error: `\u83B7\u53D6\u6240\u6709\u5185\u5B58\u9879\u65F6\u53D1\u751F\u9519\u8BEF: ${error.message}`
341
+ });
342
+ }
343
+ };
344
+ var getAgentState = async (request, reply) => {
345
+ try {
346
+ const { assistantId, thread_id } = request.params;
347
+ if (!thread_id) {
348
+ reply.status(400).send({
349
+ success: false,
350
+ error: "\u7EBF\u7A0BID\u662F\u5FC5\u9700\u7684"
351
+ });
352
+ return;
353
+ }
354
+ if (!assistantId) {
355
+ reply.status(400).send({
356
+ success: false,
357
+ error: "\u52A9\u624BID\u662F\u5FC5\u9700\u7684"
358
+ });
359
+ return;
360
+ }
361
+ const result = await agent_state({
362
+ assistant_id: assistantId,
363
+ thread_id
364
+ });
365
+ if (!result) {
366
+ reply.status(500).send(result);
367
+ return;
368
+ }
369
+ reply.send(result);
370
+ } catch (error) {
371
+ reply.status(500).send({
372
+ success: false,
373
+ error: `\u83B7\u53D6\u52A9\u624B\u72B6\u6001\u65F6\u53D1\u751F\u9519\u8BEF: ${error.message}`
374
+ });
375
+ }
376
+ };
377
+ var deleteMemoryItem = async (request, reply) => {
378
+ try {
379
+ const { assistantId, key } = request.params;
380
+ if (!assistantId || !key) {
381
+ reply.status(400).send({
382
+ success: false,
383
+ error: "\u52A9\u624BID\u548C\u952E\u662F\u5FC5\u9700\u7684"
384
+ });
385
+ return;
386
+ }
387
+ reply.status(500).send();
388
+ return;
389
+ reply.status(204).send();
390
+ } catch (error) {
391
+ reply.status(500).send({
392
+ success: false,
393
+ error: `\u5220\u9664\u5185\u5B58\u9879\u65F6\u53D1\u751F\u9519\u8BEF: ${error.message}`
394
+ });
395
+ }
396
+ };
397
+ var clearMemory = async (request, reply) => {
398
+ try {
399
+ const { assistantId } = request.params;
400
+ if (!assistantId) {
401
+ reply.status(400).send({
402
+ success: false,
403
+ error: "\u52A9\u624BID\u662F\u5FC5\u9700\u7684"
404
+ });
405
+ return;
406
+ }
407
+ let result;
408
+ if (!result.success) {
409
+ reply.status(500).send(result);
410
+ return;
411
+ }
412
+ reply.status(204).send();
413
+ } catch (error) {
414
+ reply.status(500).send({
415
+ success: false,
416
+ error: `\u6E05\u9664\u5185\u5B58\u65F6\u53D1\u751F\u9519\u8BEF: ${error.message}`
417
+ });
418
+ }
419
+ };
420
+
421
+ // src/controllers/assistant.ts
422
+ var getAgentGraph = async (request, reply) => {
423
+ try {
424
+ const { assistantId } = request.params;
425
+ const imageData = await draw_graph(assistantId);
426
+ reply.header("Content-Type", "application/json").send({
427
+ image: imageData
428
+ });
429
+ } catch (error) {
430
+ reply.status(500).send({
431
+ success: false,
432
+ error: error.message || "\u83B7\u53D6\u4EE3\u7406\u56FE\u8868\u5931\u8D25"
433
+ });
434
+ }
435
+ };
436
+
437
+ // src/routes/index.ts
438
+ var registerRoutes = (app2) => {
439
+ app2.post("/api/runs", createRun);
440
+ app2.get(
441
+ "/api/assistants/:assistantId/:thread_id/memory",
442
+ getAllMemoryItems
443
+ );
444
+ app2.get(
445
+ "/api/assistants/:assistantId/:thread_id/state",
446
+ getAgentState
447
+ );
448
+ app2.get(
449
+ "/api/assistants/:assistantId/memory/:key",
450
+ getMemoryItem
451
+ );
452
+ app2.put(
453
+ "/api/assistants/:assistantId/memory/:key",
454
+ setMemoryItem
455
+ );
456
+ app2.delete(
457
+ "/api/assistants/:assistantId/memory/:key",
458
+ deleteMemoryItem
459
+ );
460
+ app2.delete("/api/assistants/:assistantId/memory", clearMemory);
461
+ app2.get("/api/assistants/:assistantId/graph", getAgentGraph);
462
+ };
463
+
464
+ // src/logger/Logger.ts
465
+ import pino from "pino";
466
+ import "pino-pretty";
467
+ import "pino-roll";
468
+ var PinoLoggerFactory = class _PinoLoggerFactory {
469
+ constructor() {
470
+ const isProd = process.env.NODE_ENV === "production";
471
+ const loggerConfig = {
472
+ // 自定义时间戳格式
473
+ timestamp: () => `,"@timestamp":"${(/* @__PURE__ */ new Date()).toISOString()}"`,
474
+ // 关闭默认的时间戳键
475
+ base: {
476
+ "@version": "1",
477
+ app_name: "lattice",
478
+ service_name: "lattice/graph-server",
479
+ thread_name: "main",
480
+ logger_name: "lattice-graph-logger"
481
+ },
482
+ formatters: {
483
+ level: (label, number) => {
484
+ return {
485
+ level: label.toUpperCase(),
486
+ level_value: number * 1e3
487
+ };
488
+ }
489
+ }
490
+ };
491
+ if (isProd) {
492
+ try {
493
+ this.pinoLogger = pino(
494
+ loggerConfig,
495
+ pino.transport({
496
+ target: "pino-roll",
497
+ options: {
498
+ file: "./logs/fin_ai_graph_server",
499
+ frequency: "daily",
500
+ mkdir: true
501
+ }
502
+ })
503
+ );
504
+ } catch (error) {
505
+ console.error(
506
+ "\u65E0\u6CD5\u521D\u59CB\u5316 pino-roll \u65E5\u5FD7\u8BB0\u5F55\u5668\uFF0C\u56DE\u9000\u5230\u63A7\u5236\u53F0\u65E5\u5FD7",
507
+ error
508
+ );
509
+ this.pinoLogger = pino({
510
+ ...loggerConfig,
511
+ transport: {
512
+ target: "pino-pretty",
513
+ options: {
514
+ colorize: true
515
+ }
516
+ }
517
+ });
518
+ }
519
+ } else {
520
+ this.pinoLogger = pino({
521
+ ...loggerConfig,
522
+ transport: {
523
+ target: "pino-pretty",
524
+ options: {
525
+ colorize: true
526
+ }
527
+ }
528
+ });
529
+ }
530
+ }
531
+ static getInstance() {
532
+ if (!_PinoLoggerFactory.instance) {
533
+ _PinoLoggerFactory.instance = new _PinoLoggerFactory();
534
+ }
535
+ return _PinoLoggerFactory.instance;
536
+ }
537
+ getPinoLogger() {
538
+ return this.pinoLogger;
539
+ }
540
+ };
541
+ var Logger = class _Logger {
542
+ constructor(options) {
543
+ this.context = options?.context || {};
544
+ this.name = options?.name || "lattice-graph-logger";
545
+ this.serviceName = options?.serviceName || "lattice/graph-server";
546
+ }
547
+ /**
548
+ * 获取合并了上下文的日志对象
549
+ * @param additionalContext 额外的上下文数据
550
+ * @returns 带有上下文的pino日志对象
551
+ */
552
+ getContextualLogger(additionalContext) {
553
+ const pinoLogger = PinoLoggerFactory.getInstance().getPinoLogger();
554
+ const contextObj = {
555
+ "x-user-id": this.context["x-user-id"] || "",
556
+ "x-tenant-id": this.context["x-tenant-id"] || "",
557
+ "x-request-id": this.context["x-request-id"] || "",
558
+ "x-task-id": this.context["x-task-id"] || "",
559
+ "x-thread-id": this.context["x-thread-id"] || "",
560
+ service_name: this.serviceName,
561
+ logger_name: this.name,
562
+ ...additionalContext
563
+ };
564
+ return pinoLogger.child(contextObj);
565
+ }
566
+ info(msg, obj) {
567
+ this.getContextualLogger(obj).info(msg);
568
+ }
569
+ error(msg, obj) {
570
+ this.getContextualLogger(obj).error(msg);
571
+ }
572
+ warn(msg, obj) {
573
+ this.getContextualLogger(obj).warn(msg);
574
+ }
575
+ debug(msg, obj) {
576
+ this.getContextualLogger(obj).debug(msg);
577
+ }
578
+ /**
579
+ * 更新Logger实例的上下文
580
+ */
581
+ updateContext(context) {
582
+ this.context = {
583
+ ...this.context,
584
+ ...context
585
+ };
586
+ }
587
+ /**
588
+ * 创建一个新的Logger实例,继承当前Logger的上下文
589
+ */
590
+ child(options) {
591
+ return new _Logger({
592
+ name: options.name || this.name,
593
+ serviceName: options.serviceName || this.serviceName,
594
+ context: {
595
+ ...this.context,
596
+ ...options.context
597
+ }
598
+ });
599
+ }
600
+ };
601
+
602
+ // src/index.ts
603
+ process.on("unhandledRejection", (reason, promise) => {
604
+ console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
605
+ });
606
+ try {
607
+ validateConfig();
608
+ } catch (error) {
609
+ console.error("\u914D\u7F6E\u9519\u8BEF:", error.message);
610
+ process.exit(1);
611
+ }
612
+ var logger = new Logger({
613
+ serviceName: "lattice-gateway",
614
+ name: "fastify-server"
615
+ });
616
+ var app = fastify({
617
+ logger: false
618
+ // 禁用内置日志记录器
619
+ });
620
+ app.addHook("onRequest", (request, reply, done) => {
621
+ const context = {
622
+ "x-tenant-id": request.headers["x-tenant-id"],
623
+ "x-request-id": request.headers["x-request-id"]
624
+ };
625
+ done();
626
+ });
627
+ app.addHook("onResponse", (request, reply, done) => {
628
+ const context = {
629
+ "x-tenant-id": request.headers["x-tenant-id"],
630
+ "x-request-id": request.headers["x-request-id"]
631
+ };
632
+ done();
633
+ });
634
+ app.register(cors, {
635
+ origin: true,
636
+ methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
637
+ allowedHeaders: [
638
+ "Content-Type",
639
+ "Authorization",
640
+ "X-Requested-With",
641
+ "x-tenant-id",
642
+ "x-request-id"
643
+ ],
644
+ exposedHeaders: ["Content-Type"],
645
+ credentials: true
646
+ });
647
+ app.register(sensible);
648
+ app.setErrorHandler((error, request, reply) => {
649
+ const context = {
650
+ "x-tenant-id": request.headers["x-tenant-id"],
651
+ "x-request-id": request.headers["x-request-id"]
652
+ };
653
+ logger.error(
654
+ `\u8BF7\u6C42\u9519\u8BEF: ${request.method} ${request.url} error:${error.message}`,
655
+ {
656
+ ...context,
657
+ error: error.message,
658
+ stack: error.stack,
659
+ statusCode: error.statusCode || 500
660
+ }
661
+ );
662
+ reply.status(error.statusCode || 500).send({
663
+ success: false,
664
+ error: error.message || "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF"
665
+ });
666
+ });
667
+ app.decorate("logger", logger);
668
+ registerRoutes(app);
669
+ var start = async () => {
670
+ try {
671
+ const port = Number(config.server.port);
672
+ await app.listen({ port, host: "0.0.0.0" });
673
+ logger.info(`Lattice Gateway is running on port: ${port}`);
674
+ logger.info(`Environment: ${config.server.env}`);
675
+ } catch (err) {
676
+ logger.error("Server start failed", { error: err });
677
+ process.exit(1);
678
+ }
679
+ };
680
+ var LatticeGateway = {
681
+ startAsHttpEndpoint: start
682
+ };
683
+ export {
684
+ LatticeGateway
685
+ };
686
+ //# sourceMappingURL=index.mjs.map