From 32dd3a82497f418fe1916ae7e6bfe74293d75194 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Fri, 17 Jul 2026 10:50:25 -0300 Subject: [PATCH] fix(dashboard): include never-tested connections in combo builder active-provider list (port from 9router#2057) (#7118) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newly-added provider connections default testStatus to null until an operator explicitly runs a connection test. The combo builder's active-providers filter only kept testStatus === active/success, so a freshly-added custom provider was excluded from activeProviders — ModelSelectModal's loadCustomProviderModels() effect never fired for it, and its models never populated the combo model picker. Extracted the eligibility check into isEligibleActiveConnection (src/lib/combos/builderDraft.ts), treating a never-tested connection the same as a known-good one (consistent with deriveConnectionStatus in builderOptions.ts, which only flags error on an explicit error/fail testStatus). Reported-by: fajarbossit (https://github.com/decolua/9router/issues/2057) --- .../2057-combo-custom-provider-models.md | 1 + src/app/(dashboard)/dashboard/combos/page.tsx | 5 +- src/lib/combos/builderDraft.ts | 27 ++++++++ ...o-builder-eligible-connection-2057.test.ts | 62 +++++++++++++++++++ 4 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 changelog.d/fixes/2057-combo-custom-provider-models.md create mode 100644 tests/unit/combo-builder-eligible-connection-2057.test.ts diff --git a/changelog.d/fixes/2057-combo-custom-provider-models.md b/changelog.d/fixes/2057-combo-custom-provider-models.md new file mode 100644 index 0000000000..b6b75b6256 --- /dev/null +++ b/changelog.d/fixes/2057-combo-custom-provider-models.md @@ -0,0 +1 @@ +- **fix(dashboard):** include never-tested custom provider connections in the combo builder's active-provider list so their models load without requiring a manual connection test first. (thanks @fajarbossit) diff --git a/src/app/(dashboard)/dashboard/combos/page.tsx b/src/app/(dashboard)/dashboard/combos/page.tsx index 1b794a1651..4efc9618a3 100644 --- a/src/app/(dashboard)/dashboard/combos/page.tsx +++ b/src/app/(dashboard)/dashboard/combos/page.tsx @@ -14,7 +14,6 @@ import Toggle from "@/shared/components/Toggle"; import Tooltip from "@/shared/components/Tooltip"; import { ComboCompressionModeSelect } from "@/shared/components/compression/ComboCompressionModeSelect"; import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard"; -import { filterUsableConnections } from "@/shared/utils/connectionStatus"; import { FieldLabelWithHelp, WeightTotalBar } from "./parts"; import { useComboProxyAssignments } from "./useComboProxyAssignments"; import { ResponseValidationEditor, type ResponseValidationValue } from "./ResponseValidationEditor"; @@ -35,6 +34,7 @@ import { getNextComboBuilderStage, getPreviousComboBuilderStage, hasExactModelStepDuplicate, + isEligibleActiveConnection, isIntelligentBuilderStrategy, parseQualifiedModel, resolveComboBuilderProviderId, @@ -772,7 +772,8 @@ export default function CombosPage() { if (combosRes.ok) setCombos((combosData.combos || []).filter((c) => !c.isHidden)); if (providersRes.ok) { - setActiveProviders(filterUsableConnections(providersData.connections || [])); + const active = (providersData.connections || []).filter(isEligibleActiveConnection); + setActiveProviders(active); } if (metricsRes.ok) setMetrics(metricsData.metrics || {}); setProviderNodes(nodesData.nodes || []); diff --git a/src/lib/combos/builderDraft.ts b/src/lib/combos/builderDraft.ts index 1638f1b35c..18e6a09e31 100644 --- a/src/lib/combos/builderDraft.ts +++ b/src/lib/combos/builderDraft.ts @@ -20,6 +20,33 @@ export function isIntelligentBuilderStrategy(strategy: unknown): boolean { return strategy === "auto" || strategy === "lkgp"; } +export type ComboEligibleConnectionLike = { + isActive?: boolean | null; + testStatus?: string | null; +}; + +/** + * Whether a provider connection should be treated as eligible for the combo + * builder's "active providers" list (used to decide which providers get their + * models fetched/shown when creating or editing a combo). + * + * Newly-created connections default `testStatus` to `null` until someone + * explicitly runs a connection test (`src/lib/db/providers.ts`). Excluding + * those from the combo builder meant a freshly-added custom provider's models + * never populated the combo model picker until an operator manually tested + * the connection — matching the reported symptom (#2057). "Never tested" is + * therefore treated the same as "known good", consistent with + * `deriveConnectionStatus` in `src/lib/combos/builderOptions.ts`, which only + * flags a connection as an error when `testStatus` explicitly matches + * `/error|fail/i`. + */ +export function isEligibleActiveConnection(connection: ComboEligibleConnectionLike): boolean { + if (connection.isActive === false) return false; + const testStatus = connection.testStatus; + if (!testStatus) return true; + return testStatus === "active" || testStatus === "success" || testStatus === "unknown"; +} + export function getComboBuilderStages(options: ComboBuilderStageOptions = {}): ComboBuilderStage[] { if (isIntelligentBuilderStrategy(options.strategy)) { return [...COMBO_BUILDER_STAGES]; diff --git a/tests/unit/combo-builder-eligible-connection-2057.test.ts b/tests/unit/combo-builder-eligible-connection-2057.test.ts new file mode 100644 index 0000000000..63699aa990 --- /dev/null +++ b/tests/unit/combo-builder-eligible-connection-2057.test.ts @@ -0,0 +1,62 @@ +/** + * tests/unit/combo-builder-eligible-connection-2057.test.ts + * + * Regression for upstream issue #2057: the combo builder's "active providers" + * filter (src/app/(dashboard)/dashboard/combos/page.tsx::fetchData) only kept + * connections whose `testStatus` was exactly "active" or "success". New + * connections default `testStatus` to `null` until someone explicitly runs a + * connection test (src/lib/db/providers.ts), so a freshly-added custom + * provider was excluded from `activeProviders` — which meant + * ModelSelectModal's loadCustomProviderModels() effect never fetched its + * models, and the combo builder's model list stayed empty for that provider. + * + * Fix: `isEligibleActiveConnection` (src/lib/combos/builderDraft.ts) treats a + * never-tested connection (`testStatus` null/undefined) as eligible too, + * matching the "error only on explicit failure" semantics already used by + * `deriveConnectionStatus` in src/lib/combos/builderOptions.ts. + */ +import test from "node:test"; +import assert from "node:assert/strict"; +import { isEligibleActiveConnection } from "../../src/lib/combos/builderDraft.ts"; + +test("isEligibleActiveConnection includes a never-tested (testStatus null) connection", () => { + assert.equal(isEligibleActiveConnection({ testStatus: null }), true); +}); + +test("isEligibleActiveConnection includes a never-tested (testStatus undefined) connection", () => { + assert.equal(isEligibleActiveConnection({}), true); +}); + +test("isEligibleActiveConnection includes explicit active/success/unknown statuses", () => { + assert.equal(isEligibleActiveConnection({ testStatus: "active" }), true); + assert.equal(isEligibleActiveConnection({ testStatus: "success" }), true); + assert.equal(isEligibleActiveConnection({ testStatus: "unknown" }), true); +}); + +test("isEligibleActiveConnection excludes connections that were explicitly deactivated", () => { + assert.equal(isEligibleActiveConnection({ isActive: false, testStatus: null }), false); + assert.equal(isEligibleActiveConnection({ isActive: false, testStatus: "active" }), false); +}); + +test("isEligibleActiveConnection excludes connections with a known error/failed test status", () => { + assert.equal(isEligibleActiveConnection({ testStatus: "error" }), false); + assert.equal(isEligibleActiveConnection({ testStatus: "failed" }), false); + assert.equal(isEligibleActiveConnection({ testStatus: "expired" }), false); + assert.equal(isEligibleActiveConnection({ testStatus: "unavailable" }), false); +}); + +test("simulated fetchData filter: a fresh custom provider connection with testStatus=null survives to activeProviders", () => { + const connections = [ + { id: "conn-1", provider: "openai", testStatus: "active", isActive: true }, + // Freshly-added custom provider, never tested yet. + { id: "conn-2", provider: "my-custom-provider", testStatus: null, isActive: true }, + { id: "conn-3", provider: "broken-provider", testStatus: "error", isActive: true }, + ]; + + const activeProviders = connections.filter(isEligibleActiveConnection); + + assert.deepEqual( + activeProviders.map((c) => c.id), + ["conn-1", "conn-2"] + ); +});