Skip to content

fix(models): four provider-catalog and connection defects (#3262, #3115, #3486, #3467) - #3880

Open
ntdatt812 wants to merge 3 commits into
decolua:masterfrom
ntdatt812:bundle/models-provider-config
Open

ntdatt812 wants to merge 3 commits into
decolua:masterfrom
ntdatt812:bundle/models-provider-config

Conversation

@ntdatt812

Copy link
Copy Markdown
Contributor

Four provider/model-catalog fixes that were sitting in separate PRs (#3330, #3252, #3529, #3539), rebased onto current master. Independent commits.

commit defect
fix(auth) (#3262) no-auth free providers got a fresh upstream session id per request, so providers that key state on the session saw every call as a new conversation
fix(models) (#3115) a curated custom-model list did not suppress the live /models fetch, so the catalog came back with the provider's full list instead of the operator's selection
feat(models) (#3486) combo entries in /v1/models reported no token limits, so clients sizing a request against a combo had nothing to read
fix(providers) (#3467) self-hosted TTS/STT/embedding connections had no Base URL field, so they could not point at anything but the default host

The #3115 commit reads as larger than it is: most of it is one block moved earlier in buildModelsList so that customModelIds exists before the dynamic-fetch decision. The behavioural change is the added && customModelIds.length === 0 on that condition.

Verification

Run with vitest 3 and the repo config — both parts matter:

npx vitest@3 run --config tests/vitest.config.js <files>

Without --config the @/ alias does not resolve (Failed to load url @/lib/dataDir.js). And vitest 2.x cannot run anything that reaches open-sse/translator/index.js: it throws TypeError: register is not a function at the top-level register(...) calls, because index.js deliberately relies on function-declaration hoisting to survive the import cycle while Vite 5's SSR transform turns the named import into a call-time property access. I confirmed that failure on clean master and on master as of 15 Aug, so it is not caused by these changes.

Every fix was checked by reverting it and confirming which test dies. No claim here rests on a test that passes for another reason.

…ve catalog

For a custom OpenAI/Anthropic-compatible node PROVIDER_MODELS is empty, so
rawModelIds came out empty and /v1/models fetched the upstream's entire
catalogue and merged it into the listing. The user's own model list, managed in
the dashboard and stored as customModels, was only merged afterwards — so
deleting a model there had no effect: the live fetch put it straight back.

Compute customModelIds before the fallback and skip the fetch when the provider
already has curated models. Providers with nothing configured still fall back to
the live catalogue, and skipDynamicFetch is unaffected.

Fixes decolua#3115
…olua#3486)

Individual models carry context_length and max_completion_tokens; combo
entries were built as bare { id, object, owned_by } and skipped them. A
client that sizes its context window off /v1/models therefore fell back to
guessing from the model name, and guessed against a pool whose members can
be far wider - a 1M pool read as deepseek 128k, compacting at ~96k.

Emit the same two snake_case fields on combo entries. A request routed to
a combo can land on any member, so the pool can only promise what its
smallest member accepts: take the minimum across members.

A member that reports no usable number is skipped rather than read as
zero, and when no member reports one the field is omitted entirely - an
absent field lets the client fall back, a wrong one does not.

Web-search and web-fetch combos are untouched: they are not chat models
and carry no window.

The alias-to-provider map the DB-unavailable branch already built is
hoisted so the combo loop can share it, rather than building a second.
…ecolua#3467)

The three self-hosted providers read their endpoint from
providerSpecificData.baseUrl, and their own registry entries tell the user
to set it:

    text: "Set providerSpecificData.baseUrl to the server root, e.g.
           http://host:8080 — /v1/audio/speech is appended."

The dashboard never rendered a field for it. Every connection created
through the UI was therefore saved without one and fell back to the
built-in localhost default — which inside Docker is the 9router container
itself, so a separately hosted server is unreachable and nothing says why.

The modal did have such a field, but it was hardcoded to one provider:

    const isOllamaLocal = provider === "ollama-local";
    {isOllamaLocal && <Input label="Ollama Host URL" ... />}

Make it declarative instead. A provider whose endpoint is per-connection
now says so in its registry entry:

    baseUrlField: { label, placeholder, help }

and the modal renders whatever the registry declares. ollama-local moves
onto the same mechanism rather than keeping a second one beside it, with
its label and placeholder preserved exactly.

The placeholders are each provider's real fallback, so the box shows what
the connection would otherwise use.

buildProviderSpecificData moves into src/shared/utils so the saved shape
can be tested without rendering. An empty box still saves nothing: it
means "keep the default", not "save an empty endpoint".
@ntdatt812
ntdatt812 force-pushed the bundle/models-provider-config branch from 23c9a8a to ed8069b Compare September 24, 2026 02:42
@ntdatt812

Copy link
Copy Markdown
Contributor Author

Rebased onto master (39e36d3d). Was CONFLICTING, now clean.

One commit came out of the bundle: fix(auth): give no-auth free providers a stable upstream session id. 0c6ab4f9 solved the same problem more generally — stableSessionId now keys one long-lived session per identity, and identityKey reads credentials?.connectionId || credentials?.id. The no-auth record already carries id: "noauth", so the connectionId this commit added changed nothing. Its two remaining test expectations were characterizing the behaviour 0c6ab4f9 replaced, so they went with it.

Three commits remain:

commit what
b7b07df7 a curated custom-provider model list suppresses the live catalog instead of being merged into it (#3115)
0a560c77 combo entries in /v1/models report context_length and max_completion_tokens (#3486)
ed8069b6 self-hosted TTS/STT/embedding providers can set their base URL (#3467)

The conflict in src/app/api/v1/models/route.js was an addition on both sides, not a choice: your aggregateComboCapabilities still sets entry.capabilities, and this adds the two token-limit fields beside it. Both imports are kept.

npx vitest@3 run --config tests/vitest.config.js: 89 failures, the same 89 by name as on master at 39e36d3d.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant