@juspay/neurolink 12.41.7 → 12.41.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.
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { ModelResolver, formatSearchResults, formatRecommendation, formatComparison, } from "../../models/modelResolver.js";
7
7
  import { getAllModels, getModelsByProvider, getAvailableProviders, formatModelForDisplay, } from "../../models/modelRegistry.js";
8
+ import { getCatalogJsonEntries } from "../../providers/catalog/loader.js";
8
9
  import chalk from "chalk";
9
10
  import ora from "ora";
10
11
  import { logger } from "../../utils/logger.js";
@@ -334,9 +335,23 @@ export class ModelsCommandFactory {
334
335
  try {
335
336
  const { AIProviderFactory } = await import("../../core/factory.js");
336
337
  const provider = (await AIProviderFactory.createProvider(providerName));
337
- const liveIds = typeof provider.getAvailableModels === "function"
338
+ const rawLiveIds = typeof provider.getAvailableModels === "function"
338
339
  ? await provider.getAvailableModels()
339
340
  : [];
341
+ // Project against the JSON catalog's non-retired ids, mirroring
342
+ // getAllModels()'s listing filter: a catalog-backed provider's
343
+ // vendor `/models` endpoint often keeps listing an id long after
344
+ // it 404s on chat completions (see e.g. fireworks.json's
345
+ // `evidence.liveMatrix`), so an unfiltered live listing would
346
+ // print a retired id as a usable "live" result. Providers with no
347
+ // catalog entry (litellm, ollama, openai-compatible gateways,
348
+ // lm-studio, llamacpp) have nothing to project against, so their
349
+ // live discovery stays unrestricted — that is the whole point of
350
+ // this fallback for them.
351
+ const catalogEntry = getCatalogJsonEntries().find((entry) => entry.id === providerName);
352
+ const liveIds = catalogEntry
353
+ ? rawLiveIds.filter((id) => catalogEntry.models.catalog[id]?.status !== "retired")
354
+ : rawLiveIds;
340
355
  if (liveIds.length > 0) {
341
356
  if (spinner) {
342
357
  spinner.succeed(`Found ${liveIds.length} models (live from ${providerName})`);
@@ -23,7 +23,7 @@
23
23
  "enumMember": "KIMI_K2P6",
24
24
  "vision": false,
25
25
  "status": "retired",
26
- "description": "Retired 2026-09 — still on the Fireworks roster, but a real chat call returns 404 'not deployed' (since the 2026-09-26 nightly)"
26
+ "description": "Retired 2026-09 — still listed on the Fireworks /models roster, but the chat endpoint 404s 'Model not found, inaccessible, and/or not deployed' (since the 2026-09-26 nightly); superseded by kimi-k3, same Moonshot family"
27
27
  },
28
28
  "accounts/fireworks/models/gpt-oss-120b": {
29
29
  "enumMember": "GPT_OSS_120B",
@@ -107,7 +107,8 @@
107
107
  "topModels": [
108
108
  "accounts/fireworks/models/kimi-k3",
109
109
  "accounts/fireworks/models/gpt-oss-120b",
110
- "accounts/fireworks/models/qwen3p8-max"
110
+ "accounts/fireworks/models/qwen3p8-max",
111
+ "accounts/fireworks/models/glm-5p3"
111
112
  ]
112
113
  },
113
114
  "capabilities": {
@@ -146,7 +147,7 @@
146
147
  },
147
148
  "liveMatrix": {
148
149
  "date": "2026-09-28",
149
- "result": "kimi-k2p6 is still listed on /models but a real chat call now returns 404 'not deployed', which failed the nightly model-liveness gate from 2026-09-26 and sent the matrix vision test down the text-only fallback. kimi-k3 answers a text call and a call carrying a PNG with 200, so it becomes the default; gpt-oss-120b, qwen3p8-max and glm-5p3 also answer text calls. Earlier (2026-08-30): deepseek-v4-pro was listed but hung past 120s, six ids returned 404 'not deployed', and glm-5p3 answers 'does not support image inputs'."
150
+ "result": "Issue #1796: the 2026-09-26 nightly model-liveness gate reported the default, kimi-k2p6, as dead, sending the matrix vision test down the text-only fallback. Confirmed live — GET /models still lists kimi-k2p6, but POST /chat/completions 404s 'Model not found, inaccessible, and/or not deployed', the same still-listed-but-unservable pattern deepseek-v4-pro hit in 2026-08 (hung past 120s rather than 404ing). Moved the default to kimi-k3, the same Moonshot family's newest generation: 200 OK on both a text-only call and a call carrying a PNG, and a full-length call returns a proper OpenAI-shaped tool_calls entry with finish_reason=tool_calls, confirming vision and tool support both survive the swap. kimi-k2p6 marked retired rather than deleted, per the frozen-enum rule. gpt-oss-120b, qwen3p8-max and glm-5p3 (the rest of fallbacks/topModels) were re-probed alive at the same time; glm-5p3 still only answers text calls, not image ones, as found 2026-08-30."
150
151
  },
151
152
  "addedInPR": "https://github.com/juspay/neurolink/pull/1587"
152
153
  }
@@ -30,6 +30,12 @@ export declare function getTopModelChoices(provider: AIProviderName, limit?: num
30
30
  * Get all available models for a provider
31
31
  * Returns all values from the provider's model enum
32
32
  *
33
+ * For a JSON-catalog provider, excludes retired ids. The `catalog` map keeps
34
+ * retired entries as raw table keys (they still resolve for a caller who
35
+ * pins that id explicitly — see `isValidModel`'s dedicated retired-id
36
+ * allowance below), but a listing/selection surface should not offer them.
37
+ * Mirrors the `selectableModels` filter in catalog/schema.ts.
38
+ *
33
39
  * @param provider - The AI provider to get models for
34
40
  * @returns Array of model identifier strings
35
41
  */
@@ -57,6 +63,20 @@ export declare function getDefaultModel(provider: AIProviderName): string | unde
57
63
  /**
58
64
  * Check if a model is valid for a given provider
59
65
  *
66
+ * `getAllModels` excludes retired catalog ids (it is a listing/selection
67
+ * surface), but a caller who pins a retired id explicitly must not have
68
+ * that pin start failing validation just because it dropped out of the
69
+ * listing — so this falls back to the full catalog (all statuses) before
70
+ * rejecting a JSON-catalog provider's model.
71
+ *
72
+ * The empty-listing case is checked against catalog membership, not
73
+ * accepted outright: a catalog provider whose every entry happens to be
74
+ * retired still has a real catalog to validate against, and a fabricated
75
+ * id must not slip through just because the *selectable* listing is empty.
76
+ * Only a provider with no catalog entry at all (openai-compatible,
77
+ * lm-studio, llamacpp — providers without a strict model list) keeps the
78
+ * permissive "allow any model" behavior.
79
+ *
60
80
  * @param provider - The AI provider
61
81
  * @param model - The model identifier to check
62
82
  * @returns true if the model exists in the provider's model enum
@@ -510,13 +510,21 @@ export function getTopModelChoices(provider, limit = 5) {
510
510
  * Get all available models for a provider
511
511
  * Returns all values from the provider's model enum
512
512
  *
513
+ * For a JSON-catalog provider, excludes retired ids. The `catalog` map keeps
514
+ * retired entries as raw table keys (they still resolve for a caller who
515
+ * pins that id explicitly — see `isValidModel`'s dedicated retired-id
516
+ * allowance below), but a listing/selection surface should not offer them.
517
+ * Mirrors the `selectableModels` filter in catalog/schema.ts.
518
+ *
513
519
  * @param provider - The AI provider to get models for
514
520
  * @returns Array of model identifier strings
515
521
  */
516
522
  export function getAllModels(provider) {
517
523
  const catalogEntry = catalogEntryFor(provider);
518
524
  if (catalogEntry) {
519
- return Object.keys(catalogEntry.models.catalog);
525
+ return Object.entries(catalogEntry.models.catalog)
526
+ .filter(([, spec]) => spec.status !== "retired")
527
+ .map(([model]) => model);
520
528
  }
521
529
  const modelEnum = MODEL_ENUMS[provider];
522
530
  if (!modelEnum) {
@@ -557,17 +565,42 @@ export function getDefaultModel(provider) {
557
565
  /**
558
566
  * Check if a model is valid for a given provider
559
567
  *
568
+ * `getAllModels` excludes retired catalog ids (it is a listing/selection
569
+ * surface), but a caller who pins a retired id explicitly must not have
570
+ * that pin start failing validation just because it dropped out of the
571
+ * listing — so this falls back to the full catalog (all statuses) before
572
+ * rejecting a JSON-catalog provider's model.
573
+ *
574
+ * The empty-listing case is checked against catalog membership, not
575
+ * accepted outright: a catalog provider whose every entry happens to be
576
+ * retired still has a real catalog to validate against, and a fabricated
577
+ * id must not slip through just because the *selectable* listing is empty.
578
+ * Only a provider with no catalog entry at all (openai-compatible,
579
+ * lm-studio, llamacpp — providers without a strict model list) keeps the
580
+ * permissive "allow any model" behavior.
581
+ *
560
582
  * @param provider - The AI provider
561
583
  * @param model - The model identifier to check
562
584
  * @returns true if the model exists in the provider's model enum
563
585
  */
564
586
  export function isValidModel(provider, model) {
565
587
  const models = getAllModels(provider);
566
- if (models.length === 0) {
567
- // For providers without strict model lists (like openai-compatible), allow any model
588
+ if (models.includes(model)) {
568
589
  return true;
569
590
  }
570
- return models.includes(model);
591
+ const catalogEntry = catalogEntryFor(provider);
592
+ if (catalogEntry) {
593
+ // Falls back to the FULL catalog (all statuses) so an explicitly pinned
594
+ // retired id still validates, and — critically — so an all-retired
595
+ // catalog (getAllModels() === []) is checked against real membership
596
+ // instead of accepting any string. Object.hasOwn (not `in`) so an
597
+ // inherited Object.prototype member name (e.g. "toString") is never
598
+ // mistaken for a real catalog key.
599
+ return Object.hasOwn(catalogEntry.models.catalog, model);
600
+ }
601
+ // No JSON catalog and no strict MODEL_ENUMS entry (openai-compatible,
602
+ // lm-studio, llamacpp): keep the permissive "allow any model" behavior.
603
+ return models.length === 0;
571
604
  }
572
605
  /**
573
606
  * Get model choices formatted for inquirer prompts with a specific default