Skip to content

Ollama support - #16

Open
Blaumaus wants to merge 5 commits into
mainfrom
feature/ollama
Open

Ollama support#16
Blaumaus wants to merge 5 commits into
mainfrom
feature/ollama

Conversation

@Blaumaus

@Blaumaus Blaumaus commented May 20, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added Ollama as a supported AI provider with local endpoint and gateway connection support.
  • Documentation

    • Expanded docs with Ollama-specific base URL examples and optional API key guidance.
  • UI Improvements

    • Provider settings, model selector, badges, and about/settings text now include Ollama-specific inputs and messaging.
  • Models

    • Model management and refresh flows recognize and surface Ollama models.
  • Database

    • Connection records now allow an optional base URL and nullable API key for Ollama.
  • Tests

    • Added tests for Ollama/OpenWebUI connection and model-listing helpers.
  • Infrastructure

    • Docker compose updated to enable host gateway resolution.

Review Change Stack

@Blaumaus Blaumaus self-assigned this May 20, 2026
@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Blaumaus has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 36 minutes and 5 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 17991408-7aba-4188-82b3-1b53a2c66941

📥 Commits

Reviewing files that changed from the base of the PR and between 765fdeb and 723a7c6.

📒 Files selected for processing (2)
  • apps/web/app/lib/ollama.server.ts
  • apps/web/app/routes/settings.models.tsx
📝 Walkthrough

Walkthrough

This PR adds Ollama/OpenWebUI as a supported provider with endpoint normalization/validation, stores optional per-provider baseUrl in the DB, updates web settings/UI to configure local endpoints, expands worker model discovery/creation to use Ollama endpoints, and updates docs, tests, and compose infra.

Changes

Ollama Provider Support

Layer / File(s) Summary
Provider type system and database schema
packages/database/src/types.ts, packages/database/src/index.ts, packages/database/prisma/schema.prisma, packages/database/prisma/migrations/20260520120000_add_ollama_connection/migration.sql
Adds ollama to the shared provider set, derives Provider from PROVIDERS, exports PROVIDER_NAMES and isProvider, makes ApiKey.encryptedKey nullable and adds optional baseUrl.
Ollama/OpenWebUI endpoint utilities (web)
apps/web/app/lib/ollama.server.ts
Implement trusted-origin allowlist, getOllamaBaseUrlCandidates, parseOpenAICompatibleModelIds, and validateOpenAICompatibleModelEndpoint to normalize candidate URLs, parse OpenAI-compatible /models responses, and probe endpoints with timeout and optional Bearer auth.
Ollama/OpenWebUI helpers (worker)
apps/worker/src/ollama.ts
Worker helpers: getOllamaBaseUrlCandidates, parseOpenAICompatibleModelIds, and fetchOpenAICompatibleModelIds with timeout and optional auth for model discovery.
Web provider configuration
apps/web/app/lib/providers.ts
Introduce centralized ALL_PROVIDERS, PROVIDER_NAMES, OLLAMA_DEFAULT_BASE_URL, and endpoint presets for UI forms.
Web settings: connection configuration
apps/web/app/routes/settings.connections.tsx
Loader returns per-provider connection including baseUrl; action validates/saves Ollama baseUrl (optional apiKey), upserts encrypted key (nullable) + baseUrl, rebuilds cached models and schedules refresh; ProviderCard renders Ollama base-URL input with presets and optional API key.
Web UI components
apps/web/app/ui/Badge.tsx, apps/web/app/ui/ProviderLogo.tsx
Add ollama badge variant, Ollama SVG/logo styling, import shared Provider type and re-export PROVIDER_NAMES.
Web settings: models page and chat input
apps/web/app/routes/settings.models.tsx, apps/web/app/components/ChatInput.tsx
Models page and ChatInput iterate over ALL_PROVIDERS, include an ollama bucket, and show Ollama-specific empty-state guidance (pull model, then refresh).
Worker model utilities and provider inference
apps/worker/src/model-utils.ts
Add model-id predicates for OpenAI/Google, provider inference heuristics, cached-provider preference, and model-name formatting helpers.
Worker provider connection and model creation
apps/worker/src/utils.ts
Add getProviderConnection to fetch/decrypt per-user apiKey/baseUrl (best-effort), createLanguageModelForProvider to instantiate SDK clients (OpenAI/Anthropic/Google/OpenAI-compatible for Ollama), and re-export model-utils helpers.
Worker model refresh job
apps/worker/src/processors/refresh.ts
Refresh logic now reads baseUrl per API-key record, decrypts keys conditionally, allows Ollama to proceed without an apiKey, and uses fetchOpenAICompatibleModelIds to populate cached models.
Worker chat processor
apps/worker/src/processors/chat.ts
Refactor to use createLanguageModelForProvider and add an Ollama fallback for title generation by iterating enabled Ollama models.
Worker Ollama helpers & tests
apps/worker/src/ollama.ts, apps/worker/test/ollama.test.ts
Worker-side Ollama/OpenWebUI helpers and tests for URL candidate normalization, model-list parsing, fetch behavior, and model metadata formatting/provider inference.
Infrastructure and dependencies
compose-cloud.yaml, apps/worker/package.json, apps/worker/tsconfig.json
Add host.docker.internal:host-gateway to compose, add @ai-sdk/openai-compatible dependency to worker, and update worker TS lib to ES2023.
Docs and README
README.md, apps/docs/content/docs/selfhosting/configuring.mdx, apps/docs/content/docs/usage/connections.mdx, apps/docs/content/docs/usage/conversations.mdx, apps/docs/content/docs/usage/models.mdx, apps/web/app/routes/settings.about.tsx
Docs updated to mention Ollama/OpenWebUI endpoints, example base URLs for Docker and non-Docker deployments, and Ollama-specific refresh workflow guidance.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A new endpoint hops into the yard,
Ollama whispers, local and starred.
Base URLs trimmed, models found with a ping,
Workers and web now make the models sing.
Bounce a refresh — the local bloom takes wing! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Ollama support' directly and clearly reflects the primary objective of the changeset, which is to add comprehensive support for Ollama as a new provider throughout the application.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/ollama

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/app/components/ChatInput.tsx (1)

