cascade-ai 0.12.6 → 0.12.8

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.cts CHANGED
@@ -1084,8 +1084,15 @@ declare class CascadeRouter extends EventEmitter {
1084
1084
  * Useful for listing configured/usable models per provider.
1085
1085
  */
1086
1086
  getModelsForProvider(provider: ProviderType): ModelInfo[];
1087
+ /**
1088
+ * Every model available across the configured + reachable providers, after
1089
+ * discovery (Ollama tags, OpenAI-compatible/llama.cpp models, cloud catalog).
1090
+ * Used to populate the desktop model pickers with the user's real models.
1091
+ */
1092
+ getAvailableModels(): ModelInfo[];
1087
1093
  private detectAvailableProviders;
1088
1094
  private discoverOllamaModels;
1095
+ private discoverOpenAICompatibleModels;
1089
1096
  private ensureProvider;
1090
1097
  private getProvider;
1091
1098
  private createProvider;
package/dist/index.d.ts CHANGED
@@ -1084,8 +1084,15 @@ declare class CascadeRouter extends EventEmitter {
1084
1084
  * Useful for listing configured/usable models per provider.
1085
1085
  */
1086
1086
  getModelsForProvider(provider: ProviderType): ModelInfo[];
1087
+ /**
1088
+ * Every model available across the configured + reachable providers, after
1089
+ * discovery (Ollama tags, OpenAI-compatible/llama.cpp models, cloud catalog).
1090
+ * Used to populate the desktop model pickers with the user's real models.
1091
+ */
1092
+ getAvailableModels(): ModelInfo[];
1087
1093
  private detectAvailableProviders;
1088
1094
  private discoverOllamaModels;
1095
+ private discoverOpenAICompatibleModels;
1089
1096
  private ensureProvider;
1090
1097
  private getProvider;
1091
1098
  private createProvider;
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ import cron from 'node-cron';
35
35
 
36
36
 
37
37
  // src/constants.ts
38
- var CASCADE_VERSION = "0.12.6";
38
+ var CASCADE_VERSION = "0.12.8";
39
39
  var CASCADE_CONFIG_DIR = ".cascade";
40
40
  var CASCADE_MD_FILE = "CASCADE.md";
41
41
  var CASCADE_IGNORE_FILE = ".cascadeignore";
@@ -1474,6 +1474,7 @@ var ModelSelector = class {
1474
1474
  if (lower.includes("claude")) providerStr = "anthropic";
1475
1475
  else if (lower.startsWith("gpt") || lower.startsWith("o1") || lower.startsWith("o3")) providerStr = "openai";
1476
1476
  else if (lower.includes("gemini")) providerStr = "gemini";
1477
+ else if ((lower.endsWith(".gguf") || actualId.includes("/") || actualId.includes("\\")) && this.availableProviders.has("openai-compatible")) providerStr = "openai-compatible";
1477
1478
  else if (this.availableProviders.has("ollama")) providerStr = "ollama";
1478
1479
  else if (this.availableProviders.has("openai-compatible")) providerStr = "openai-compatible";
1479
1480
  else if (this.availableProviders.size === 1) providerStr = Array.from(this.availableProviders)[0];
@@ -2161,6 +2162,11 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
2161
2162
  if (availableProviders.has("ollama")) {
2162
2163
  await this.discoverOllamaModels(ollamaCfg);
2163
2164
  }
2165
+ if (availableProviders.has("openai-compatible")) {
2166
+ await Promise.all(
2167
+ config.providers.filter((p) => p.type === "openai-compatible").map((cfg) => this.discoverOpenAICompatibleModels(cfg))
2168
+ );
2169
+ }
2164
2170
  for (const tier of ["T1", "T2", "T3"]) {
2165
2171
  const override = tier === "T1" ? config.models.t1 : tier === "T2" ? config.models.t2 : config.models.t3;
2166
2172
  if (!override || override === "auto") continue;
@@ -2592,6 +2598,14 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
2592
2598
  getModelsForProvider(provider) {
2593
2599
  return this.selector.getAvailableModelsForProvider(provider);
2594
2600
  }
2601
+ /**
2602
+ * Every model available across the configured + reachable providers, after
2603
+ * discovery (Ollama tags, OpenAI-compatible/llama.cpp models, cloud catalog).
2604
+ * Used to populate the desktop model pickers with the user's real models.
2605
+ */
2606
+ getAvailableModels() {
2607
+ return this.selector?.getAllAvailableModels() ?? [];
2608
+ }
2595
2609
  // ── Private ──────────────────────────────────
2596
2610
  async detectAvailableProviders(configs) {
2597
2611
  const available = /* @__PURE__ */ new Set();
@@ -2622,6 +2636,28 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
2622
2636
  } catch {
2623
2637
  }
2624
2638
  }
2639
+ async discoverOpenAICompatibleModels(cfg) {
2640
+ try {
2641
+ const seed = {
2642
+ id: "openai-compatible",
2643
+ name: "openai-compatible",
2644
+ provider: "openai-compatible",
2645
+ contextWindow: 32e3,
2646
+ isVisionCapable: false,
2647
+ inputCostPer1kTokens: 0,
2648
+ outputCostPer1kTokens: 0,
2649
+ maxOutputTokens: 4e3,
2650
+ supportsStreaming: true,
2651
+ isLocal: false
2652
+ };
2653
+ const provider = new OpenAICompatibleProvider(cfg, seed);
2654
+ const models = await provider.listModels();
2655
+ for (const m of models) {
2656
+ this.selector.addDynamicModel(m);
2657
+ }
2658
+ } catch {
2659
+ }
2660
+ }
2625
2661
  ensureProvider(model, configs) {
2626
2662
  const key = `${model.provider}:${model.id}`;
2627
2663
  if (this.providers.has(key)) return;
@@ -2651,7 +2687,23 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
2651
2687
  }
2652
2688
  }
2653
2689
  getAnyModelForProvider(type) {
2654
- return Object.values(MODELS).find((m) => m.provider === type);
2690
+ const fromCatalog = Object.values(MODELS).find((m) => m.provider === type);
2691
+ if (fromCatalog) return fromCatalog;
2692
+ if (type === "openai-compatible" || type === "azure") {
2693
+ return {
2694
+ id: type,
2695
+ name: type,
2696
+ provider: type,
2697
+ contextWindow: 32e3,
2698
+ isVisionCapable: false,
2699
+ inputCostPer1kTokens: 0,
2700
+ outputCostPer1kTokens: 0,
2701
+ maxOutputTokens: 4e3,
2702
+ supportsStreaming: true,
2703
+ isLocal: false
2704
+ };
2705
+ }
2706
+ return void 0;
2655
2707
  }
2656
2708
  recordStats(tier, model, usage) {
2657
2709
  this.stats.totalTokens += usage.totalTokens;
@@ -8792,7 +8844,11 @@ ${last.partialOutput}` : "");
8792
8844
  looksLikeConversational(prompt) {
8793
8845
  const LOW_COMPLEXITY = [
8794
8846
  /^(?:hi|hello|hey|thanks|thank you|ok|okay|yes|no|sure|got it|sounds good)\b/i,
8795
- /^(?:what is|what are|list|show me|tell me|who is|where is|when is|how do i)\b/i,
8847
+ /^(?:what is|what are|what'?s|list|show me|tell me|who is|who are|who'?re|where is|when is|how do i)\b/i,
8848
+ // Self-identity / capability questions ("who are you", "what can you do",
8849
+ // "who made you") are pure conversation — never a multi-agent build.
8850
+ /^(?:who|what)\b.*\byou\b/i,
8851
+ /^what can you\b/i,
8796
8852
  /\b(?:simple|quick|brief|small|single|one-line|typo|rename)\b/i
8797
8853
  ];
8798
8854
  const wordCount = prompt.trim().split(/\s+/).length;
@@ -8890,10 +8946,16 @@ ${prompt}` : prompt;
8890
8946
  temperature: 0
8891
8947
  });
8892
8948
  const content = result.content.trim();
8893
- const firstWord = (content.split(/[\s—–-]+/)[0] ?? "").toLowerCase();
8949
+ const match = content.toLowerCase().match(/\b(simple|moderate|complex)\b/);
8894
8950
  const reason = content.replace(/^\S+\s*[—–-]*\s*/, "").trim();
8895
- const verdict = firstWord.includes("simple") ? "Simple" : firstWord.includes("moderate") ? "Moderate" : "Complex";
8896
- this.recordDecision("complexity", `${verdict} \u2014 classifier: ${reason || "no reason given"}`);
8951
+ let verdict;
8952
+ if (match) {
8953
+ verdict = match[1] === "simple" ? "Simple" : match[1] === "moderate" ? "Moderate" : "Complex";
8954
+ this.recordDecision("complexity", `${verdict} \u2014 classifier: ${reason || "no reason given"}`);
8955
+ } else {
8956
+ verdict = prompt.trim().split(/\s+/).length <= 12 ? "Simple" : "Moderate";
8957
+ this.recordDecision("complexity", `${verdict} \u2014 classifier output unparseable; defaulted by length`);
8958
+ }
8897
8959
  return verdict;
8898
8960
  } catch {
8899
8961
  const followUpPrompt = /^(proceed|continue|go ahead|do it|yes|yep|ok|okay|carry on)$/i.test(prompt.trim());