@cloudbase/agent-server 0.0.15 → 0.0.18

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.d.ts CHANGED
@@ -10,6 +10,14 @@ import { Repeater } from '@repeaterjs/repeater';
10
10
  import * as _whatwg_node_server from '@whatwg-node/server';
11
11
  import { OpenAI } from 'openai';
12
12
 
13
+ /**
14
+ * Type-only reference to ObservabilityConfig for documentation purposes.
15
+ * The actual type is: ObservabilityConfig | ObservabilityConfig[]
16
+ *
17
+ * At runtime, this accepts any value matching the ObservabilityConfig interface
18
+ * from @cloudbase/agent-observability/server package (if installed).
19
+ */
20
+ type ObservabilityConfigOrArray = any;
13
21
  /**
14
22
  * Context passed to the agent factory function.
15
23
  * Contains request information for per-request agent configuration.
@@ -53,16 +61,49 @@ interface ICreateServer {
53
61
  * createExpressServer({ createAgent, logger: pino({ level: 'info' }) });
54
62
  */
55
63
  logger?: Logger;
64
+ /**
65
+ * Observability configuration for trace exporters.
66
+ *
67
+ * Requires @cloudbase/agent-observability package to be installed.
68
+ * If the package is not installed, this option is silently ignored.
69
+ *
70
+ * Type reference: ObservabilityConfig | ObservabilityConfig[] from @cloudbase/agent-observability/server
71
+ *
72
+ * @example
73
+ * // Console exporter (from env AUTO_TRACES_STDOUT)
74
+ * createExpressServer({ createAgent, observability: { type: 'console' } });
75
+ *
76
+ * // OTLP exporter (Langfuse, Jaeger, etc.)
77
+ * createExpressServer({
78
+ * createAgent,
79
+ * observability: {
80
+ * type: 'otlp',
81
+ * url: 'https://cloud.langfuse.com/api/public/otlp/v1/traces',
82
+ * headers: { 'Authorization': 'Basic xxx' }
83
+ * }
84
+ * });
85
+ *
86
+ * // Multiple exporters
87
+ * createExpressServer({
88
+ * createAgent,
89
+ * observability: [
90
+ * { type: 'console' },
91
+ * { type: 'otlp', url: 'http://localhost:4318/v1/traces' }
92
+ * ]
93
+ * });
94
+ */
95
+ observability?: ObservabilityConfigOrArray;
56
96
  }
57
97
  interface IRun extends ICreateServer {
58
98
  port?: number | string;
59
99
  }
60
100
  interface ICreateExpressRoutes extends Omit<ICreateServer, "cors"> {
61
101
  express: Express;
102
+ observability?: ICreateServer["observability"];
62
103
  }
63
104
  declare function run(props: IRun): void;
64
105
  declare function createExpressServer(props: ICreateServer): Express;