672-681: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use Ollama-specific copy in the disconnected state.

When the Ollama tab is selected and not connected, this branch still tells the user to “Connect your API key”. That contradicts the new endpoint-based flow and sends them toward a credential they may not need.

Proposed fix
                               <div>
                                 <Text as="p" size="sm" weight="medium" className="mb-1">
                                   {PROVIDER_NAMES[activeTab]}
                                 </Text>
                                 <Text as="p" size="sm" colour="muted">
-                                  Connect your API key to use these models
+                                  {activeTab === 'ollama'
+                                    ? 'Connect your Ollama endpoint to use these models'
+                                    : 'Connect your API key to use these models'}
                                 </Text>
                               </div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/app/components/ChatInput.tsx` around lines 672 - 681, The
disconnected-state copy currently always shows "Connect your API key" for any
provider; update the UI branch that renders when activeTab is not 'favorites'
and not in connectedProviders to show Ollama-specific guidance when activeTab
=== 'ollama' instead of the API-key text: inside the block that renders
ProviderLogo and the two Text elements, conditionally render
PROVIDER_NAMES[activeTab] as before but change the second Text to show
endpoint/configuration instructions for Ollama (e.g., "Configure an Ollama
endpoint to use local/remote models") when activeTab === 'ollama', otherwise
keep the existing "Connect your API key" copy so other providers are unchanged.
🧹 Nitpick comments (2)
apps/docs/content/docs/selfhosting/configuring.mdx (1)

166-166: ⚡ Quick win

Clarify OpenWebUI URL for Docker deployments.

The OpenWebUI example uses http://localhost:3000/api, but when Chathouse runs in Docker and OpenWebUI runs on the host, users should use http://host.docker.internal:3000/api instead. Consider adding this clarification to avoid confusion.

📝 Suggested clarification
-For Ollama, the base URL must be reachable from the Chathouse server and worker. Use `http://localhost:11434/v1` when Ollama runs beside the app, `http://host.docker.internal:11434/v1` when Chathouse runs in Docker and Ollama runs on the host, or `http://localhost:3000/api` for an OpenWebUI gateway.
+For Ollama, the base URL must be reachable from the Chathouse server and worker. Use `http://localhost:11434/v1` when Ollama runs beside the app, `http://host.docker.internal:11434/v1` when Chathouse runs in Docker and Ollama runs on the host, or `http://localhost:3000/api` (or `http://host.docker.internal:3000/api` if Chathouse is in Docker and OpenWebUI is on the host) for an OpenWebUI gateway.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/docs/content/docs/selfhosting/configuring.mdx` at line 166, Update the
Ollama/OpenWebUI URL line to clarify Docker-host networking: note that the
OpenWebUI example `http://localhost:3000/api` is correct when both services run
on the same host, but when Chathouse runs in Docker and OpenWebUI runs on the
host the URL should be `http://host.docker.internal:3000/api`; modify the
sentence that currently lists `http://localhost:3000/api` to include this
Docker-specific alternative and a brief parenthetical explaining when to use
each form.
apps/docs/content/docs/usage/connections.mdx (1)

