@vectorx/functions-framework 0.1.3 → 0.3.0

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/bin/rcb-ff.js CHANGED
@@ -20,6 +20,7 @@ program
20
20
  .option('--enable-cors', '启用 CORS')
21
21
  .option('--allowed-origins <origins>', 'CORS 允许的域名,用逗号分隔', 'localhost,127.0.0.1')
22
22
  .option('--config <path>', '指定配置文件路径', 'agent-cloudbase-functions.json')
23
+ .option('--enableRedLangfuse', '启用 Red Langfuse 上报')
23
24
  .option('-w, --watch', '启用文件监听模式,文件变化时自动重启服务');
24
25
 
25
26
  program.parse();
@@ -45,6 +46,9 @@ async function startServer() {
45
46
  functionsConfigFile: path.resolve(directory, options.config),
46
47
  enableCors: options.enableCors,
47
48
  allowedOrigins: options.allowedOrigins.split(','),
49
+ redLangfuseConfig: {
50
+ enable: Boolean(options.enableRedLangfuse),
51
+ },
48
52
  kitInfo: {
49
53
  agentRuntimeVersion: getDependencyVersion('@vectorx/agent-runtime', directory),
50
54
  functionsFrameworkVersion: getDependencyVersion('@vectorx/functions-framework', directory)
package/lib/config.js CHANGED
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getBaseUrl = getBaseUrl;
4
4
  const ENV_BASE_URL = {
5
- development: "https://agentbase-sandbox.xiaohongshu.com",
5
+ development: "https://agentbase-sandbox.beta.xiaohongshu.com",
6
6
  production: "https://agentbase.xiaohongshu.com",
7
7
  };
8
8
  function getBaseUrl() {
@@ -79,11 +79,11 @@ const wrapEventFunction = (execute, projectConfig) => {
79
79
  ctx.state.event = event;
80
80
  ctx.state.contextInjected = frozenContext;
81
81
  let result;
82
- const start = Date.now();
83
82
  try {
84
83
  result = yield execute(event, context);
85
84
  }
86
85
  catch (e) {
86
+ console.log("============>>>>>>>>>> function wrapper error ", e);
87
87
  ctx.state.error = e;
88
88
  if (hasSwitchSSEMode) {
89
89
  const sse = (_a = ctx.sse) === null || _a === void 0 ? void 0 : _a.call(ctx);
@@ -93,14 +93,13 @@ const wrapEventFunction = (execute, projectConfig) => {
93
93
  data: e.message,
94
94
  comment: "uncaught-exception-from-user-code",
95
95
  };
96
+ console.log("============>>>>>>>>>> errorEvent ", errorEvent);
96
97
  sse.emit("error", errorEvent);
98
+ console.log("============>>>>>>>>>> sse.end(errorEvent) ");
97
99
  sse.end(errorEvent);
98
100
  }
99
101
  }
100
102
  }
101
- finally {
102
- ctx.state.userCodeTimeCost = Date.now() - start;
103
- }
104
103
  executeDone = true;
105
104
  if (hasSwitchSSEMode) {
106
105
  return;
package/lib/logger.js CHANGED
@@ -131,6 +131,50 @@ class FunctionsLogger {
131
131
  return logs;
132
132
  });
133
133
  }
134
+ getLogsPaginated(options) {
135
+ return __awaiter(this, void 0, void 0, function* () {
136
+ const { type, pageNum = 1, pageSize = 50 } = options;
137
+ const allLogs = yield this.getAllLogs(type);
138
+ const total = allLogs.length;
139
+ const offset = (pageNum - 1) * pageSize;
140
+ const logs = allLogs.slice(offset, offset + pageSize);
141
+ const hasMore = pageNum * pageSize < total;
142
+ return {
143
+ logs,
144
+ pageNum,
145
+ pageSize: logs.length,
146
+ total,
147
+ hasMore,
148
+ };
149
+ });
150
+ }
151
+ getAllLogs(type) {
152
+ return __awaiter(this, void 0, void 0, function* () {
153
+ const logs = [];
154
+ const logDir = DEFAULT_LOG_DIRNAME;
155
+ const files = fs_1.default
156
+ .readdirSync(logDir)
157
+ .filter((file) => file.startsWith(type) && file.endsWith(".log"))
158
+ .sort()
159
+ .reverse();
160
+ let globalId = 0;
161
+ for (const file of files) {
162
+ const content = fs_1.default.readFileSync(path_1.default.join(logDir, file), "utf-8");
163
+ const lines = content.split("\n").filter(Boolean).reverse();
164
+ for (const line of lines) {
165
+ try {
166
+ const log = JSON.parse(line);
167
+ log.id = globalId++;
168
+ logs.push(log);
169
+ }
170
+ catch (e) {
171
+ console.error("Failed to parse log line:", line);
172
+ }
173
+ }
174
+ }
175
+ return logs;
176
+ });
177
+ }
134
178
  createWinstonLogger(options) {
135
179
  const transports = [
136
180
  new winston_daily_rotate_file_1.default({
@@ -16,32 +16,82 @@ function logsQueryMiddleware() {
16
16
  return (ctx, next) => __awaiter(this, void 0, void 0, function* () {
17
17
  if (ctx.path.startsWith("/@logs")) {
18
18
  const type = ctx.query.type || logger_1.LogType.ACCESS;
19
+ const pageNumParam = ctx.query.pageNum;
20
+ const pageSizeParam = ctx.query.pageSize;
19
21
  const limitParam = ctx.query.limit;
20
- const limit = limitParam ? Number.parseInt(limitParam) : 100;
21
- if (Number.isNaN(limit) || limit <= 0 || limit > 1000) {
22
- ctx.status = 400;
23
- ctx.body = {
24
- success: false,
25
- error: "Invalid limit parameter. Must be a positive number <= 1000",
26
- };
22
+ if (pageNumParam !== undefined || pageSizeParam !== undefined) {
23
+ const pageNum = pageNumParam ? Number.parseInt(pageNumParam) : 1;
24
+ const pageSize = pageSizeParam ? Number.parseInt(pageSizeParam) : 50;
25
+ if (Number.isNaN(pageNum) || pageNum <= 0) {
26
+ ctx.status = 400;
27
+ ctx.body = {
28
+ success: false,
29
+ error: "Invalid pageNum parameter. Must be a positive number.",
30
+ };
31
+ return;
32
+ }
33
+ if (Number.isNaN(pageSize) || pageSize <= 0 || pageSize > 100) {
34
+ ctx.status = 400;
35
+ ctx.body = {
36
+ success: false,
37
+ error: "Invalid pageSize parameter. Must be a positive number <= 100",
38
+ };
39
+ return;
40
+ }
41
+ try {
42
+ const result = yield logger_2.functionsLogger.getLogsPaginated({
43
+ type,
44
+ pageNum,
45
+ pageSize,
46
+ });
47
+ ctx.body = {
48
+ success: true,
49
+ data: result.logs,
50
+ pagination: {
51
+ pageNum: result.pageNum,
52
+ pageSize: result.pageSize,
53
+ total: result.total,
54
+ hasMore: result.hasMore,
55
+ },
56
+ };
57
+ }
58
+ catch (error) {
59
+ logger_2.functionsLogger.logError(error);
60
+ ctx.status = 500;
61
+ ctx.body = {
62
+ success: false,
63
+ error: error.message,
64
+ };
65
+ }
27
66
  return;
28
67
  }
29
- try {
30
- const logs = yield logger_2.functionsLogger.getLogs(type, limit);
31
- ctx.body = {
32
- success: true,
33
- data: logs,
34
- };
35
- }
36
- catch (error) {
37
- logger_2.functionsLogger.logError(error);
38
- ctx.status = 500;
39
- ctx.body = {
40
- success: false,
41
- error: error.message,
42
- };
68
+ else {
69
+ const limit = limitParam ? Number.parseInt(limitParam) : 100;
70
+ if (Number.isNaN(limit) || limit <= 0 || limit > 1000) {
71
+ ctx.status = 400;
72
+ ctx.body = {
73
+ success: false,
74
+ error: "Invalid limit parameter. Must be a positive number <= 1000",
75
+ };
76
+ return;
77
+ }
78
+ try {
79
+ const logs = yield logger_2.functionsLogger.getLogs(type, limit);
80
+ ctx.body = {
81
+ success: true,
82
+ data: logs,
83
+ };
84
+ }
85
+ catch (error) {
86
+ logger_2.functionsLogger.logError(error);
87
+ ctx.status = 500;
88
+ ctx.body = {
89
+ success: false,
90
+ error: error.message,
91
+ };
92
+ }
93
+ return;
43
94
  }
44
- return;
45
95
  }
46
96
  return yield next();
47
97
  });
package/lib/sse.js CHANGED
@@ -32,15 +32,20 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
35
38
  Object.defineProperty(exports, "__esModule", { value: true });
36
39
  exports.ServerSentEvent = void 0;
37
40
  const events_1 = require("events");
38
41
  const stream_1 = require("stream");
39
42
  const util = __importStar(require("util"));
43
+ const langfuse_1 = __importDefault(require("./telemetry/langfuse"));
40
44
  const helper_1 = require("./utils/helper");
41
45
  const kDefaultEncoder = new util.TextEncoder();
42
46
  class ServerSentEvent extends events_1.EventEmitter {
43
47
  constructor(ctx) {
48
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
44
49
  super();
45
50
  this.closed = false;
46
51
  this.encoder = kDefaultEncoder;
@@ -77,6 +82,36 @@ class ServerSentEvent extends events_1.EventEmitter {
77
82
  writable: false,
78
83
  });
79
84
  }
85
+ try {
86
+ const enabled = ((_c = (_b = (_a = ctx.state) === null || _a === void 0 ? void 0 : _a.frameworkOptions) === null || _b === void 0 ? void 0 : _b.redLangfuseConfig) === null || _c === void 0 ? void 0 : _c.enable) === true;
87
+ if (!enabled)
88
+ return;
89
+ const stage = process.env.OPEN_PLATFORM_STAGE || "development";
90
+ const openId = ctx.headers["open-id"] || undefined;
91
+ const eventID = (_d = ctx.state) === null || _d === void 0 ? void 0 : _d.eventID;
92
+ const agentInfo = ((_e = ctx.state) === null || _e === void 0 ? void 0 : _e.agentInfo) || {};
93
+ const name = [(agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.agentName) || (agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.name), agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.version].filter(Boolean).join("@");
94
+ const metadata = {
95
+ http: { method: ctx.method, path: ctx.path },
96
+ eventID,
97
+ agent: {
98
+ id: agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.agentId,
99
+ name: (agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.agentName) || (agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.name),
100
+ version: agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.version,
101
+ },
102
+ };
103
+ const trace = (_f = langfuse_1.default === null || langfuse_1.default === void 0 ? void 0 : langfuse_1.default.trace) === null || _f === void 0 ? void 0 : _f.call(langfuse_1.default, {
104
+ name: name || "chat-app-session",
105
+ userId: openId,
106
+ tags: [stage],
107
+ metadata,
108
+ });
109
+ if (trace) {
110
+ ctx.state.langfuseTrace = trace;
111
+ (_g = trace.event) === null || _g === void 0 ? void 0 : _g.call(trace, { name: "sse:start", input: (_j = (_h = ctx.state) === null || _h === void 0 ? void 0 : _h.event) !== null && _j !== void 0 ? _j : null, metadata: { path: ctx.path } });
112
+ }
113
+ }
114
+ catch (_k) { }
80
115
  }
81
116
  setEncoder(encoder) {
82
117
  this.encoder = encoder;
@@ -94,6 +129,7 @@ class ServerSentEvent extends events_1.EventEmitter {
94
129
  return super.emit(event, ...args);
95
130
  }
96
131
  send(event) {
132
+ var _a, _b, _c;
97
133
  const encoder = this.encoder;
98
134
  const stream = this.stream;
99
135
  if (this.closed) {
@@ -122,17 +158,63 @@ class ServerSentEvent extends events_1.EventEmitter {
122
158
  }
123
159
  const buf = Buffer.concat(bufs);
124
160
  if (Buffer.byteLength(buf) > 0) {
125
- return stream.write(Buffer.concat(bufs));
161
+ const ok = stream.write(Buffer.concat(bufs));
162
+ try {
163
+ const lfTrace = (_a = this.ctx.state) === null || _a === void 0 ? void 0 : _a.langfuseTrace;
164
+ if (lfTrace) {
165
+ let output = null;
166
+ try {
167
+ if (!Array.isArray(event)) {
168
+ if (typeof event === "string")
169
+ output = event;
170
+ else if (Buffer.isBuffer(event))
171
+ output = event.toString("utf-8");
172
+ else if ((0, helper_1.isPlainObject)(event))
173
+ output = (_b = event.data) !== null && _b !== void 0 ? _b : null;
174
+ }
175
+ else {
176
+ const last = event[event.length - 1];
177
+ if (typeof last === "string")
178
+ output = last;
179
+ else if (Buffer.isBuffer(last))
180
+ output = last.toString("utf-8");
181
+ else if ((0, helper_1.isPlainObject)(last))
182
+ output = (_c = last.data) !== null && _c !== void 0 ? _c : null;
183
+ }
184
+ }
185
+ catch (_d) { }
186
+ lfTrace.event({
187
+ name: "sse:send",
188
+ input: null,
189
+ output,
190
+ metadata: { size: Buffer.byteLength(buf) },
191
+ });
192
+ }
193
+ }
194
+ catch (_e) { }
195
+ return ok;
126
196
  }
127
197
  return false;
128
198
  }
129
199
  end(msg) {
200
+ var _a, _b, _c, _d, _e, _f;
130
201
  if (msg) {
131
202
  this.send(msg);
132
203
  }
133
204
  this.stream.end();
134
205
  this.ctx.res.end();
135
206
  this.closed = true;
207
+ try {
208
+ const lfTrace = (_a = this.ctx.state) === null || _a === void 0 ? void 0 : _a.langfuseTrace;
209
+ const enabled = ((_d = (_c = (_b = this.ctx.state) === null || _b === void 0 ? void 0 : _b.frameworkOptions) === null || _c === void 0 ? void 0 : _c.redLangfuseConfig) === null || _d === void 0 ? void 0 : _d.enable) === true;
210
+ if (lfTrace) {
211
+ lfTrace.event({ name: "sse:end", input: null, output: null });
212
+ }
213
+ if (enabled && (langfuse_1.default === null || langfuse_1.default === void 0 ? void 0 : langfuse_1.default.shutdownAsync)) {
214
+ (_f = (_e = langfuse_1.default).shutdownAsync) === null || _f === void 0 ? void 0 : _f.call(_e);
215
+ }
216
+ }
217
+ catch (_g) { }
136
218
  }
137
219
  get isClosed() {
138
220
  return this.closed;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.langfuse = void 0;
4
+ const langfuse_1 = require("langfuse");
5
+ const secretKey = process.env.LANGFUSE_SECRET_KEY;
6
+ const publicKey = process.env.LANGFUSE_PUBLIC_KEY;
7
+ const baseUrl = process.env.LANGFUSE_BASEURL;
8
+ exports.langfuse = new langfuse_1.Langfuse({
9
+ secretKey: "sk-lf-f389bb1e-24db-4b42-b506-8d6118f45044",
10
+ publicKey: "pk-lf-ee301553-7a64-4f0f-bf6b-a2ac1e5ca2df",
11
+ baseUrl: "https://xray-langfuse.devops.xiaohongshu.com",
12
+ });
13
+ exports.default = exports.langfuse;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectorx/functions-framework",
3
- "version": "0.1.3",
3
+ "version": "0.3.0",
4
4
  "description": "VectorX Functions Framework",
5
5
  "main": "lib/index.js",
6
6
  "types": "types/index.d.ts",
@@ -25,17 +25,18 @@
25
25
  "node": ">=18.0.0"
26
26
  },
27
27
  "dependencies": {
28
- "@vectorx/ai-types": "0.1.3",
29
- "commander": "^12.1.0",
30
- "nodemon": "^3.1.10",
28
+ "@vectorx/ai-types": "0.3.0",
31
29
  "async_hooks": "^1.0.0",
32
30
  "chalk": "4",
31
+ "commander": "^12.1.0",
33
32
  "dotenv": "^16.5.0",
34
33
  "koa": "^2.14.2",
35
34
  "koa-body": "^6.0.1",
36
35
  "koa-bodyparser": "^4.4.1",
37
36
  "koa-router": "^12.0.1",
37
+ "langfuse": "^3.38.4",
38
38
  "node-fetch": "v2",
39
+ "nodemon": "^3.1.10",
39
40
  "openai": "^4.103.0",
40
41
  "radix3": "^1.1.2",
41
42
  "raw-body": "^2.5.2",
@@ -15,6 +15,9 @@ export interface FrameworkOptions {
15
15
  agentRuntimeVersion: string;
16
16
  functionsFrameworkVersion: string;
17
17
  };
18
+ redLangfuseConfig?: {
19
+ enable?: boolean;
20
+ };
18
21
  }
19
22
  export type FunctionName = string;
20
23
  declare class AgentServerFramework {
package/types/logger.d.ts CHANGED
@@ -55,6 +55,18 @@ declare class FunctionsLogger {
55
55
  logUserCodelog(): void;
56
56
  log(type: LogType, level: LogLevel, message: string, metadata?: any): void;
57
57
  getLogs(type: LogType, limit?: number): Promise<LogEntry[]>;
58
+ getLogsPaginated(options: {
59
+ type: LogType;
60
+ pageNum?: number;
61
+ pageSize?: number;
62
+ }): Promise<{
63
+ logs: LogEntry[];
64
+ pageNum: number;
65
+ pageSize: number;
66
+ total: number;
67
+ hasMore: boolean;
68
+ }>;
69
+ private getAllLogs;
58
70
  private createWinstonLogger;
59
71
  logError(error: Error | string, metadata?: any): void;
60
72
  }
package/types/server.d.ts CHANGED
@@ -7,6 +7,7 @@ import type { Router } from "./router";
7
7
  declare module "koa" {
8
8
  interface DefaultState {
9
9
  isTimeout?: boolean;
10
+ langfuseTrace?: any;
10
11
  }
11
12
  interface Application {
12
13
  routers: Map<string, Router>;
@@ -0,0 +1,4 @@
1
+ import { Langfuse } from "langfuse";
2
+ export declare const langfuse: Langfuse;
3
+ export type LangfuseClient = typeof langfuse;
4
+ export default langfuse;