65
- declare function createExpressRoutes({ createAgent, basePath: _basePath, express, useAGUI: _useAGUI, aguiOptions, logger: _logger, }: ICreateExpressRoutes): expressLib.Express;
106
+ declare function createExpressRoutes({ createAgent, basePath: _basePath, express, useAGUI: _useAGUI, aguiOptions, logger: _logger, observability, }: ICreateExpressRoutes): expressLib.Express;
66
107
  interface AGUIOptions {
67
108
  runtimeOptions?: Partial<CopilotRuntimeOptions>;
68
109
  endpointOptions?: Partial<CreateCopilotRuntimeServerOptions>;
package/dist/index.js CHANGED
@@ -389,12 +389,52 @@ var ErrorCode = {
389
389
  };
390
390
 
391
391
  // src/agui/sendMessageAGUI/server.ts
392
+ var startObservation;
393
+ var setupObservability;
394
+ var processTraceContextFromHeaders;
395
+ var observabilityLoadingPromise = null;
396
+ async function loadObservability() {
397
+ if (startObservation) {
398
+ return true;
399
+ }
400
+ if (observabilityLoadingPromise) {
401
+ return observabilityLoadingPromise;
402
+ }
403
+ observabilityLoadingPromise = (async () => {
404
+ try {
405
+ const obs = await import("@cloudbase/agent-observability");
406
+ const obsServer = await import("@cloudbase/agent-observability/server");
407
+ startObservation = obs.startObservation;
408
+ processTraceContextFromHeaders = obs.processTraceContextFromHeaders;
409
+ setupObservability = obsServer.setupObservability;
410
+ return true;
411
+ } catch (e) {
412
+ return false;
413
+ }
414
+ })();
415
+ return observabilityLoadingPromise;
416
+ }
417
+ async function ensureObservabilityReady() {
418
+ if (!setupObservability) return false;
419
+ try {
420
+ const timeoutPromise = new Promise((_, reject) => {
421
+ setTimeout(() => reject(new Error("Observability setup timeout")), 2e3);
422
+ });
423
+ await Promise.race([
424
+ setupObservability(),
425
+ timeoutPromise
426
+ ]);
427
+ return true;
428
+ } catch (e) {
429
+ return false;
430
+ }
431
+ }
392
432
  function createServerAdapter2(createAgent, options) {
393
433
  var _a;
394
434
  const { logger: parentLogger = import_agent_shared2.noopLogger } = options ?? {};
395
435
  const adapterLogger = ((_a = parentLogger.child) == null ? void 0 : _a.call(parentLogger, { component: "sendMessageAGUI" })) ?? parentLogger;
396
436
  return (0, import_server3.createServerAdapter)(async (request) => {
397
- var _a2, _b, _c, _d, _e, _f, _g, _h;
437
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
398
438
  const requestId = getOrGenerateRequestId(request.headers, "agui");
399
439
  const logger = ((_a2 = adapterLogger.child) == null ? void 0 : _a2.call(adapterLogger, { requestId })) ?? adapterLogger;
400
440
  (_b = logger.info) == null ? void 0 : _b.call(logger, "Request received");
@@ -474,11 +514,71 @@ function createServerAdapter2(createAgent, options) {
474
514
  }
475
515
  );
476
516
  }
477
- const events = handler2(
478
- inputRes.result,
479
- createAgentRes.result.agent
480
- );
481
- let heartbeat;
517
+ const hasObservability = await loadObservability();
518
+ let serverSpan = null;
519
+ let serverContextData = null;
520
+ if (hasObservability && startObservation) {
521
+ try {
522
+ const isReady = await ensureObservabilityReady();
523
+ if (isReady) {
524
+ const spanAttributes = {
525
+ "http.method": request.method,
526
+ "http.url": request.url,
527
+ "http.host": request.headers.get("host") || "unknown",
528
+ "http.user_agent": request.headers.get("user-agent") || "unknown",
529
+ "agui.thread_id": inputRes.result.threadId,
530
+ "agui.run_id": inputRes.result.runId,
531
+ "agui.request_id": requestId
532
+ };
533
+ if (processTraceContextFromHeaders) {
534
+ try {
535
+ const traceContext = processTraceContextFromHeaders(
536
+ request.headers,
537
+ logger
538
+ );
539
+ if (traceContext.isInherited) {
540
+ Object.assign(spanAttributes, traceContext.spanAttributes);
541
+ }
542
+ serverSpan = startObservation(
543
+ "AG-UI.Server",
544
+ spanAttributes,
545
+ {
546
+ asType: "chain",
547
+ links: traceContext.links.length > 0 ? traceContext.links : void 0
548
+ }
549
+ );
550
+ } catch (e) {
551
+ (_i = logger.debug) == null ? void 0 : _i.call(logger, "Failed to process external trace context:", e);
552
+ serverSpan = startObservation(
553
+ "AG-UI.Server",
554
+ spanAttributes,
555
+ { asType: "chain" }
556
+ );
557
+ }
558
+ } else {
559
+ serverSpan = startObservation(
560
+ "AG-UI.Server",
561
+ spanAttributes,
562
+ { asType: "chain" }
563
+ );
564
+ }
565
+ const spanContext = (_k = (_j = serverSpan == null ? void 0 : serverSpan.otelSpan) == null ? void 0 : _j.spanContext) == null ? void 0 : _k.call(_j);
566
+ if ((spanContext == null ? void 0 : spanContext.traceId) && (spanContext == null ? void 0 : spanContext.spanId)) {
567
+ serverContextData = {
568
+ traceId: spanContext.traceId,
569
+ spanId: spanContext.spanId,
570
+ traceFlags: spanContext.traceFlags
571
+ };
572
+ inputRes.result.forwardedProps = {
573
+ ...inputRes.result.forwardedProps,
574
+ __agui_server_context: serverContextData
575
+ };
576
+ }
577
+ }
578
+ } catch (e) {
579
+ (_l = logger.debug) == null ? void 0 : _l.call(logger, "Failed to create server span:", e);
580
+ }
581
+ }
482
582
  let cleanupCalled = false;
483
583
  const safeCleanup = () => {
484
584
  var _a3, _b2, _c2;
@@ -491,9 +591,50 @@ function createServerAdapter2(createAgent, options) {
491
591
  }
492
592
  }
493
593
  };
594
+ const eventsResult = safe(
595
+ () => handler2(
596
+ inputRes.result,
597
+ createAgentRes.result.agent
598
+ )
599
+ );
600
+ if ("error" in eventsResult) {
601
+ const { error } = eventsResult;
602
+ (_m = logger.error) == null ? void 0 : _m.call(logger, { err: error }, "Run agent failed");
603
+ if (serverSpan) {
604
+ try {
605
+ serverSpan.otelSpan.setStatus({
606
+ code: 2,
607
+ // SpanStatusCode.ERROR
608
+ message: error instanceof Error ? error.message : String(error)
609
+ });
610
+ serverSpan.otelSpan.setAttribute("error.type", error instanceof Error ? error.name : "UnknownError");
611
+ serverSpan.otelSpan.setAttribute("error.message", error instanceof Error ? error.message : String(error));
612
+ serverSpan.end();
613
+ } catch (e) {
614
+ (_n = logger.debug) == null ? void 0 : _n.call(logger, "Failed to update server span with error status:", e);
615
+ }
616
+ }
617
+ const errorCode = (0, import_agent_shared3.isErrorWithCode)(error) ? error.code : ErrorCode.INTERNAL_ERROR;
618
+ const errorMessage = error instanceof Error ? error.message : String(error);
619
+ return new Response(
620
+ JSON.stringify({
621
+ error: {
622
+ code: errorCode,
623
+ message: errorMessage
624
+ },
625
+ requestId
626
+ }),
627
+ {
628
+ status: 500,
629
+ headers: { "Content-Type": "application/json" }
630
+ }
631
+ );
632
+ }
633
+ const { result: events } = eventsResult;
634
+ let heartbeat;
494
635
  const stream = new ReadableStream({
495
636
  async start(controller) {
496
- var _a3, _b2, _c2, _d2;
637
+ var _a3, _b2, _c2, _d2, _e2;
497
638
  const encoder = new TextEncoder();
498
639
  heartbeat = setInterval(() => {
499
640
  controller.enqueue(encoder.encode(":ping\n\n"));
@@ -532,13 +673,21 @@ function createServerAdapter2(createAgent, options) {
532
673
  if (heartbeat) clearInterval(heartbeat);
533
674
  controller.close();
534
675
  safeCleanup();
676
+ if (serverSpan) {
677
+ serverSpan.end();
678
+ (_e2 = logger.debug) == null ? void 0 : _e2.call(logger, "\u2713 Server span ended");
679
+ }
535
680
  }
536
681
  },
537
682
  cancel() {
538
- var _a3;
683
+ var _a3, _b2;
539
684
  (_a3 = logger.info) == null ? void 0 : _a3.call(logger, "Request cancelled by client");
540
685
  if (heartbeat) clearInterval(heartbeat);
541
686
  safeCleanup();
687
+ if (serverSpan) {
688
+ serverSpan.end();
689
+ (_b2 = logger.debug) == null ? void 0 : _b2.call(logger, "\u2713 Server span ended (cancelled)");
690
+ }
542
691
  }
543
692
  });
544
693
  const headers = new Headers({
@@ -558,6 +707,15 @@ async function safeAsync(fn) {
558
707
  return { error };
559
708
  }
560
709
  }
710
+ function safe(fn) {
711
+ try {
712
+ return {
713
+ result: fn()
714
+ };
715
+ } catch (error) {
716
+ return { error };
717
+ }
718
+ }
561
719
 
562
720
  // src/agui/healthz/index.ts
563
721
  var healthz_exports = {};
@@ -756,6 +914,25 @@ var import_cors = __toESM(require("cors"));
756
914
  var import_async_hooks = require("async_hooks");
757
915
  var import_server8 = require("@whatwg-node/server");
758
916
  var DefaultFetchAPI = __toESM(require("@whatwg-node/fetch"));
917
+ var observabilitySetupPromise = null;
918
+ var observabilitySetupComplete = false;
919
+ async function setupObservabilityIfAvailable(configs) {
920
+ if (observabilitySetupComplete) {
921
+ return;
922
+ }
923
+ if (observabilitySetupPromise) {
924
+ return observabilitySetupPromise;
925
+ }
926
+ observabilitySetupPromise = (async () => {
927
+ try {
928
+ const { setupObservability: setupObservability2 } = await import("@cloudbase/agent-observability/server");
929
+ await setupObservability2(configs);
930
+ observabilitySetupComplete = true;
931
+ } catch (error) {
932
+ }
933
+ })();
934
+ return observabilitySetupPromise;
935
+ }
759
936
  var preparedAgentStorage = new import_async_hooks.AsyncLocalStorage();
760
937
  function agentCloneFn() {
761
938
  const preparedAgent = preparedAgentStorage.getStore();
@@ -790,33 +967,77 @@ function createExpressRoutes({
790
967
  express,
791
968
  useAGUI: _useAGUI,
792
969
  aguiOptions,
793
- logger: _logger
970
+ logger: _logger,
971
+ observability
794
972
  }) {
795
- var _a, _b, _c;
973
+ var _a, _b, _c, _d;
796
974
  const useAGUI = _useAGUI ?? true;
797
975
  const logger = _logger ?? import_agent_shared2.noopLogger;
798
- const basePath = _basePath ?? (process.env.TENCENTCLOUD_RUNENV === "SCF" ? "/v1/aibot/bots/:agentId/" : "/");
976
+ const userProvidedBasePath = _basePath !== void 0;
799
977
  const serverLogger = ((_a = logger.child) == null ? void 0 : _a.call(logger, { component: "server" })) ?? logger;
800
- (_b = serverLogger.debug) == null ? void 0 : _b.call(serverLogger, { basePath, useAGUI }, "Initializing server routes");
801
- const sendMessageServerAdapter = useAGUI ? sendMessageAGUI_exports.createServerAdapter(createAgent, { logger: serverLogger }) : sendMessage_exports.createServerAdapter(createAgent);
802
- if (useAGUI) {
803
- createAGUIRoute({
804
- basePath,
805
- express,
806
- createAgent,
807
- logger: serverLogger,
808
- ...aguiOptions || {}
978
+ (_b = serverLogger.debug) == null ? void 0 : _b.call(
979
+ serverLogger,
980
+ { basePath: _basePath, useAGUI, userProvidedBasePath },
981
+ "Initializing server routes"
982
+ );
983
+ if (observability) {
984
+ setupObservabilityIfAvailable(observability).catch(() => {
809
985
  });
810
986
  }
987
+ const sendMessageServerAdapter = useAGUI ? sendMessageAGUI_exports.createServerAdapter(createAgent, {
988
+ logger: serverLogger
989
+ }) : sendMessage_exports.createServerAdapter(createAgent);
811
990
  const openaiServerAdapter = openai_exports.createServerAdapter(createAgent);
812
991
  const healthzServerAdapter = healthz_exports.serverAdapter;
813
- express.use(`${basePath}send-message`, sendMessageServerAdapter);
814
- express.use(`${basePath}healthz`, healthzServerAdapter);
815
- express.use(`${basePath}chat/completions`, openaiServerAdapter);
816
- (_c = serverLogger.info) == null ? void 0 : _c.call(serverLogger, { basePath }, "Server routes initialized");
992
+ if (userProvidedBasePath) {
993
+ const basePath = _basePath;
994
+ if (useAGUI) {
995
+ createAGUIRoute({
996
+ basePath,
997
+ express,
998
+ createAgent,
999
+ logger: serverLogger,
1000
+ ...aguiOptions || {}
1001
+ });
1002
+ }
1003
+ express.use(`${basePath}send-message`, sendMessageServerAdapter);
1004
+ express.use(`${basePath}healthz`, healthzServerAdapter);
1005
+ express.use(`${basePath}chat/completions`, openaiServerAdapter);
1006
+ (_c = serverLogger.info) == null ? void 0 : _c.call(serverLogger, { basePath }, "Server routes initialized");
1007
+ } else {
1008
+ const simplePath = "/";
1009
+ const scfPath = "/v1/aibot/bots/:agentId/";
1010
+ if (useAGUI) {
1011
+ createAGUIRoute({
1012
+ basePath: simplePath,
1013
+ express,
1014
+ createAgent,
1015
+ logger: serverLogger,
1016
+ ...aguiOptions || {}
1017
+ });
1018
+ createAGUIRoute({
1019
+ basePath: scfPath,
1020
+ express,
1021
+ createAgent,
1022
+ logger: serverLogger,
1023
+ ...aguiOptions || {}
1024
+ });
1025
+ }
1026
+ express.use(`${simplePath}send-message`, sendMessageServerAdapter);
1027
+ express.use(`${simplePath}healthz`, healthzServerAdapter);
1028
+ express.use(`${simplePath}chat/completions`, openaiServerAdapter);
1029
+ express.use(`${scfPath}send-message`, sendMessageServerAdapter);
1030
+ express.use(`${scfPath}healthz`, healthzServerAdapter);
1031
+ express.use(`${scfPath}chat/completions`, openaiServerAdapter);
1032
+ (_d = serverLogger.info) == null ? void 0 : _d.call(
1033
+ serverLogger,
1034
+ { simplePath, scfPath },
1035
+ "Server routes initialized (dual endpoints)"
1036
+ );
1037
+ }
817
1038
  return express;
818
1039
  }
819
- var AGUIRpcHandlerPromise = null;
1040
+ var AGUIRpcHandlerPromiseMap = /* @__PURE__ */ new Map();
820
1041
  function getAGUIRpcHandler({
821
1042
  createAgent,
822
1043
  runtimeOptions,
@@ -826,8 +1047,9 @@ function getAGUIRpcHandler({
826
1047
  logger,
827
1048
  requestId
828
1049
  }) {
829
- if (AGUIRpcHandlerPromise) return AGUIRpcHandlerPromise;
830
- AGUIRpcHandlerPromise = (async () => {
1050
+ const cached = AGUIRpcHandlerPromiseMap.get(basePath);
1051
+ if (cached) return cached;
1052
+ const handlerPromise = (async () => {
831
1053
  const { agent } = await createAgent({ request, logger, requestId });
832
1054
  const templateAgent = "toAGUIAgent" in agent ? agent.toAGUIAgent() : agent;
833
1055
  templateAgent.clone = agentCloneFn;
@@ -844,7 +1066,8 @@ function getAGUIRpcHandler({
844
1066
  ...endpointOptions || {}
845
1067
  });
846
1068
  })();
847
- return AGUIRpcHandlerPromise;
1069
+ AGUIRpcHandlerPromiseMap.set(basePath, handlerPromise);
1070
+ return handlerPromise;
848
1071
  }
849
1072
  function createAGUIRoute({
850
1073
  express,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/agent-server",
3
- "version": "0.0.15",
3
+ "version": "0.0.18",
4
4
  "main": "dist/index.js",
5
5
  "files": [
6
6
  "dist/",
@@ -20,10 +20,16 @@
20
20
  "express": "^5.1.0",
21
21
  "openai": "6.3.0",
22
22
  "uuid": "^10.0.0",
23
- "@cloudbase/agent-shared": "^0.0.15"
23
+ "@cloudbase/agent-shared": "^0.0.18"
24
24
  },
25
25
  "peerDependencies": {
26
- "zod": "^3.25.0 || ^4.0.0"
26
+ "zod": "^3.25.0 || ^4.0.0",
27
+ "@cloudbase/agent-observability": "0.0.18"
28
+ },
29
+ "peerDependenciesMeta": {
30
+ "@cloudbase/agent-observability": {
31
+ "optional": true
32
+ }
27
33
  },
28
34
  "devDependencies": {
29
35
  "@types/cors": "^2.8.19",