41-47: ⚡ Quick win

Clarify OpenWebUI URL for Docker deployments.

Similar to the configuration documentation, when Chathouse runs in Docker and OpenWebUI runs on the host, http://localhost:3000/api won't work — users need http://host.docker.internal:3000/api instead.

📝 Suggested clarification
 For Ollama, enter the base URL that the Chathouse server or worker can reach:
 
 - **Local Ollama** — `http://localhost:11434/v1`
 - **Docker host Ollama** — `http://host.docker.internal:11434/v1`
-- **OpenWebUI gateway** — `http://localhost:3000/api`
+- **OpenWebUI gateway** — `http://localhost:3000/api` (or `http://host.docker.internal:3000/api` if Chathouse is in Docker and OpenWebUI is on the host)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/docs/content/docs/usage/connections.mdx` around lines 41 - 47, Update
the OpenWebUI gateway example line that currently reads "OpenWebUI gateway —
`http://localhost:3000/api`" to clarify Docker host scenarios: add the
alternative URL `http://host.docker.internal:3000/api` and a short note stating
that when Chathouse runs in Docker and OpenWebUI runs on the host, users must
use the host.docker.internal address instead of localhost; modify the sentence
about pasting an OpenWebUI API key only if authentication is required to remain
accurate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/app/lib/ollama.server.ts`:
- Line 3: The two helper functions getOllamaBaseUrlCandidates and
parseOpenAICompatibleModelIds are exported but not used externally causing knip
CI failures; remove the export by making them module-private (remove the
"export" keyword) or, if they should be used elsewhere, import them from their
intended consumers instead; update any references within
apps/web/app/lib/ollama.server.ts to call the now non-exported functions
directly and run the linter/knip to confirm the unused-export error is resolved.
- Around line 58-61: The fetch call that queries `${baseUrl}/models` using
`trimmedApiKey` lacks a timeout and can hang; update the code around the fetch
(the block that assigns `const response = await fetch(...)`) to use an
AbortController: create a controller, pass `signal: controller.signal` in the
fetch options (merging with existing headers), start a setTimeout to call
`controller.abort()` after a short timeout (e.g., 5s), and clear the timeout
after the fetch completes; also ensure callers handle the aborted request
(AbortError) appropriately so validation fails fast on network stalls.

In `@apps/web/app/lib/providers.ts`:
- Around line 3-8: ALL_PROVIDERS is declared with `as const` (a readonly tuple)
but uses `satisfies Provider[]`, which conflicts with readonly → mutable type in
strict mode; update the type to `satisfies readonly Provider[]` to match the
readonly tuple, and apply the same fix to the analogous WORKER_PROVIDERS
declaration (replace `satisfies Provider[]` with `satisfies readonly
Provider[]`) so the tuple and its satisfied type are compatible.

In `@apps/web/app/routes/settings.connections.tsx`:
- Around line 116-143: The Ollama branch uses raw form values for validation but
only trims the apiKey when storing; trim both apiKey and baseUrl before calling
validateOpenAICompatibleModelEndpoint to ensure pasted tokens/URLs with
surrounding whitespace validate properly. Concretely, derive a trimmedApiKey =
(formData.get('apiKey') as string)?.trim() and trimmedBaseUrl =
((formData.get('baseUrl') as string) || OLLAMA_DEFAULT_BASE_URL).trim(), use
trimmedBaseUrl and trimmedApiKey when calling
validateOpenAICompatibleModelEndpoint and when building the upsert payload
(encryptedKey via encrypt(trimmedApiKey)), leaving db.apiKey.upsert,
validateOpenAICompatibleModelEndpoint, OLLAMA_DEFAULT_BASE_URL and encrypt as
the referenced symbols to update.

In `@apps/web/app/routes/settings.models.tsx`:
- Around line 383-386: The current initializer for groups uses
Object.fromEntries and force-casts to Record<Provider, typeof filteredModels>,
which causes TS2352; replace that cast with an explicit object literal typed as
Record<Provider, typeof filteredModels> that lists all provider keys (the four
keys from ALL_PROVIDERS) and initializes each to filteredModels (or an empty
array of the same type). Update the declaration using the symbol groups and
ensure the literal's type matches typeof filteredModels so the typechecker
accepts it instead of the Object.fromEntries cast.

In `@apps/worker/src/ollama.ts`:
- Around line 54-57: fetchOpenAICompatibleModelIds calls fetchImpl to GET
`${baseUrl}/models` without a timeout; wrap this request in an AbortController
and pass its signal into the fetchImpl options (alongside headers when
trimmedApiKey is present), start a timer (e.g., 5–10s) that calls
controller.abort() on expiry, and clear the timer when the fetch completes to
avoid leaks; update the call site where fetchImpl is invoked (referencing
fetchImpl, baseUrl, trimmedApiKey, and fetchOpenAICompatibleModelIds) so hung
endpoints are aborted and the job processor cannot stall.

In `@apps/worker/src/processors/chat.ts`:
- Around line 135-157: The callbacks in getOllamaTitleModels lose type inference
causing TS7006; explicitly type the results of the Prisma queries and the
intermediate arrays: annotate models as Array<{ modelId: string }> (the result
of db.cachedModel.findMany) and settings as Array<{ modelId: string; enabled:
boolean; favorite: boolean }> (the result of db.enabledModel.findMany), ensure
settingsMap is typed as Map<string, { modelId: string; enabled: boolean;
favorite: boolean }>, and add explicit parameter types for the arrow callbacks
used in .map(), .filter(), and .toSorted() (e.g., (model: {modelId: string}) and
(a: {modelId: string}, b: {modelId: string})) so the chain no longer produces
implicit any errors in getOllamaTitleModels.

In `@apps/worker/src/processors/refresh.ts`:
- Around line 102-104: The refresh logic in the 'ollama' branch throws when
baseUrl is missing, preventing use of the same default endpoint used by
createLanguageModelForProvider; update the 'ollama' case to fall back to
OLLAMA_DEFAULT_BASE_URL when baseUrl is falsy (instead of throwing) and then
call fetchOpenAICompatibleModelIds with that fallback (preserve apiKey
handling), ensuring cachedModel can populate the same way as
createLanguageModelForProvider.
- Around line 74-83: The catch block around decrypt(encryptedKey) currently does
a bare continue which skips refreshing the entire provider; change it to only
skip for providers that absolutely require a valid API key and allow execution
to proceed for 'ollama' by not continuing — e.g., in the catch, log the error as
before but if provider === 'ollama' clear/leave apiKey undefined (so the
endpoint can be used without auth) and do not continue, otherwise continue for
providers that need the key; update references to encryptedKey, decrypt,
provider, userId and apiKey in the refresh logic to implement this conditional
behavior.

In `@apps/worker/src/utils.ts`:
- Around line 406-410: The barrel export currently re-exports
resolveProviderForModelId even though it is unused and flagged by Knip; remove
resolveProviderForModelId from the export list so only formatModelName,
isGoogleChatModelId, and isOpenAIModelId are exported, and if any callers need
resolveProviderForModelId update them to import it directly from
./model-utils.js (or delete those imports if unused).
- Around line 78-81: getProviderConnection is exported but not used outside this
module, causing a lint/CI failure; remove the public export by making the
function internal (drop the "export" on getProviderConnection) in
apps/worker/src/utils.ts and update any local references if necessary so the
function is still callable within the module; ensure the function signature and
return type stay the same but is not exported from the module.
- Around line 89-94: The current try/catch around
decrypt(connection.encryptedKey) returns null on any decryption failure, which
incorrectly marks providers like Ollama (where apiKey is optional) as
unconfigured; update the logic in the function that sets apiKey (look for
connection.encryptedKey, decrypt(), and the apiKey variable) so that a
decryption error does not return null unconditionally: catch the error, log or
warn about the failed decrypt, set apiKey to undefined (or leave it unset), and
only return null when the provider actually requires an API key (i.e., perform
the provider-specific check after attempting decryption and only return null for
providers that mandate apiKey).

---

Outside diff comments:
In `@apps/web/app/components/ChatInput.tsx`:
- Around line 672-681: The disconnected-state copy currently always shows
"Connect your API key" for any provider; update the UI branch that renders when
activeTab is not 'favorites' and not in connectedProviders to show
Ollama-specific guidance when activeTab === 'ollama' instead of the API-key
text: inside the block that renders ProviderLogo and the two Text elements,
conditionally render PROVIDER_NAMES[activeTab] as before but change the second
Text to show endpoint/configuration instructions for Ollama (e.g., "Configure an
Ollama endpoint to use local/remote models") when activeTab === 'ollama',
otherwise keep the existing "Connect your API key" copy so other providers are
unchanged.

---

Nitpick comments:
In `@apps/docs/content/docs/selfhosting/configuring.mdx`:
- Line 166: Update the Ollama/OpenWebUI URL line to clarify Docker-host
networking: note that the OpenWebUI example `http://localhost:3000/api` is
correct when both services run on the same host, but when Chathouse runs in
Docker and OpenWebUI runs on the host the URL should be
`http://host.docker.internal:3000/api`; modify the sentence that currently lists
`http://localhost:3000/api` to include this Docker-specific alternative and a
brief parenthetical explaining when to use each form.

