@cloudbase/agent-server 1.0.1-alpha.7 → 1.0.1-alpha.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @cloudbase/agent-server
2
2
 
3
+ ## 1.0.1-alpha.8
4
+
5
+ ### Patch Changes
6
+
7
+ - alpha release 0.1.2-alpha.1
8
+ - Update all public packages to version 0.1.2-alpha.1
9
+ - Trigger automated alpha release workflow
10
+ - Includes latest features and improvements
11
+
12
+ - Updated dependencies
13
+ - @cloudbase/agent-shared@1.0.1-alpha.8
14
+
15
+ ## 1.0.1-alpha.7
16
+
17
+ ### Patch Changes
18
+
19
+ - alpha release 0.1.2-alpha.1
20
+ - Update all public packages to version 0.1.2-alpha.1
21
+ - Trigger automated alpha release workflow
22
+ - Includes latest features and improvements
23
+
24
+ - Updated dependencies
25
+ - @cloudbase/agent-shared@1.0.1-alpha.7
26
+
3
27
  ## 1.0.1-alpha.6
4
28
 
5
29
  ### Patch Changes
package/README.md ADDED
@@ -0,0 +1,253 @@
1
+ # @cloudbase/agent-server
2
+
3
+ 将 AG-UI 兼容的 Agent 部署为 HTTP 服务。
4
+
5
+ ## 什么是 AG-UI?
6
+
7
+ [AG-UI](https://ag-ui.com/) 是一个开放、轻量级、基于事件的协议,用于标准化 AI Agent 与用户界面的交互。它让 Agent 可以:
8
+
9
+ - 实时流式对话
10
+ - 双向状态同步
11
+ - 前端工具集成(Client Tools)
12
+ - 人机协作(Human-in-the-loop)
13
+
14
+ ## 这个包解决什么问题?
15
+
16
+ - **快速部署 Agent 为 HTTP 服务**:一行代码将 Agent 部署为支持 AG-UI 协议的 HTTP 服务
17
+ - **多端点支持**:自动创建 `/send-message`、`/healthz` 等端点
18
+ - **云函数兼容**:自动适配腾讯云开发 HTTP 云函数环境
19
+
20
+ ### 架构图
21
+
22
+ ```mermaid
23
+ flowchart LR
24
+ Client[AG-UI 客户端] -->|AG-UI 协议| Server["@cloudbase/agent-server"]
25
+ Server --> Adapter[Adapter<br/>LangGraph / LangChain]
26
+ Adapter --> Agent[Agent 框架]
27
+ ```
28
+
29
+ ## 配合使用
30
+
31
+ | 包名 | 作用 |
32
+ |------|------|
33
+ | `@cloudbase/agent-adapter-langgraph` | LangGraph 工作流适配器 |
34
+ | `@cloudbase/agent-adapter-langchain` | LangChain Agent 适配器 |
35
+ | `express` | HTTP 服务框架 |
36
+
37
+ ## 安装
38
+
39
+ ```bash
40
+ pnpm add @cloudbase/agent-server express
41
+ ```
42
+
43
+ ## 快速开始
44
+
45
+ ### 方式一:使用 run(最简单)
46
+
47
+ ```typescript
48
+ import { run } from "@cloudbase/agent-server";
49
+ import { LanggraphAgent } from "@cloudbase/agent-adapter-langgraph";
50
+ import { workflow } from "./workflow.js"; // 你的 LangGraph 工作流
51
+
52
+ run({
53
+ createAgent: () => ({
54
+ agent: new LanggraphAgent({ workflow }),
55
+ }),
56
+ port: 9000,
57
+ });
58
+ ```
59
+
60
+ ### 方式二:使用 createExpressServer
61
+
62
+ 创建一个配置好的 Express 应用:
63
+
64
+ ```typescript
65
+ import { createExpressServer } from "@cloudbase/agent-server";
66
+ import { LanggraphAgent } from "@cloudbase/agent-adapter-langgraph";
67
+ import { workflow } from "./workflow.js";
68
+
69
+ const app = createExpressServer({
70
+ createAgent: () => ({
71
+ agent: new LanggraphAgent({ workflow }),
72
+ }),
73
+ });
74
+
75
+ app.listen(9000, () => console.log("Listening on 9000!"));
76
+ ```
77
+
78
+ ### 方式三:使用 createExpressRoutes
79
+
80
+ 将路由挂载到现有的 Express 应用:
81
+
82
+ ```typescript
83
+ import { createExpressRoutes } from "@cloudbase/agent-server";
84
+ import { LanggraphAgent } from "@cloudbase/agent-adapter-langgraph";
85
+ import { workflow } from "./workflow.js";
86
+ import express from "express";
87
+
88
+ const app = express();
89
+
90
+ createExpressRoutes({
91
+ createAgent: () => ({
92
+ agent: new LanggraphAgent({ workflow }),
93
+ }),
94
+ express: app,
95
+ });
96
+
97
+ app.listen(9000, () => console.log("Listening on 9000!"));
98
+ ```
99
+
100
+ ## API 参考
101
+
102
+ ### run
103
+
104
+ 创建并启动 HTTP 服务。
105
+
106
+ ```typescript
107
+ run({
108
+ createAgent,
109
+ port: 9000,
110
+ });
111
+ ```
112
+
113
+ **参数:**
114
+
115
+ | 参数 | 类型 | 说明 |
116
+ |------|------|------|
117
+ | `createAgent` | `AgentCreator` | Agent 创建函数,见下方说明 |
118
+ | `port` | `number \| string` | 监听端口 |
119
+ | `basePath` | `string` | 可选,路由基础路径,默认为 `/`,云函数环境默认为 `/v1/aibot/bots/:agentId/` |
120
+ | `cors` | `boolean \| CorsOptions` | 可选,CORS 配置,默认启用 |
121
+ | `logger` | `Logger` | 可选,日志实例,用于记录服务端日志 |
122
+
123
+ ### createExpressServer
124
+
125
+ 创建一个配置好的 Express 应用。
126
+
127
+ ```typescript
128
+ const app = createExpressServer({
129
+ createAgent,
130
+ cors: true, // 可选,默认启用 CORS
131
+ });
132
+ ```
133
+
134
+ **参数:**
135
+
136
+ | 参数 | 类型 | 说明 |
137
+ |------|------|------|
138
+ | `createAgent` | `AgentCreator` | Agent 创建函数,见下方说明 |
139
+ | `basePath` | `string` | 可选,路由基础路径 |
140
+ | `cors` | `boolean \| CorsOptions` | 可选,CORS 配置,默认启用 |
141
+ | `logger` | `Logger` | 可选,日志实例,用于记录服务端日志 |
142
+
143
+ ### createExpressRoutes
144
+
145
+ 将 AG-UI 路由挂载到现有的 Express 应用。
146
+
147
+ ```typescript
148
+ createExpressRoutes({
149
+ createAgent,
150
+ express: app,
151
+ basePath, // 可选
152
+ });
153
+ ```
154
+
155
+ **参数:**
156
+
157
+ | 参数 | 类型 | 说明 |
158
+ |------|------|------|
159
+ | `createAgent` | `AgentCreator` | Agent 创建函数,见下方说明 |
160
+ | `express` | `Express` | Express 应用实例 |
161
+ | `basePath` | `string` | 可选,路由基础路径,默认为 `/`,云函数环境默认为 `/v1/aibot/bots/:agentId/` |
162
+ | `logger` | `Logger` | 可选,日志实例,用于记录服务端日志 |
163
+
164
+ ## 自动创建的端点
165
+
166
+ | 端点 | 说明 |
167
+ |------|------|
168
+ | `{basePath}send-message` | AG-UI 消息发送端点 |
169
+ | `{basePath}healthz` | 健康检查端点 |
170
+
171
+ ## createAgent 参数
172
+
173
+ `createAgent` 返回一个对象:
174
+ - `agent`:符合 [AG-UI 协议](https://docs.ag-ui.com) 的 Agent
175
+ - `cleanup`:可选,请求结束后的清理函数
176
+
177
+ ```typescript
178
+ type AgentCreator = (context?: {
179
+ request: Request; // 当前 HTTP 请求(Web Standard Request)
180
+ logger?: Logger; // 日志实例(带 requestId 上下文)
181
+ requestId?: string; // 请求追踪 ID
182
+ }) => AgentCreatorResult | Promise<AgentCreatorResult>; // 支持异步
183
+
184
+ type AgentCreatorResult = {
185
+ agent: AbstractAgent | { toAGUIAgent(): AbstractAgent }; // AG-UI 兼容的 Agent
186
+ cleanup?: () => void; // 可选,清理函数
187
+ };
188
+ ```
189
+
190
+ 使用适配器将你的 Agent 框架转换为 AG-UI 兼容的 Agent:
191
+
192
+ | 适配器 | 包名 | 说明 |
193
+ |--------|------|------|
194
+ | `LanggraphAgent` | `@cloudbase/agent-adapter-langgraph` | LangGraph 工作流适配器 |
195
+ | `LangchainAgent` | `@cloudbase/agent-adapter-langchain` | LangChain Agent 适配器 |
196
+
197
+ ```typescript
198
+ import { LanggraphAgent } from "@cloudbase/agent-adapter-langgraph";
199
+
200
+ createAgent: () => ({
201
+ agent: new LanggraphAgent({ workflow }),
202
+ })
203
+ ```
204
+
205
+ **高级用法:** `createAgent` 可以接收请求上下文,也支持异步:
206
+
207
+ ```typescript
208
+ createAgent: async (context) => {
209
+ console.log("Request ID:", context.requestId);
210
+ return { agent: new LanggraphAgent({ workflow }) };
211
+ }
212
+ ```
213
+
214
+ ## Logger
215
+
216
+ `logger` 参数用于记录服务端日志。需要实现以下接口:
217
+
218
+ ```typescript
219
+ interface Logger {
220
+ info?(message: string): void;
221
+ info?(obj: object, message?: string): void;
222
+ debug?(message: string): void;
223
+ debug?(obj: object, message?: string): void;
224
+ warn?(message: string): void;
225
+ warn?(obj: object, message?: string): void;
226
+ error?(message: string): void;
227
+ error?(obj: object, message?: string): void;
228
+ trace?(message: string): void;
229
+ trace?(obj: object, message?: string): void;
230
+ child?(bindings: object): Logger; // 创建带上下文的子 logger
231
+ }
232
+ ```
233
+
234
+ **示例:**
235
+
236
+ ```typescript
237
+ // 开发环境:使用 console
238
+ run({ createAgent, logger: console, port: 9000 });
239
+
240
+ // 生产环境:使用 pino
241
+ import pino from "pino";
242
+ run({ createAgent, logger: pino({ level: "info" }), port: 9000 });
243
+ ```
244
+
245
+ ## 文档
246
+
247
+ 📚 完整文档请参阅 [云开发 Agent 开发指南](https://docs.cloudbase.net/ai/agent-development/)
248
+
249
+ ## 相关资源
250
+
251
+ - [AG-UI 协议](https://docs.cloudbase.net/ai/agent-development/ag-ui-protocol)
252
+ - [框架开发指南](https://docs.cloudbase.net/ai/agent-development/frameworks/)
253
+ - [部署指南](https://docs.cloudbase.net/ai/agent-development/deploy/)
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
+ import * as _cloudbase_agent_observability_server from '@cloudbase/agent-observability/server';
1
2
  import { CreateCopilotRuntimeServerOptions } from '@copilotkit/runtime';
2
3
  import { CopilotRuntimeOptions } from '@copilotkit/runtime/v2';
3
4
  import expressLib, { Express } from 'express';
4
5
  import * as _ag_ui_client from '@ag-ui/client';
5
6
  import { AbstractAgent, RunAgentInput } from '@ag-ui/client';
6
7
  import cors from 'cors';
7
- import { SendMessageInput } from '@cloudbase/agent-shared';
8
+ import { Logger, SendMessageInput } from '@cloudbase/agent-shared';
9
+ export { LogFn, Logger, createConsoleLogger, isErrorWithCode, noopLogger } from '@cloudbase/agent-shared';
8
10
  import { Repeater } from '@repeaterjs/repeater';
9
11
  import * as _whatwg_node_server from '@whatwg-node/server';
10
12
  import { OpenAI } from 'openai';
@@ -16,6 +18,10 @@ import { OpenAI } from 'openai';
16
18
  interface AgentCreatorContext {
17
19
  /** The incoming HTTP request (Web Standard Request) */
18
20
  request: Request;
21
+ /** Logger instance for this request (with requestId context). Only available when using AGUI routes. */
22
+ logger?: Logger;
23
+ /** Unique request ID for tracing. Only available when using AGUI routes. */
24
+ requestId?: string;
19
25
  }
20
26
  type AgentCreatorRet = {
21
27
  agent: AbstractAgent | {
@@ -34,16 +40,61 @@ interface ICreateServer {
34
40
  cors?: boolean | cors.CorsOptions;
35
41
  useAGUI?: boolean;
36
42
  aguiOptions?: AGUIOptions;
43
+ /**
44
+ * Logger instance for structured logging.
45
+ *
46
+ * @default noopLogger (silent - no output)
47
+ *
48
+ * @example
49
+ * // Development: see all logs
50
+ * createExpressServer({ createAgent, logger: console });
51
+ *
52
+ * // Production: structured JSON logs
53
+ * import pino from 'pino';
54
+ * createExpressServer({ createAgent, logger: pino({ level: 'info' }) });
55
+ */
56
+ logger?: Logger;
57
+ /**
58
+ * Observability configuration for trace exporters.
59
+ *
60
+ * Requires @cloudbase/agent-observability package to be installed.
61
+ * If the package is not installed, this option is silently ignored.
62
+ *
63
+ * @example
64
+ * // Console exporter (from env AUTO_TRACES_STDOUT)
65
+ * createExpressServer({ createAgent, observability: { type: 'console' } });
66
+ *
67
+ * // OTLP exporter (Langfuse, Jaeger, etc.)
68
+ * createExpressServer({
69
+ * createAgent,
70
+ * observability: {
71
+ * type: 'otlp',
72
+ * url: 'https://cloud.langfuse.com/api/public/otlp/v1/traces',
73
+ * headers: { 'Authorization': 'Basic xxx' }
74
+ * }
75
+ * });
76
+ *
77
+ * // Multiple exporters
78
+ * createExpressServer({
79
+ * createAgent,
80
+ * observability: [
81
+ * { type: 'console' },
82
+ * { type: 'otlp', url: 'http://localhost:4318/v1/traces' }
83
+ * ]
84
+ * });
85
+ */
86
+ observability?: _cloudbase_agent_observability_server.ObservabilityConfig | _cloudbase_agent_observability_server.ObservabilityConfig[];
37
87
  }
38
88
  interface IRun extends ICreateServer {
39
89
  port?: number | string;
40
90
  }
41
91
  interface ICreateExpressRoutes extends Omit<ICreateServer, "cors"> {
42
92
  express: Express;
93
+ observability?: ICreateServer['observability'];
43
94
  }
44
95
  declare function run(props: IRun): void;
45
96
  declare function createExpressServer(props: ICreateServer): Express;
46
- declare function createExpressRoutes({ createAgent, basePath: _basePath, express, useAGUI: _useAGUI, aguiOptions, }: ICreateExpressRoutes): expressLib.Express;
97
+ declare function createExpressRoutes({ createAgent, basePath: _basePath, express, useAGUI: _useAGUI, aguiOptions, logger: _logger, observability, }: ICreateExpressRoutes): expressLib.Express;
47
98
  interface AGUIOptions {
48
99
  runtimeOptions?: Partial<CopilotRuntimeOptions>;
49
100
  endpointOptions?: Partial<CreateCopilotRuntimeServerOptions>;
@@ -86,10 +137,21 @@ declare function handler$1(input: RunAgentInput, agent: AbstractAgent): Repeater
86
137
  rawEvent?: any;
87
138
  }, any, unknown>;
88
139
 
89
- declare function createServerAdapter$1(createAgent: AgentCreator): _whatwg_node_server.ServerAdapter<{}, _whatwg_node_server.ServerAdapterBaseObject<{}, (request: Request) => Promise<Response>>>;
140
+ /**
141
+ * Options for createServerAdapter
142
+ */
143
+ interface CreateServerAdapterOptions {
144
+ /**
145
+ * Logger instance for structured logging.
146
+ * @default noopLogger (silent)
147
+ */
148
+ logger?: Logger;
149
+ }
150
+ declare function createServerAdapter$1(createAgent: AgentCreator, options?: CreateServerAdapterOptions): _whatwg_node_server.ServerAdapter<{}, _whatwg_node_server.ServerAdapterBaseObject<{}, (request: Request) => Promise<Response>>>;
90
151
 
152
+ type index$3_CreateServerAdapterOptions = CreateServerAdapterOptions;
91
153
  declare namespace index$3 {
92
- export { createServerAdapter$1 as createServerAdapter, handler$1 as handler };
154
+ export { type index$3_CreateServerAdapterOptions as CreateServerAdapterOptions, createServerAdapter$1 as createServerAdapter, handler$1 as handler };
93
155
  }
94
156
 
95
157
  declare const serverAdapter: _whatwg_node_server.ServerAdapter<{}, _whatwg_node_server.ServerAdapterBaseObject<{}, () => Response>>;
@@ -119,4 +181,63 @@ declare namespace index {
119
181
  export { index$2 as healthz, index$1 as openai, index$4 as sendMessage, index$3 as sendMessageAGUI };
120
182
  }
121
183
 
122
- export { type AgentCreator, type AgentCreatorContext, index as agui, createExpressRoutes, createExpressServer, run };
184
+ /**
185
+ * Generates a unique request ID for tracing.
186
+ *
187
+ * The request ID is used to:
188
+ * 1. Correlate all logs from a single request
189
+ * 2. Include in error responses for debugging
190
+ * 3. Trace requests across distributed systems
191
+ *
192
+ * @param prefix - Optional prefix for the request ID (default: 'req')
193
+ * @returns A unique request ID string
194
+ *
195
+ * @example
196
+ * generateRequestId() // => 'req-a1b2c3d4-e5f6-7890-abcd-ef1234567890'
197
+ * generateRequestId('agui') // => 'agui-a1b2c3d4-e5f6-7890-abcd-ef1234567890'
198
+ */
199
+ declare function generateRequestId(prefix?: string): string;
200
+ /**
201
+ * Extracts request ID from incoming request headers.
202
+ * Supports common request ID header conventions.
203
+ *
204
+ * Header priority (first found wins):
205
+ * 1. x-request-id
206
+ * 2. x-correlation-id
207
+ * 3. x-trace-id
208
+ *
209
+ * @param headers - Request headers (Headers object or plain object)
210
+ * @returns The request ID from headers, or undefined if not found
211
+ */
212
+ declare function extractRequestId(headers: Headers | Record<string, string | string[] | undefined>): string | undefined;
213
+ /**
214
+ * Gets or generates a request ID.
215
+ * First tries to extract from headers, then generates a new one.
216
+ *
217
+ * @param headers - Request headers
218
+ * @param prefix - Prefix for generated IDs (default: 'req')
219
+ * @returns Request ID (either extracted or generated)
220
+ */
221
+ declare function getOrGenerateRequestId(headers: Headers | Record<string, string | string[] | undefined>, prefix?: string): string;
222
+
223
+ /**
224
+ * AG-Kit Error Handling
225
+ *
226
+ * Error handling follows these rules:
227
+ * 1. Pre-stream errors: Return JSON `{error: {code, message}, requestId}`
228
+ * 2. In-stream errors: Emit RunError event `{type: "RUN_ERROR", code, message}`
229
+ *
230
+ * Adapters can:
231
+ * - Directly emit RunError events into the stream
232
+ * - Throw errors with a `code` property (server extracts and converts to RunError)
233
+ * - Throw other errors (server uses fallback INTERNAL_ERROR code)
234
+ */
235
+ declare const ErrorCode: {
236
+ /** Invalid request format or parameters (400) */
237
+ readonly INVALID_REQUEST: "INVALID_REQUEST";
238
+ /** Internal server error (500) */
239
+ readonly INTERNAL_ERROR: "INTERNAL_ERROR";
240
+ };
241
+ type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];
242
+
243
+ export { type AgentCreator, type AgentCreatorContext, ErrorCode, type ErrorCodeType, index as agui, createExpressRoutes, createExpressServer, extractRequestId, generateRequestId, getOrGenerateRequestId, run };
package/dist/index.js CHANGED
@@ -29,9 +29,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  // src/index.ts
30
30
  var index_exports = {};
31
31
  __export(index_exports, {
32
+ ErrorCode: () => ErrorCode,
32
33
  agui: () => agui_exports,
34
+ createConsoleLogger: () => import_agent_shared2.createConsoleLogger,
33
35
  createExpressRoutes: () => createExpressRoutes,
34
36
  createExpressServer: () => createExpressServer,
37
+ extractRequestId: () => extractRequestId,
38
+ generateRequestId: () => generateRequestId,
39
+ getOrGenerateRequestId: () => getOrGenerateRequestId,
40
+ isErrorWithCode: () => import_agent_shared3.isErrorWithCode,
41
+ noopLogger: () => import_agent_shared2.noopLogger,
35
42
  run: () => run
36
43
  });
37
44
  module.exports = __toCommonJS(index_exports);
@@ -231,9 +238,199 @@ function handler2(input, agent) {
231
238
  // src/agui/sendMessageAGUI/server.ts
232
239
  var import_server3 = require("@whatwg-node/server");
233
240
  var import_client2 = require("@ag-ui/client");
241
+ var import_uuid3 = require("uuid");
242
+
243
+ // src/logger/index.ts
244
+ var import_agent_shared2 = require("@cloudbase/agent-shared");
245
+
246
+ // src/logger/request-id.ts
234
247
  var import_uuid2 = require("uuid");
235
- function createServerAdapter2(createAgent) {
248
+ function generateRequestId(prefix = "req") {
249
+ return `${prefix}-${(0, import_uuid2.v4)()}`;
250
+ }
251
+ function extractRequestId(headers) {
252
+ const headerNames = ["x-request-id", "x-correlation-id", "x-trace-id"];
253
+ for (const name of headerNames) {
254
+ let value;
255
+ if (headers instanceof Headers) {
256
+ value = headers.get(name) || void 0;
257
+ } else {
258
+ const raw = headers[name];
259
+ value = Array.isArray(raw) ? raw[0] : raw;
260
+ }
261
+ if (value) {
262
+ return value;
263
+ }
264
+ }
265
+ return void 0;
266
+ }
267
+ function getOrGenerateRequestId(headers, prefix = "req") {
268
+ return extractRequestId(headers) || generateRequestId(prefix);
269
+ }
270
+
271
+ // src/agui/sendMessageAGUI/schemas.ts
272
+ var import_v4 = require("zod/v4");
273
+ var FunctionCallSchema = import_v4.z.object({
274
+ name: import_v4.z.string(),
275
+ arguments: import_v4.z.string()
276
+ });
277
+ var ToolCallSchema = import_v4.z.object({
278
+ id: import_v4.z.string(),
279
+ type: import_v4.z.literal("function"),
280
+ function: FunctionCallSchema
281
+ });
282
+ var ToolSchema = import_v4.z.object({
283
+ name: import_v4.z.string(),
284
+ description: import_v4.z.string(),
285
+ parameters: import_v4.z.any()
286
+ });
287
+ var TextInputContentSchema = import_v4.z.object({
288
+ type: import_v4.z.literal("text"),
289
+ text: import_v4.z.string()
290
+ });
291
+ var BinaryInputContentObjectSchema = import_v4.z.object({
292
+ type: import_v4.z.literal("binary"),
293
+ mimeType: import_v4.z.string(),
294
+ id: import_v4.z.string().optional(),
295
+ url: import_v4.z.string().optional(),
296
+ data: import_v4.z.string().optional(),
297
+ filename: import_v4.z.string().optional()
298
+ });
299
+ var ensureBinaryPayload = (value, ctx) => {
300
+ if (!value.id && !value.url && !value.data) {
301
+ ctx.addIssue({
302
+ code: import_v4.z.ZodIssueCode.custom,
303
+ message: "BinaryInputContent requires at least one of id, url, or data.",
304
+ path: ["id"]
305
+ });
306
+ }
307
+ };
308
+ var BinaryInputContentSchema = BinaryInputContentObjectSchema.superRefine((value, ctx) => {
309
+ ensureBinaryPayload(value, ctx);
310
+ });
311
+ var InputContentBaseSchema = import_v4.z.discriminatedUnion("type", [
312
+ TextInputContentSchema,
313
+ BinaryInputContentObjectSchema
314
+ ]);
315
+ var InputContentSchema = InputContentBaseSchema.superRefine(
316
+ (value, ctx) => {
317
+ if (value.type === "binary") {
318
+ ensureBinaryPayload(value, ctx);
319
+ }
320
+ }
321
+ );
322
+ var BaseMessageSchema = import_v4.z.object({
323
+ id: import_v4.z.string(),
324
+ role: import_v4.z.string(),
325
+ content: import_v4.z.string().optional(),
326
+ name: import_v4.z.string().optional()
327
+ });
328
+ var DeveloperMessageSchema = BaseMessageSchema.extend({
329
+ role: import_v4.z.literal("developer"),
330
+ content: import_v4.z.string()
331
+ });
332
+ var SystemMessageSchema = BaseMessageSchema.extend({
333
+ role: import_v4.z.literal("system"),
334
+ content: import_v4.z.string()
335
+ });
336
+ var AssistantMessageSchema = BaseMessageSchema.extend({
337
+ role: import_v4.z.literal("assistant"),
338
+ content: import_v4.z.string().optional(),
339
+ toolCalls: import_v4.z.array(ToolCallSchema).optional()
340
+ });
341
+ var UserMessageSchema = BaseMessageSchema.extend({
342
+ role: import_v4.z.literal("user"),
343
+ content: import_v4.z.union([import_v4.z.string(), import_v4.z.array(InputContentSchema)])
344
+ });
345
+ var ToolMessageSchema = import_v4.z.object({
346
+ id: import_v4.z.string(),
347
+ content: import_v4.z.string(),
348
+ role: import_v4.z.literal("tool"),
349
+ toolCallId: import_v4.z.string(),
350
+ error: import_v4.z.string().optional()
351
+ });
352
+ var ActivityMessageSchema = import_v4.z.object({
353
+ id: import_v4.z.string(),
354
+ role: import_v4.z.literal("activity"),
355
+ activityType: import_v4.z.string(),
356
+ content: import_v4.z.record(import_v4.z.string(), import_v4.z.any())
357
+ });
358
+ var MessageSchema = import_v4.z.discriminatedUnion("role", [
359
+ DeveloperMessageSchema,
360
+ SystemMessageSchema,
361
+ AssistantMessageSchema,
362
+ UserMessageSchema,
363
+ ToolMessageSchema,
364
+ ActivityMessageSchema
365
+ ]);
366
+ var ContextSchema = import_v4.z.object({
367
+ description: import_v4.z.string(),
368
+ value: import_v4.z.string()
369
+ });
370
+ var ServerRunAgentInputSchema = import_v4.z.object({
371
+ threadId: import_v4.z.string().optional(),
372
+ // Modified: optional instead of required
373
+ runId: import_v4.z.string(),
374
+ parentRunId: import_v4.z.string().optional(),
375
+ state: import_v4.z.any(),
376
+ messages: import_v4.z.array(MessageSchema),
377
+ tools: import_v4.z.array(ToolSchema),
378
+ context: import_v4.z.array(ContextSchema),
379
+ forwardedProps: import_v4.z.any()
380
+ });
381
+
382
+ // src/errors/index.ts
383
+ var import_agent_shared3 = require("@cloudbase/agent-shared");
384
+ var ErrorCode = {
385
+ /** Invalid request format or parameters (400) */
386
+ INVALID_REQUEST: "INVALID_REQUEST",
387
+ /** Internal server error (500) */
388
+ INTERNAL_ERROR: "INTERNAL_ERROR"
389
+ };
390
+
391
+ // src/agui/sendMessageAGUI/server.ts
392
+ var startObservation;
393
+ var setupObservability;
394
+ var observabilityLoadAttempted = false;
395
+ async function loadObservability() {
396
+ if (!observabilityLoadAttempted) {
397
+ observabilityLoadAttempted = true;
398
+ try {
399
+ const obs = await import("@cloudbase/agent-observability");
400
+ const obsServer = await import("@cloudbase/agent-observability/server");
401
+ startObservation = obs.startObservation;
402
+ setupObservability = obsServer.setupObservability;
403
+ return true;
404
+ } catch (e) {
405
+ return false;
406
+ }
407
+ }
408
+ return !!startObservation;
409
+ }
410
+ async function ensureObservabilityReady() {
411
+ if (!setupObservability) return false;
412
+ try {
413
+ const timeoutPromise = new Promise((_, reject) => {
414
+ setTimeout(() => reject(new Error("Observability setup timeout")), 2e3);
415
+ });
416
+ await Promise.race([
417
+ setupObservability(),
418
+ timeoutPromise
419
+ ]);
420
+ return true;
421
+ } catch (e) {
422
+ return false;
423
+ }
424
+ }
425
+ function createServerAdapter2(createAgent, options) {
426
+ var _a;
427
+ const { logger: parentLogger = import_agent_shared2.noopLogger } = options ?? {};
428
+ const adapterLogger = ((_a = parentLogger.child) == null ? void 0 : _a.call(parentLogger, { component: "sendMessageAGUI" })) ?? parentLogger;
236
429
  return (0, import_server3.createServerAdapter)(async (request) => {
430
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
431
+ const requestId = getOrGenerateRequestId(request.headers, "agui");
432
+ const logger = ((_a2 = adapterLogger.child) == null ? void 0 : _a2.call(adapterLogger, { requestId })) ?? adapterLogger;
433
+ (_b = logger.info) == null ? void 0 : _b.call(logger, "Request received");
237
434
  const inputRes = await safeAsync(async () => {
238
435
  const rawInput = await request.json();
239
436
  const inputWithDefaults = {
@@ -242,16 +439,24 @@ function createServerAdapter2(createAgent) {
242
439
  state: {},
243
440
  forwardedProps: {},
244
441
  ...rawInput,
245
- runId: typeof rawInput.runId === "string" && rawInput.runId ? rawInput.runId : (0, import_uuid2.v4)()
442
+ runId: typeof rawInput.runId === "string" && rawInput.runId ? rawInput.runId : (0, import_uuid3.v4)()
246
443
  };
247
- return import_client2.RunAgentInputSchema.parse(inputWithDefaults);
444
+ return ServerRunAgentInputSchema.parse(inputWithDefaults);
248
445
  });
249
446
  if ("error" in inputRes) {
250
447
  const { error } = inputRes;
251
- console.error("[AGUI Server] Pre-stream error:", error);
448
+ (_c = logger.warn) == null ? void 0 : _c.call(
449
+ logger,
450
+ { err: error instanceof Error ? error.message : String(error) },
451
+ "Input validation failed"
452
+ );
252
453
  return new Response(
253
454
  JSON.stringify({
254
- error: error instanceof Error ? error.message : String(error)
455
+ error: {
456
+ code: ErrorCode.INVALID_REQUEST,
457
+ message: error instanceof Error ? error.message : String(error)
458
+ },
459
+ requestId
255
460
  }),
256
461
  {
257
462
  status: 400,
@@ -259,8 +464,25 @@ function createServerAdapter2(createAgent) {
259
464
  }
260
465
  );
261
466
  }
467
+ const input = inputRes.result;
468
+ const lastUserMessage = input.messages.filter((m) => m.role === "user").pop();
469
+ (_f = logger.debug) == null ? void 0 : _f.call(
470
+ logger,
471
+ {
472
+ runId: input.runId,
473
+ threadId: input.threadId,
474
+ messageCount: input.messages.length,
475
+ toolCount: ((_d = input.tools) == null ? void 0 : _d.length) ?? 0,
476
+ tools: (_e = input.tools) == null ? void 0 : _e.map((t) => t.name),
477
+ lastUserMessage: typeof (lastUserMessage == null ? void 0 : lastUserMessage.content) === "string" ? lastUserMessage.content.slice(0, 200) : void 0
478
+ },
479
+ "Input validated"
480
+ );
481
+ (_g = logger.trace) == null ? void 0 : _g.call(logger, { input }, "Full request input");
262
482
  const createAgentRes = await safeAsync(async () => {
263
- const res = await Promise.resolve(createAgent({ request }));
483
+ const res = await Promise.resolve(
484
+ createAgent({ request, logger, requestId })
485
+ );
264
486
  return {
265
487
  cleanup: res.cleanup,
266
488
  agent: "toAGUIAgent" in res.agent ? res.agent.toAGUIAgent() : res.agent
@@ -268,10 +490,16 @@ function createServerAdapter2(createAgent) {
268
490
  });
269
491
  if ("error" in createAgentRes) {
270
492
  const { error } = createAgentRes;
271
- console.error("[AGUI Server] Pre-stream error:", error);
493
+ (_h = logger.error) == null ? void 0 : _h.call(logger, { err: error }, "Agent creation failed");
494
+ const errorCode = (0, import_agent_shared3.isErrorWithCode)(error) ? error.code : ErrorCode.INTERNAL_ERROR;
495
+ const errorMessage = error instanceof Error ? error.message : String(error);
272
496
  return new Response(
273
497
  JSON.stringify({
274
- error: error instanceof Error ? error.message : String(error)
498
+ error: {
499
+ code: errorCode,
500
+ message: errorMessage
501
+ },
502
+ requestId
275
503
  }),
276
504
  {
277
505
  status: 500,
@@ -279,38 +507,116 @@ function createServerAdapter2(createAgent) {
279
507
  }
280
508
  );
281
509
  }
282
- const events = handler2(inputRes.result, createAgentRes.result.agent);
283
- let heartbeat;
510
+ const hasObservability = await loadObservability();
511
+ let serverSpan = null;
512
+ let serverContextData = null;
513
+ if (hasObservability && startObservation) {
514
+ try {
515
+ const isReady = await ensureObservabilityReady();
516
+ if (isReady) {
517
+ serverSpan = startObservation(
518
+ "AG-UI.Server",
519
+ {
520
+ "http.method": request.method,
521
+ "http.url": request.url,
522
+ "http.host": request.headers.get("host") || "unknown",
523
+ "http.user_agent": request.headers.get("user-agent") || "unknown",
524
+ "agui.thread_id": inputRes.result.threadId,
525
+ "agui.run_id": inputRes.result.runId
526
+ },
527
+ { asType: "span" }
528
+ );
529
+ const spanContext = serverSpan.otelSpan.spanContext();
530
+ serverContextData = {
531
+ traceId: spanContext.traceId,
532
+ spanId: spanContext.spanId,
533
+ traceFlags: spanContext.traceFlags
534
+ };
535
+ inputRes.result.forwardedProps = {
536
+ ...inputRes.result.forwardedProps,
537
+ __agui_server_context: serverContextData
538
+ };
539
+ (_i = logger.debug) == null ? void 0 : _i.call(logger, "\u2713 Server span created:", {
540
+ traceId: serverContextData.traceId,
541
+ spanId: serverContextData.spanId
542
+ });
543
+ } else {
544
+ (_j = logger.debug) == null ? void 0 : _j.call(logger, "Observability not ready, skipping span creation");
545
+ }
546
+ } catch (e) {
547
+ (_k = logger.debug) == null ? void 0 : _k.call(logger, "Failed to create server span:", e);
548
+ }
549
+ }
284
550
  let cleanupCalled = false;
285
551
  const safeCleanup = () => {
286
- var _a, _b;
552
+ var _a3, _b2, _c2;
287
553
  if (!cleanupCalled) {
288
554
  cleanupCalled = true;
289
555
  try {
290
- (_b = (_a = createAgentRes.result).cleanup) == null ? void 0 : _b.call(_a);
556
+ (_b2 = (_a3 = createAgentRes.result).cleanup) == null ? void 0 : _b2.call(_a3);
291
557
  } catch (e) {
292
- console.error(e);
558
+ (_c2 = logger.error) == null ? void 0 : _c2.call(logger, { err: e }, "Cleanup error");
293
559
  }
294
560
  }
295
561
  };
562
+ const eventsResult = safe(
563
+ () => handler2(
564
+ inputRes.result,
565
+ createAgentRes.result.agent
566
+ )
567
+ );
568
+ if ("error" in eventsResult) {
569
+ const { error } = eventsResult;
570
+ (_l = logger.error) == null ? void 0 : _l.call(logger, { err: error }, "Run agent failed");
571
+ const errorCode = (0, import_agent_shared3.isErrorWithCode)(error) ? error.code : ErrorCode.INTERNAL_ERROR;
572
+ const errorMessage = error instanceof Error ? error.message : String(error);
573
+ return new Response(
574
+ JSON.stringify({
575
+ error: {
576
+ code: errorCode,
577
+ message: errorMessage
578
+ },
579
+ requestId
580
+ }),
581
+ {
582
+ status: 500,
583
+ headers: { "Content-Type": "application/json" }
584
+ }
585
+ );
586
+ }
587
+ const { result: events } = eventsResult;
588
+ let heartbeat;
296
589
  const stream = new ReadableStream({
297
590
  async start(controller) {
591
+ var _a3, _b2, _c2, _d2, _e2;
298
592
  const encoder = new TextEncoder();
299
593
  heartbeat = setInterval(() => {
300
594
  controller.enqueue(encoder.encode(":ping\n\n"));
301
595
  }, 15 * 1e3);
596
+ let eventCount = 0;
302
597
  try {
303
598
  for await (const event of events) {
599
+ eventCount++;
600
+ (_a3 = logger.debug) == null ? void 0 : _a3.call(
601
+ logger,
602
+ { eventType: event.type, eventCount },
603
+ "Emitting SSE event"
604
+ );
605
+ (_b2 = logger.trace) == null ? void 0 : _b2.call(logger, { aguiEvent: event }, "SSE event content");
304
606
  const sseChunk = `data: ${JSON.stringify(event)}
305
607
 
306
608
  `;
307
609
  controller.enqueue(encoder.encode(sseChunk));
308
610
  }
611
+ (_c2 = logger.info) == null ? void 0 : _c2.call(logger, { eventCount }, "Request completed");
309
612
  } catch (error) {
310
- console.error("[AGUI Server] Stream error:", error);
613
+ (_d2 = logger.error) == null ? void 0 : _d2.call(logger, { err: error }, "Stream error");
614
+ const errorCode = (0, import_agent_shared3.isErrorWithCode)(error) ? error.code : ErrorCode.INTERNAL_ERROR;
615
+ const errorMessage = error instanceof Error ? error.message : String(error);
311
616
  const errorEvent = {
312
617
  type: import_client2.EventType.RUN_ERROR,
313
- message: error instanceof Error ? error.message : String(error)
618
+ code: errorCode,
619
+ message: errorMessage
314
620
  };
315
621
  controller.enqueue(
316
622
  encoder.encode(`data: ${JSON.stringify(errorEvent)}
@@ -321,11 +627,21 @@ function createServerAdapter2(createAgent) {
321
627
  if (heartbeat) clearInterval(heartbeat);
322
628
  controller.close();
323
629
  safeCleanup();
630
+ if (serverSpan) {
631
+ serverSpan.end();
632
+ (_e2 = logger.debug) == null ? void 0 : _e2.call(logger, "\u2713 Server span ended");
633
+ }
324
634
  }
325
635
  },
326
636
  cancel() {
637
+ var _a3, _b2;
638
+ (_a3 = logger.info) == null ? void 0 : _a3.call(logger, "Request cancelled by client");
327
639
  if (heartbeat) clearInterval(heartbeat);
328
640
  safeCleanup();
641
+ if (serverSpan) {
642
+ serverSpan.end();
643
+ (_b2 = logger.debug) == null ? void 0 : _b2.call(logger, "\u2713 Server span ended (cancelled)");
644
+ }
329
645
  }
330
646
  });
331
647
  const headers = new Headers({
@@ -345,6 +661,15 @@ async function safeAsync(fn) {
345
661
  return { error };
346
662
  }
347
663
  }
664
+ function safe(fn) {
665
+ try {
666
+ return {
667
+ result: fn()
668
+ };
669
+ } catch (error) {
670
+ return { error };
671
+ }
672
+ }
348
673
 
349
674
  // src/agui/healthz/index.ts
350
675
  var healthz_exports = {};
@@ -362,7 +687,7 @@ __export(openai_exports, {
362
687
  });
363
688
 
364
689
  // src/agui/openai/handler.ts
365
- var import_uuid3 = require("uuid");
690
+ var import_uuid4 = require("uuid");
366
691
  var import_repeater3 = require("@repeaterjs/repeater");
367
692
  function handler3(input, agent) {
368
693
  var _a;
@@ -374,12 +699,12 @@ function handler3(input, agent) {
374
699
  description: tool.function.description,
375
700
  parameters: tool.function.parameters
376
701
  })),
377
- conversationId: (0, import_uuid3.v4)()
702
+ conversationId: (0, import_uuid4.v4)()
378
703
  },
379
704
  agent
380
705
  );
381
706
  return new import_repeater3.Repeater(async (push, stop) => {
382
- const id = (0, import_uuid3.v4)();
707
+ const id = (0, import_uuid4.v4)();
383
708
  let tools = [];
384
709
  let lastWithToolCall = false;
385
710
  let maxIndex = 0;
@@ -543,6 +868,13 @@ var import_cors = __toESM(require("cors"));
543
868
  var import_async_hooks = require("async_hooks");
544
869
  var import_server8 = require("@whatwg-node/server");
545
870
  var DefaultFetchAPI = __toESM(require("@whatwg-node/fetch"));
871
+ async function setupObservabilityIfAvailable(configs) {
872
+ try {
873
+ const { setupObservability: setupObservability2 } = await import("@cloudbase/agent-observability/server");
874
+ await setupObservability2(configs);
875
+ } catch (error) {
876
+ }
877
+ }
546
878
  var preparedAgentStorage = new import_async_hooks.AsyncLocalStorage();
547
879
  function agentCloneFn() {
548
880
  const preparedAgent = preparedAgentStorage.getStore();
@@ -576,16 +908,27 @@ function createExpressRoutes({
576
908
  basePath: _basePath,
577
909
  express,
578
910
  useAGUI: _useAGUI,
579
- aguiOptions
911
+ aguiOptions,
912
+ logger: _logger,
913
+ observability
580
914
  }) {
915
+ var _a, _b, _c;
581
916
  const useAGUI = _useAGUI ?? true;
917
+ const logger = _logger ?? import_agent_shared2.noopLogger;
582
918
  const basePath = _basePath ?? (process.env.TENCENTCLOUD_RUNENV === "SCF" ? "/v1/aibot/bots/:agentId/" : "/");
583
- const sendMessageServerAdapter = useAGUI ? sendMessageAGUI_exports.createServerAdapter(createAgent) : sendMessage_exports.createServerAdapter(createAgent);
919
+ const serverLogger = ((_a = logger.child) == null ? void 0 : _a.call(logger, { component: "server" })) ?? logger;
920
+ (_b = serverLogger.debug) == null ? void 0 : _b.call(serverLogger, { basePath, useAGUI }, "Initializing server routes");
921
+ if (observability) {
922
+ setupObservabilityIfAvailable(observability).catch(() => {
923
+ });
924
+ }
925
+ const sendMessageServerAdapter = useAGUI ? sendMessageAGUI_exports.createServerAdapter(createAgent, { logger: serverLogger }) : sendMessage_exports.createServerAdapter(createAgent);
584
926
  if (useAGUI) {
585
927
  createAGUIRoute({
586
928
  basePath,
587
929
  express,
588
930
  createAgent,
931
+ logger: serverLogger,
589
932
  ...aguiOptions || {}
590
933
  });
591
934
  }
@@ -594,6 +937,7 @@ function createExpressRoutes({
594
937
  express.use(`${basePath}send-message`, sendMessageServerAdapter);
595
938
  express.use(`${basePath}healthz`, healthzServerAdapter);
596
939
  express.use(`${basePath}chat/completions`, openaiServerAdapter);
940
+ (_c = serverLogger.info) == null ? void 0 : _c.call(serverLogger, { basePath }, "Server routes initialized");
597
941
  return express;
598
942
  }
599
943
  var AGUIRpcHandlerPromise = null;
@@ -602,11 +946,13 @@ function getAGUIRpcHandler({
602
946
  runtimeOptions,
603
947
  basePath,
604
948
  endpointOptions,
605
- request
949
+ request,
950
+ logger,
951
+ requestId
606
952
  }) {
607
953
  if (AGUIRpcHandlerPromise) return AGUIRpcHandlerPromise;
608
954
  AGUIRpcHandlerPromise = (async () => {
609
- const { agent } = await createAgent({ request });
955
+ const { agent } = await createAgent({ request, logger, requestId });
610
956
  const templateAgent = "toAGUIAgent" in agent ? agent.toAGUIAgent() : agent;
611
957
  templateAgent.clone = agentCloneFn;
612
958
  const runtime = new import_runtime.CopilotRuntime({
@@ -629,12 +975,21 @@ function createAGUIRoute({
629
975
  basePath,
630
976
  createAgent,
631
977
  runtimeOptions,
632
- endpointOptions
978
+ endpointOptions,
979
+ logger
633
980
  }) {
981
+ var _a;
982
+ const routeLogger = ((_a = logger.child) == null ? void 0 : _a.call(logger, { component: "agui-route" })) ?? logger;
634
983
  express.post(`${basePath}agui`, import_express.default.json(), async (req, res) => {
984
+ var _a2, _b;
635
985
  const webRequest = (0, import_server8.normalizeNodeRequest)(req, DefaultFetchAPI);
986
+ const requestId = getOrGenerateRequestId(webRequest.headers, "agui");
987
+ const requestLogger = ((_a2 = routeLogger.child) == null ? void 0 : _a2.call(routeLogger, { requestId })) ?? routeLogger;
988
+ (_b = requestLogger.info) == null ? void 0 : _b.call(requestLogger, { path: `${basePath}agui` }, "Request received");
636
989
  const { agent: rawAgent, cleanup } = await createAgent({
637
- request: webRequest
990
+ request: webRequest,
991
+ logger: requestLogger,
992
+ requestId
638
993
  });
639
994
  const preparedAgent = "toAGUIAgent" in rawAgent ? rawAgent.toAGUIAgent() : rawAgent;
640
995
  preparedAgent.clone = agentCloneFn;
@@ -643,7 +998,9 @@ function createAGUIRoute({
643
998
  basePath,
644
999
  runtimeOptions,
645
1000
  endpointOptions,
646
- request: webRequest
1001
+ request: webRequest,
1002
+ logger: requestLogger,
1003
+ requestId
647
1004
  });
648
1005
  preparedAgentStorage.run(preparedAgent, () => {
649
1006
  rpcHandler(req, res);
@@ -658,8 +1015,15 @@ function isCorsOptions(cors2) {
658
1015
  }
659
1016
  // Annotate the CommonJS export names for ESM import in node:
660
1017
  0 && (module.exports = {
1018
+ ErrorCode,
661
1019
  agui,
1020
+ createConsoleLogger,
662
1021
  createExpressRoutes,
663
1022
  createExpressServer,
1023
+ extractRequestId,
1024
+ generateRequestId,
1025
+ getOrGenerateRequestId,
1026
+ isErrorWithCode,
1027
+ noopLogger,
664
1028
  run
665
1029
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/agent-server",
3
- "version": "1.0.1-alpha.7",
3
+ "version": "1.0.1-alpha.9",
4
4
  "main": "dist/index.js",
5
5
  "files": [
6
6
  "dist/",
@@ -20,13 +20,19 @@
20
20
  "express": "^5.1.0",
21
21
  "openai": "6.3.0",
22
22
  "uuid": "^10.0.0",
23
- "zod": "^4.1.12",
24
- "@cloudbase/agent-shared": "^1.0.1-alpha.7"
23
+ "@cloudbase/agent-shared": "^1.0.1-alpha.9"
24
+ },
25
+ "peerDependencies": {
26
+ "zod": "^3.25.0 || ^4.0.0"
27
+ },
28
+ "optionalDependencies": {
29
+ "@cloudbase/agent-observability": "1.0.1-alpha.9"
25
30
  },
26
31
  "devDependencies": {
27
32
  "@types/cors": "^2.8.19",
28
33
  "@types/express": "^5.0.3",
29
- "tsup": "^8.5.0"
34
+ "tsup": "^8.5.0",
35
+ "zod": "^4.0.0"
30
36
  },
31
37
  "scripts": {
32
38
  "test": "echo \"Error: no test specified\" && exit 1",