cascade-ai 0.10.1 → 0.10.4

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.cjs CHANGED
@@ -8060,6 +8060,19 @@ var TaskAnalyzer = class {
8060
8060
  this.lastSelectedModels.clear();
8061
8061
  void this.tracker.save();
8062
8062
  }
8063
+ /**
8064
+ * Record an explicit user rating (good/bad) for the last run's selected models.
8065
+ * Explicit ratings carry 3× the weight of auto-detected outcomes.
8066
+ * Does NOT clear lastSelectedModels — the auto record already did that.
8067
+ */
8068
+ recordExplicitRating(rating) {
8069
+ if (!this.tracker || !this.lastProfile) return false;
8070
+ const taskType = this.lastProfile.type;
8071
+ for (const [, model] of this.lastSelectedModels) {
8072
+ this.tracker.recordExplicit(model.id, taskType, rating, 0);
8073
+ }
8074
+ return this.lastSelectedModels.size > 0;
8075
+ }
8063
8076
  scoreModel(model, profile) {
8064
8077
  const perf = this.tracker?.performanceScore(model.id, profile.type) ?? 0.5;
8065
8078
  const costEff = this.costEfficiency(model, profile.complexity);
@@ -8139,6 +8152,20 @@ var ModelPerformanceTracker = class {
8139
8152
  sampleCount: s.sampleCount + 1
8140
8153
  });
8141
8154
  }
8155
+ /**
8156
+ * Record an explicit user rating (good/bad). Counts as 3 automatic samples
8157
+ * so user feedback carries significantly more weight than auto-detected outcomes.
8158
+ */
8159
+ recordExplicit(modelId, taskType, rating, costUsd = 0) {
8160
+ const outcome = rating === "good" ? "success" : "failure";
8161
+ this.record(modelId, taskType, outcome, 0, costUsd);
8162
+ this.record(modelId, taskType, outcome, 0, 0);
8163
+ this.record(modelId, taskType, outcome, 0, 0);
8164
+ }
8165
+ /** Returns all stats keyed by "modelId:taskType" — used by `cascade stats`. */
8166
+ getAll() {
8167
+ return new Map(this.stats);
8168
+ }
8142
8169
  /**
8143
8170
  * Returns 0.05–1.0; defaults to 0.5 (neutral prior) when no history exists.
8144
8171
  * High retry counts penalise the score.
@@ -8743,6 +8770,18 @@ ${last.partialOutput}` : "");
8743
8770
  if (!prompt) return null;
8744
8771
  return this.run({ prompt });
8745
8772
  }
8773
+ /**
8774
+ * Record an explicit user rating for the last completed run.
8775
+ * Explicit ratings carry 3× the weight of auto-detected outcomes so user
8776
+ * feedback meaningfully shifts future routing decisions.
8777
+ * Returns false when called before any task has run in this session.
8778
+ */
8779
+ rateLastRun(rating) {
8780
+ if (!this.taskAnalyzer) return false;
8781
+ const recorded = this.taskAnalyzer.recordExplicitRating(rating);
8782
+ if (recorded) void this.perfTracker?.save();
8783
+ return recorded;
8784
+ }
8746
8785
  /**
8747
8786
  * Rough pre-execution cost estimate for a plan: ~3 T2 calls per section
8748
8787
  * plus ~4 T3 calls per subtask at typical token volumes. A ballpark for
@@ -10539,6 +10578,31 @@ var DashboardSocket = class {
10539
10578
  const { sessionId } = normalizeSessionSubscriptionPayload(payload);
10540
10579
  socket.leave(`session:${sessionId}`);
10541
10580
  });
10581
+ socket.on("session:rate", (payload) => {
10582
+ const sessionId = typeof payload?.sessionId === "string" ? payload.sessionId : "";
10583
+ const rating = payload?.rating === "good" || payload?.rating === "bad" ? payload.rating : null;
10584
+ if (sessionId && rating) {
10585
+ this.io.emit("session:rate", { sessionId, rating });
10586
+ }
10587
+ });
10588
+ });
10589
+ }
10590
+ onSessionRate(callback) {
10591
+ this.io.on("connection", (socket) => {
10592
+ socket.on("session:rate", (payload) => {
10593
+ const sessionId = typeof payload?.sessionId === "string" ? payload.sessionId : "";
10594
+ const rating = payload?.rating === "good" || payload?.rating === "bad" ? payload.rating : null;
10595
+ if (sessionId && rating) callback(sessionId, rating);
10596
+ });
10597
+ });
10598
+ }
10599
+ onConfigUpdate(callback) {
10600
+ this.io.on("connection", (socket) => {
10601
+ socket.on("config:update", (payload) => {
10602
+ if (typeof payload === "object" && payload !== null) {
10603
+ callback(payload);
10604
+ }
10605
+ });
10542
10606
  });
10543
10607
  }
10544
10608
  close() {
@@ -10555,6 +10619,7 @@ var DashboardServer = class {
10555
10619
  store;
10556
10620
  globalStore = null;
10557
10621
  broadcastTimer = null;
10622
+ activeSessions = /* @__PURE__ */ new Map();
10558
10623
  port;
10559
10624
  host;
10560
10625
  workspacePath;
@@ -10574,6 +10639,30 @@ var DashboardServer = class {
10574
10639
  });
10575
10640
  this.setupMiddleware();
10576
10641
  this.setupRoutes();
10642
+ this.socket.onSessionRate((sessionId, rating) => {
10643
+ this.activeSessions.get(sessionId)?.rateLastRun(rating);
10644
+ });
10645
+ this.socket.onConfigUpdate((data) => {
10646
+ if (data.keys) {
10647
+ for (const [type, apiKey] of Object.entries(data.keys)) {
10648
+ if (!apiKey) continue;
10649
+ const provider = this.config.providers.find((p) => p.type === type);
10650
+ if (provider) provider.apiKey = apiKey;
10651
+ else this.config.providers.push({ type, apiKey });
10652
+ }
10653
+ }
10654
+ if (data.models) {
10655
+ this.config.models = { ...this.config.models, ...data.models };
10656
+ }
10657
+ if (data.budget) {
10658
+ if (typeof data.budget.maxCostPerRun === "number") {
10659
+ this.config.budget.maxCostPerRunUsd = data.budget.maxCostPerRun;
10660
+ }
10661
+ if (data.budget.autoBias === "balanced" || data.budget.autoBias === "quality" || data.budget.autoBias === "cost") {
10662
+ this.config.autoBias = data.budget.autoBias;
10663
+ }
10664
+ }
10665
+ });
10577
10666
  }
10578
10667
  async start() {
10579
10668
  const isLoopback = this.host === "127.0.0.1" || this.host === "::1" || this.host === "localhost";
@@ -11027,6 +11116,7 @@ var DashboardServer = class {
11027
11116
  res.json({ sessionId, status: "ACTIVE" });
11028
11117
  void (async () => {
11029
11118
  const cascade = new Cascade(this.config, this.workspacePath, this.store);
11119
+ this.activeSessions.set(sessionId, cascade);
11030
11120
  cascade.on("stream:token", (e) => {
11031
11121
  this.socket.broadcast("stream:token", { sessionId, tierId: e.tierId, text: e.text });
11032
11122
  this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, tierId: e.tierId, text: e.text });
@@ -11055,6 +11145,8 @@ var DashboardServer = class {
11055
11145
  sessionId,
11056
11146
  error: err instanceof Error ? err.message : String(err)
11057
11147
  });
11148
+ } finally {
11149
+ this.activeSessions.delete(sessionId);
11058
11150
  }
11059
11151
  })();
11060
11152
  });