In `@apps/docs/content/docs/usage/connections.mdx`:
- Around line 41-47: Update the OpenWebUI gateway example line that currently
reads "OpenWebUI gateway — `http://localhost:3000/api`" to clarify Docker host
scenarios: add the alternative URL `http://host.docker.internal:3000/api` and a
short note stating that when Chathouse runs in Docker and OpenWebUI runs on the
host, users must use the host.docker.internal address instead of localhost;
modify the sentence about pasting an OpenWebUI API key only if authentication is
required to remain accurate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f21ce64a-96a0-49ef-a418-7a2c791fd397

📥 Commits

Reviewing files that changed from the base of the PR and between 995676c and a0639e8.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (25)
  • README.md
  • apps/docs/content/docs/selfhosting/configuring.mdx
  • apps/docs/content/docs/usage/connections.mdx
  • apps/docs/content/docs/usage/conversations.mdx
  • apps/docs/content/docs/usage/models.mdx
  • apps/web/app/components/ChatInput.tsx
  • apps/web/app/lib/ollama.server.ts
  • apps/web/app/lib/providers.ts
  • apps/web/app/routes/settings.about.tsx
  • apps/web/app/routes/settings.connections.tsx
  • apps/web/app/routes/settings.models.tsx
  • apps/web/app/ui/Badge.tsx
  • apps/web/app/ui/ProviderLogo.tsx
  • apps/worker/package.json
  • apps/worker/src/model-utils.ts
  • apps/worker/src/ollama.ts
  • apps/worker/src/processors/chat.ts
  • apps/worker/src/processors/refresh.ts
  • apps/worker/src/utils.ts
  • apps/worker/test/ollama.test.ts
  • compose-cloud.yaml
  • packages/database/prisma/migrations/20260520120000_add_ollama_connection/migration.sql
  • packages/database/prisma/schema.prisma
  • packages/database/src/index.ts
  • packages/database/src/types.ts

