@axlsdk/studio 0.13.5 → 0.13.7

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.
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/server/index.ts
@@ -58,11 +68,18 @@ async function errorHandler(c, next) {
58
68
  }
59
69
 
60
70
  // src/server/ws/connection-manager.ts
71
+ function isBufferedChannel(channel) {
72
+ return channel.startsWith("execution:");
73
+ }
74
+ var BUFFER_TTL_MS = 3e4;
75
+ var MAX_BUFFER_EVENTS = 500;
61
76
  var ConnectionManager = class {
62
77
  /** channel -> set of WS connections */
63
78
  channels = /* @__PURE__ */ new Map();
64
79
  /** ws -> set of subscribed channels (for cleanup) */
65
80
  connections = /* @__PURE__ */ new Map();
81
+ /** channel -> replay buffer for execution streams */
82
+ buffers = /* @__PURE__ */ new Map();
66
83
  maxConnections = 100;
67
84
  /** Register a new WS connection. */
68
85
  add(ws) {
@@ -85,7 +102,7 @@ var ConnectionManager = class {
85
102
  }
86
103
  this.connections.delete(ws);
87
104
  }
88
- /** Subscribe a connection to a channel. No-op if the connection was not added. */
105
+ /** Subscribe a connection to a channel. Replays buffered events for execution channels. */
89
106
  subscribe(ws, channel) {
90
107
  if (!this.connections.has(ws)) return;
91
108
  let subs = this.channels.get(channel);
@@ -95,6 +112,17 @@ var ConnectionManager = class {
95
112
  }
96
113
  subs.add(ws);
97
114
  this.connections.get(ws).add(channel);
115
+ const buffer = this.buffers.get(channel);
116
+ if (buffer) {
117
+ for (const msg of buffer.events) {
118
+ try {
119
+ ws.send(msg);
120
+ } catch {
121
+ this.remove(ws);
122
+ return;
123
+ }
124
+ }
125
+ }
98
126
  }
99
127
  /** Unsubscribe a connection from a channel. */
100
128
  unsubscribe(ws, channel) {
@@ -104,11 +132,30 @@ var ConnectionManager = class {
104
132
  }
105
133
  this.connections.get(ws)?.delete(channel);
106
134
  }
107
- /** Broadcast data to all subscribers of a channel. */
135
+ /** Broadcast data to all subscribers of a channel. Buffers events for execution channels. */
108
136
  broadcast(channel, data) {
137
+ const msg = JSON.stringify({ type: "event", channel, data });
138
+ if (isBufferedChannel(channel)) {
139
+ let buffer = this.buffers.get(channel);
140
+ if (!buffer) {
141
+ buffer = { events: [], complete: false };
142
+ this.buffers.set(channel, buffer);
143
+ }
144
+ const event = data;
145
+ const isTerminal = event.type === "done" || event.type === "error";
146
+ if (buffer.events.length < MAX_BUFFER_EVENTS || isTerminal) {
147
+ buffer.events.push(msg);
148
+ }
149
+ if (isTerminal) {
150
+ buffer.complete = true;
151
+ if (buffer.timer) clearTimeout(buffer.timer);
152
+ buffer.timer = setTimeout(() => {
153
+ this.buffers.delete(channel);
154
+ }, BUFFER_TTL_MS);
155
+ }
156
+ }
109
157
  const subs = this.channels.get(channel);
110
158
  if (!subs || subs.size === 0) return;
111
- const msg = JSON.stringify({ type: "event", channel, data });
112
159
  for (const ws of [...subs]) {
113
160
  try {
114
161
  ws.send(msg);
@@ -135,13 +182,17 @@ var ConnectionManager = class {
135
182
  }
136
183
  }
137
184
  }
138
- /** Close all connections and clear all state. Used during shutdown. */
185
+ /** Close all connections, clear all state and buffers. Used during shutdown. */
139
186
  closeAll() {
140
187
  for (const ws of this.connections.keys()) {
141
188
  ws.close?.();
142
189
  }
190
+ for (const buffer of this.buffers.values()) {
191
+ if (buffer.timer) clearTimeout(buffer.timer);
192
+ }
143
193
  this.connections.clear();
144
194
  this.channels.clear();
195
+ this.buffers.clear();
145
196
  }
146
197
  /** Get the number of active connections. */
147
198
  get connectionCount() {
@@ -346,15 +397,8 @@ function createWorkflowRoutes(connMgr) {
346
397
  const stream = runtime.stream(name, body.input ?? {}, { metadata: body.metadata });
347
398
  const executionId = `stream-${Date.now()}`;
348
399
  (async () => {
349
- try {
350
- for await (const event of stream) {
351
- connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
352
- }
353
- } catch (err) {
354
- connMgr.broadcastWithWildcard(`execution:${executionId}`, {
355
- type: "error",
356
- message: err instanceof Error ? err.message : "Stream error"
357
- });
400
+ for await (const event of stream) {
401
+ connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
358
402
  }
359
403
  })();
360
404
  return c.json({ ok: true, data: { executionId, streaming: true } });
@@ -435,15 +479,8 @@ function createSessionRoutes(connMgr) {
435
479
  const stream = await session.stream(body.workflow, body.message);
436
480
  const executionId = `session-${id}-${Date.now()}`;
437
481
  (async () => {
438
- try {
439
- for await (const event of stream) {
440
- connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
441
- }
442
- } catch (err) {
443
- connMgr.broadcastWithWildcard(`execution:${executionId}`, {
444
- type: "error",
445
- message: err instanceof Error ? err.message : "Stream error"
446
- });
482
+ for await (const event of stream) {
483
+ connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
447
484
  }
448
485
  })();
449
486
  return c.json({ ok: true, data: { executionId, streaming: true } });
@@ -725,8 +762,80 @@ function createEvalRoutes(evalLoader) {
725
762
  404
726
763
  );
727
764
  }
765
+ let runs = 1;
766
+ try {
767
+ const body = await c.req.json().catch(() => ({}));
768
+ if (typeof body.runs === "number" && Number.isFinite(body.runs) && body.runs > 1) {
769
+ runs = Math.min(Math.floor(body.runs), 25);
770
+ }
771
+ } catch {
772
+ }
728
773
  try {
729
- const result = await runtime.runRegisteredEval(name);
774
+ if (runs > 1) {
775
+ const { randomUUID } = await import("crypto");
776
+ const { aggregateRuns } = await import("@axlsdk/eval");
777
+ const runGroupId = randomUUID();
778
+ const results = [];
779
+ for (let r = 0; r < runs; r++) {
780
+ const result2 = await runtime.runRegisteredEval(name, {
781
+ metadata: { runGroupId, runIndex: r }
782
+ });
783
+ results.push(result2);
784
+ }
785
+ const typedResults = results;
786
+ const aggregate = aggregateRuns(typedResults);
787
+ const first = typedResults[0];
788
+ const result = { ...first, _multiRun: { aggregate, allRuns: typedResults } };
789
+ return c.json({ ok: true, data: result });
790
+ } else {
791
+ const result = await runtime.runRegisteredEval(name);
792
+ return c.json({ ok: true, data: result });
793
+ }
794
+ } catch (err) {
795
+ const message = err instanceof Error ? err.message : String(err);
796
+ return c.json({ ok: false, error: { code: "EVAL_ERROR", message } }, 400);
797
+ }
798
+ });
799
+ app7.post("/evals/:name/rescore", async (c) => {
800
+ if (evalLoader) await evalLoader();
801
+ const runtime = c.get("runtime");
802
+ const name = c.req.param("name");
803
+ const body = await c.req.json();
804
+ if (!body.resultId || typeof body.resultId !== "string") {
805
+ return c.json(
806
+ { ok: false, error: { code: "BAD_REQUEST", message: "resultId is required" } },
807
+ 400
808
+ );
809
+ }
810
+ const entry = runtime.getRegisteredEval(name);
811
+ if (!entry) {
812
+ return c.json(
813
+ { ok: false, error: { code: "NOT_FOUND", message: `Eval "${name}" not found` } },
814
+ 404
815
+ );
816
+ }
817
+ const history = await runtime.getEvalHistory();
818
+ const historyEntry = history.find((h) => h.id === body.resultId);
819
+ if (!historyEntry) {
820
+ return c.json(
821
+ { ok: false, error: { code: "NOT_FOUND", message: `Result "${body.resultId}" not found` } },
822
+ 404
823
+ );
824
+ }
825
+ try {
826
+ const { rescore } = await import("@axlsdk/eval");
827
+ const config = entry.config;
828
+ const result = await rescore(
829
+ historyEntry.data,
830
+ config.scorers,
831
+ runtime
832
+ );
833
+ await runtime.saveEvalResult({
834
+ id: result.id,
835
+ eval: name,
836
+ timestamp: Date.now(),
837
+ data: result
838
+ });
730
839
  return c.json({ ok: true, data: result });
731
840
  } catch (err) {
732
841
  const message = err instanceof Error ? err.message : String(err);
@@ -737,7 +846,7 @@ function createEvalRoutes(evalLoader) {
737
846
  const runtime = c.get("runtime");
738
847
  const body = await c.req.json();
739
848
  try {
740
- const result = await runtime.evalCompare(body.baseline, body.candidate);
849
+ const result = await runtime.evalCompare(body.baseline, body.candidate, body.options);
741
850
  return c.json({ ok: true, data: result });
742
851
  } catch (err) {
743
852
  const message = err instanceof Error ? err.message : String(err);
@@ -754,26 +863,57 @@ function createPlaygroundRoutes(connMgr) {
754
863
  app7.post("/playground/chat", async (c) => {
755
864
  const runtime = c.get("runtime");
756
865
  const body = await c.req.json();
757
- const workflowName = body.workflow ?? runtime.getWorkflowNames()[0];
758
- if (!workflowName) {
866
+ if (!body.message || typeof body.message !== "string" || !body.message.trim()) {
867
+ return c.json(
868
+ {
869
+ ok: false,
870
+ error: {
871
+ code: "INVALID_INPUT",
872
+ message: "message is required and must be a non-empty string"
873
+ }
874
+ },
875
+ 400
876
+ );
877
+ }
878
+ const agents = runtime.getAgents();
879
+ const agent = body.agent ? agents.find((a) => a._name === body.agent) : agents[0];
880
+ if (!agent) {
759
881
  return c.json(
760
- { ok: false, error: { code: "NO_WORKFLOW", message: "No workflows registered" } },
882
+ {
883
+ ok: false,
884
+ error: { code: "NO_AGENT", message: `Agent "${body.agent ?? ""}" not found` }
885
+ },
761
886
  400
762
887
  );
763
888
  }
764
889
  const sessionId = body.sessionId ?? `playground-${Date.now()}`;
765
- const session = runtime.session(sessionId);
766
- const stream = await session.stream(workflowName, body.message);
767
890
  const executionId = `playground-${sessionId}-${Date.now()}`;
891
+ const store = runtime.getStateStore();
892
+ const history = await store.getSession(sessionId);
893
+ history.push({ role: "user", content: body.message });
894
+ const ctx = runtime.createContext({
895
+ sessionHistory: history,
896
+ onToken: (token) => {
897
+ connMgr.broadcastWithWildcard(`execution:${executionId}`, {
898
+ type: "token",
899
+ data: token
900
+ });
901
+ }
902
+ });
768
903
  (async () => {
769
904
  try {
770
- for await (const event of stream) {
771
- connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
772
- }
905
+ const result = await ctx.ask(agent, body.message);
906
+ const resultText = typeof result === "string" ? result : JSON.stringify(result);
907
+ history.push({ role: "assistant", content: resultText });
908
+ await store.saveSession(sessionId, history);
909
+ connMgr.broadcastWithWildcard(`execution:${executionId}`, {
910
+ type: "done",
911
+ data: resultText
912
+ });
773
913
  } catch (err) {
774
914
  connMgr.broadcastWithWildcard(`execution:${executionId}`, {
775
915
  type: "error",
776
- message: err instanceof Error ? err.message : "Stream error"
916
+ message: err instanceof Error ? err.message : String(err)
777
917
  });
778
918
  }
779
919
  })();
@@ -885,19 +1025,24 @@ function createServer(options) {
885
1025
  app7.use("/*", async (c, next) => {
886
1026
  const reqPath = c.req.path;
887
1027
  const resolved = basePath && reqPath.startsWith(basePath) ? reqPath.slice(basePath.length) || "/" : reqPath;
888
- if (resolved === "/" || resolved === "/index.html") {
1028
+ if (resolved === "/" || resolved === "/index.html" || resolved === "/ws") {
889
1029
  return next();
890
1030
  }
891
1031
  return staticHandler(c, next);
892
1032
  });
893
1033
  if (spaHtml) {
894
- app7.get("*", (c) => c.html(spaHtml));
1034
+ app7.get("*", async (c, next) => {
1035
+ const resolved = basePath && c.req.path.startsWith(basePath) ? c.req.path.slice(basePath.length) || "/" : c.req.path;
1036
+ if (resolved === "/ws") return next();
1037
+ return c.html(spaHtml);
1038
+ });
895
1039
  }
896
1040
  }
897
1041
  return {
898
1042
  app: app7,
899
1043
  connMgr,
900
1044
  costAggregator,
1045
+ /** Create WS handlers. Call before registering static/SPA routes are reached. */
901
1046
  createWsHandlers: () => createWsHandlers(connMgr),
902
1047
  traceListener
903
1048
  };