Repository navigation
fix(providers): import intentional local-catalog-only providers instead of 502 (#5460, #5465) #5787
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
diegosouzapw
merged 1 commit into
release/v3.8.43
from
fix/5460-5465-sync-models-local-catalog
Jul 1, 2026
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
src/app/api/providers/[id]/sync-models/degradedLocalCatalog.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| /** | ||
| * #5460 (Reka) + #5465 (t3.chat) — Distinguish a genuinely degraded | ||
| * `local_catalog` models response (remote model discovery failed → the sync | ||
| * route surfaces a 502) from a provider whose local catalog is its INTENDED and | ||
| * only discovery source (reka, qwen-oauth, embedding/rerank providers like | ||
| * voyage-ai/jina-ai, web-cookie providers like t3-web). | ||
| * | ||
| * The models route tags the latter with `intentional: true`. Before this guard, | ||
| * model-sync 502'd on ANY `local_catalog` source, so the Import/Sync button | ||
| * failed every single time for those providers even with a valid key/cookie. | ||
| * | ||
| * Dependency-free leaf so it can be unit-tested without booting the DB/route. | ||
| */ | ||
| export function isDegradedLocalCatalog(modelsData: { | ||
| source?: unknown; | ||
| intentional?: unknown; | ||
| }): boolean { | ||
| const source = | ||
| typeof modelsData?.source === "string" ? modelsData.source.trim().toLowerCase() : ""; | ||
| return source === "local_catalog" && modelsData?.intentional !== true; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
tests/unit/sync-models-degraded-local-catalog-5460-5465.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import test from "node:test"; | ||
| import assert from "node:assert/strict"; | ||
|
|
||
| // #5460 (Reka) + #5465 (t3.chat): the model-sync route used to 502 on ANY | ||
| // `local_catalog` source, so providers whose local catalog is their ONLY | ||
| // discovery source (reka, qwen-oauth, embedding/rerank + web-cookie providers) | ||
| // failed Import/Sync every time. The route now imports those (flagged | ||
| // `intentional: true` by the models route) and only 502s a genuinely degraded | ||
| // remote-fetch fallback. | ||
| const { isDegradedLocalCatalog } = | ||
| await import("../../src/app/api/providers/[id]/sync-models/degradedLocalCatalog.ts"); | ||
|
|
||
| test("isDegradedLocalCatalog: intentional local-only catalog is NOT a degraded failure (#5460/#5465)", () => { | ||
| // reka / voyage-ai / t3-web etc. — the models route tags these intentional. | ||
| assert.equal( | ||
| isDegradedLocalCatalog({ source: "local_catalog", intentional: true }), | ||
| false, | ||
| "intentional local-catalog-only providers must import, not 502" | ||
| ); | ||
| // Case-insensitive on source. | ||
| assert.equal(isDegradedLocalCatalog({ source: "LOCAL_CATALOG", intentional: true }), false); | ||
| }); | ||
|
|
||
| test("isDegradedLocalCatalog: a degraded remote-fetch fallback IS a failure (502)", () => { | ||
| // Provider that normally discovers remotely but the fetch failed → no flag. | ||
| assert.equal( | ||
| isDegradedLocalCatalog({ | ||
| source: "local_catalog", | ||
| warning: "API unavailable — using local catalog", | ||
| }), | ||
| true, | ||
| "unflagged local_catalog is a degraded fallback and must 502" | ||
| ); | ||
| assert.equal(isDegradedLocalCatalog({ source: "local_catalog", intentional: false }), true); | ||
| }); | ||
|
|
||
| test("isDegradedLocalCatalog: non-local sources are never degraded-local failures", () => { | ||
| assert.equal(isDegradedLocalCatalog({ source: "api" }), false); | ||
| assert.equal(isDegradedLocalCatalog({ source: "cache", intentional: false }), false); | ||
| assert.equal(isDegradedLocalCatalog({}), false); | ||
| assert.equal(isDegradedLocalCatalog({ source: "" }), false); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import test from "node:test"; | ||
| import assert from "node:assert/strict"; | ||
|
|
||
| // #5465 — t3.chat's add-credential form showed the generic, circular cookie | ||
| // hint ("Required cookie: convex-session-id + Cookie header. Paste the Cookie | ||
| // header value…"). t3.chat needs a localStorage value AND the Cookie header, so | ||
| // that copy is confusing. A step-by-step DevTools hint (t3ChatWebCookieHint) | ||
| // already shipped translated in every locale but was never wired to the UI. | ||
| const { getWebSessionCredentialHint } = | ||
| await import("../../src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts"); | ||
| const { WEB_SESSION_CREDENTIAL_REQUIREMENTS } = | ||
| await import("../../src/shared/providers/webSessionCredentials.ts"); | ||
|
|
||
| const T3_HINT = | ||
| "Open t3.chat → DevTools → Application → Local Storage → https://t3.chat, copy 'convex-session-id'. Then open DevTools → Network, copy the full Cookie header from any chat request. Paste both values in the fields below."; | ||
|
|
||
| /** Minimal translator stub mimicking next-intl's `t` + `t.has`. */ | ||
| function makeTranslator(messages: Record<string, string>) { | ||
| const t = ((key: string, values?: Record<string, unknown>) => { | ||
| const raw = messages[key]; | ||
| if (raw === undefined) return key; | ||
| return values | ||
| ? Object.entries(values).reduce( | ||
| (acc, [name, value]) => acc.replaceAll(`{${name}}`, String(value)), | ||
| raw | ||
| ) | ||
| : raw; | ||
| }) as any; | ||
| t.has = (key: string) => key in messages; | ||
| return t; | ||
| } | ||
|
|
||
| test("t3.chat add-credential hint uses the step-by-step DevTools copy, not the circular generic one (#5465)", () => { | ||
| const t = makeTranslator({ | ||
| t3ChatWebCookieHint: T3_HINT, | ||
| webCookieCredentialHint: | ||
| "Required cookie: {credential}. Paste the Cookie header value from your own signed-in {provider} web session. Do not include the Cookie: prefix.", | ||
| }); | ||
|
|
||
| const hint = getWebSessionCredentialHint( | ||
| t, | ||
| WEB_SESSION_CREDENTIAL_REQUIREMENTS["t3-web"], | ||
| "t3.chat", | ||
| false | ||
| ); | ||
|
|
||
| assert.equal(hint, T3_HINT); | ||
| assert.ok(hint && hint.includes("Local Storage"), "must explain the localStorage step"); | ||
| assert.ok( | ||
| hint && !hint.includes("Required cookie: convex-session-id + Cookie header"), | ||
| "must not fall back to the circular generic cookie hint" | ||
| ); | ||
| }); | ||
|
|
||
| test("t3-web requirement now carries the hintKey override (#5465)", () => { | ||
| const req = WEB_SESSION_CREDENTIAL_REQUIREMENTS["t3-web"] as { hintKey?: string }; | ||
| assert.equal(req.hintKey, "t3ChatWebCookieHint"); | ||
| }); | ||
|
|
||
| test("cookie providers without a hintKey still use the generic hint (#5465 regression guard)", () => { | ||
| const t = makeTranslator({ | ||
| webCookieCredentialHint: | ||
| "Required cookie: {credential}. Paste the Cookie header value from your own signed-in {provider} web session. Do not include the Cookie: prefix.", | ||
| }); | ||
| // adapta-web is a cookie provider with no hintKey. | ||
| const hint = getWebSessionCredentialHint( | ||
| t, | ||
| WEB_SESSION_CREDENTIAL_REQUIREMENTS["adapta-web"], | ||
| "adapta", | ||
| false | ||
| ); | ||
| assert.ok(hint && hint.startsWith("Required cookie: __client")); | ||
| }); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For providers that are intentionally local-catalog-only (such as embedding, rerank, or web-cookie providers with no discovery configuration), returning a warning like
"API unavailable — using local catalog"is misleading because there is no remote API to begin with. Consider omitting thewarningproperty entirely whenintentionalistrue.