Comment thread apps/web/app/lib/ollama.server.ts Outdated
Comment thread apps/web/app/lib/ollama.server.ts Outdated
Comment thread apps/web/app/lib/providers.ts Outdated
Comment thread apps/web/app/routes/settings.connections.tsx
Comment thread apps/web/app/routes/settings.models.tsx Outdated
Comment thread apps/worker/src/processors/refresh.ts
Comment thread apps/worker/src/processors/refresh.ts Outdated
Comment thread apps/worker/src/utils.ts Outdated
Comment thread apps/worker/src/utils.ts
Comment thread apps/worker/src/utils.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/web/app/lib/ollama.server.ts (1)

5-32: ⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Restrict Ollama endpoints to a trusted allowlist.

This accepts any user-supplied http(s) host and fetches it from the server, which gives authenticated users an SSRF primitive against internal services or metadata endpoints. Since the validated URL is then persisted and reused by refresh jobs, this needs an admin-controlled allowlist or an explicit unsafe opt-in instead of accepting arbitrary hosts by default.

Also applies to: 57-84

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/app/lib/ollama.server.ts` around lines 5 - 32, The function
getOllamaBaseUrlCandidates currently accepts arbitrary http(s) hosts creating an
SSRF risk; replace this behavior with an admin-controlled allowlist check (or
explicit unsafe opt-in flag) before accepting the parsed URL: in
getOllamaBaseUrlCandidates validate url.origin against a configured
TRUSTED_OLLAMA_HOSTS (or a feature flag like ALLOW_UNSAFE_OLLAMA_HOSTS) and
throw a clear error if the origin is not allowed; apply the same
allowlist/opt-in enforcement to the analogous code block referenced at lines
57-84 (the other Ollama URL validation/normalization) so only origins in the
allowlist (or when the explicit unsafe opt-in is enabled) are returned/
persisted.
apps/web/app/routes/settings.models.tsx (1)

497-501: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Honor the selected provider filter when rendering sections.

After filteredModels is narrowed by filterProvider, this loop still renders every connected provider. Selecting one provider leaves the other connected providers on screen with empty states, so the filter appears broken.

Suggested fix
         {ALL_PROVIDERS.map((provider) => {
+          if (filterProvider !== 'all' && provider !== filterProvider) return null
+
           const models = groupedModels[provider]
           const isConnected = connectedProviders.includes(provider)

           if (models.length === 0 && !isConnected) return null
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/app/routes/settings.models.tsx` around lines 497 - 501, The
rendering loop currently iterates ALL_PROVIDERS and shows connected providers
even when filterProvider is set; update the map to iterate only providers
present in filteredModels (or pre-filter ALL_PROVIDERS by filterProvider) so
sections honor the selected provider filter: replace ALL_PROVIDERS.map(...) with
Object.keys(groupedModels) or ALL_PROVIDERS.filter(p => !filterProvider || p ===
filterProvider).map(...) and keep the existing checks using groupedModels,
connectedProviders, and models to avoid rendering unrelated provider sections.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/worker/src/utils.ts`:
- Around line 96-100: The decrypt-failure log currently includes a raw userId
(logger.error(... ${userId} ...)), which must be removed or redacted; update the
logger.error call in the decrypt failure path to omit the raw userId and instead
log only non-identifying context such as the provider or a redacted/id-hash
(e.g., "[REDACTED]" or a short hash of userId), preserving the existing message
about SECRET_KEY_BASE; keep the existing control flow (the provider check and
return null when provider !== 'ollama') unchanged and only change the contents
of the logger.error invocation.

---

Outside diff comments:
In `@apps/web/app/lib/ollama.server.ts`:
- Around line 5-32: The function getOllamaBaseUrlCandidates currently accepts
arbitrary http(s) hosts creating an SSRF risk; replace this behavior with an
admin-controlled allowlist check (or explicit unsafe opt-in flag) before
accepting the parsed URL: in getOllamaBaseUrlCandidates validate url.origin
against a configured TRUSTED_OLLAMA_HOSTS (or a feature flag like
ALLOW_UNSAFE_OLLAMA_HOSTS) and throw a clear error if the origin is not allowed;
apply the same allowlist/opt-in enforcement to the analogous code block
referenced at lines 57-84 (the other Ollama URL validation/normalization) so
only origins in the allowlist (or when the explicit unsafe opt-in is enabled)
are returned/ persisted.

In `@apps/web/app/routes/settings.models.tsx`:
- Around line 497-501: The rendering loop currently iterates ALL_PROVIDERS and
shows connected providers even when filterProvider is set; update the map to
iterate only providers present in filteredModels (or pre-filter ALL_PROVIDERS by
filterProvider) so sections honor the selected provider filter: replace
ALL_PROVIDERS.map(...) with Object.keys(groupedModels) or ALL_PROVIDERS.filter(p
=> !filterProvider || p === filterProvider).map(...) and keep the existing
checks using groupedModels, connectedProviders, and models to avoid rendering
unrelated provider sections.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4ac5d2c9-efb5-4917-81ff-a08f67afd700

📥 Commits

Reviewing files that changed from the base of the PR and between a0639e8 and 4c3f8fd.

📒 Files selected for processing (13)
  • apps/docs/content/docs/selfhosting/configuring.mdx
  • apps/docs/content/docs/usage/connections.mdx
  • apps/web/app/components/ChatInput.tsx
  • apps/web/app/lib/ollama.server.ts
  • apps/web/app/lib/providers.ts
  • apps/web/app/routes/settings.connections.tsx
  • apps/web/app/routes/settings.models.tsx
  • apps/worker/src/model-utils.ts
  • apps/worker/src/ollama.ts
  • apps/worker/src/processors/chat.ts
  • apps/worker/src/processors/refresh.ts
  • apps/worker/src/utils.ts
  • apps/worker/tsconfig.json
✅ Files skipped from review due to trivial changes (3)
  • apps/worker/tsconfig.json
  • apps/docs/content/docs/usage/connections.mdx
  • apps/docs/content/docs/selfhosting/configuring.mdx

Comment thread apps/worker/src/utils.ts
@Blaumaus

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Blaumaus

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
✅ Actions performed

Reviews resumed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/web/app/lib/ollama.server.ts (1)

71-83: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject non-model payloads during endpoint validation.

Lines 71-83 return [] for any non-{ data: [...] } body, and Line 108 treats that the same as a valid empty model list. That means any 200 response with the wrong payload shape can pass validation and save a broken endpoint with zero models.

Proposed fix
-function parseOpenAICompatibleModelIds(value: unknown): string[] {
+function parseOpenAICompatibleModelIds(value: unknown): string[] | null {
   if (!value || typeof value !== 'object' || !Array.isArray((value as { data?: unknown }).data)) {
-    return []
+    return null
   }

   const ids = new Set<string>()
   for (const item of (value as { data: unknown[] }).data) {
     if (!item || typeof item !== 'object') continue
     const id = (item as { id?: unknown }).id
     if (typeof id === 'string' && id.trim()) ids.add(id.trim())
   }

   return [...ids]
 }
-      return { baseUrl, modelIds: parseOpenAICompatibleModelIds(await response.json()) }
+      const modelIds = parseOpenAICompatibleModelIds(await response.json())
+      if (!modelIds) {
+        errors.push(`${baseUrl} returned a non-OpenAI-compatible model payload`)
+        continue
+      }
+
+      return { baseUrl, modelIds }

Also applies to: 108-108

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/app/lib/ollama.server.ts` around lines 71 - 83, The helper
parseOpenAICompatibleModelIds currently returns an empty array for any
non-{data: [...] } payload, which lets malformed 200 responses be treated as
valid empty model lists; change parseOpenAICompatibleModelIds to return null (or
throw) when the shape isn't the expected object with a data array (i.e. replace
the initial guard to return null), and update every caller that currently treats
an empty array as "valid" (the call site that validates endpoint model lists) to
treat null as a validation failure and reject/save the endpoint as invalid
instead of accepting zero models.
apps/web/app/routes/settings.models.tsx (1)

552-560: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid Ollama setup guidance when filters caused the empty state.

This branch runs after search/provider/favorites filtering, so users with existing Ollama models can still see “Pull a model in Ollama” just by applying a non-matching filter. Only show this hint when the unfiltered Ollama list is empty; otherwise use a generic “adjust your search or filters” message.

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/web/app/lib/ollama.server.ts`:
- Around line 71-83: The helper parseOpenAICompatibleModelIds currently returns
an empty array for any non-{data: [...] } payload, which lets malformed 200
responses be treated as valid empty model lists; change
parseOpenAICompatibleModelIds to return null (or throw) when the shape isn't the
expected object with a data array (i.e. replace the initial guard to return
null), and update every caller that currently treats an empty array as "valid"
(the call site that validates endpoint model lists) to treat null as a
validation failure and reject/save the endpoint as invalid instead of accepting
zero models.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 568f3141-8091-4b2f-b163-54b2859c0494

📥 Commits

Reviewing files that changed from the base of the PR and between 4c3f8fd and 765fdeb.

📒 Files selected for processing (3)
  • apps/web/app/lib/ollama.server.ts
  • apps/web/app/routes/settings.models.tsx
  • apps/worker/src/utils.ts

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