feat: local LLM support via Ollama (OpenAI-compatible, behind ENABLE_OLLAMA) - #34
Open
amal66 wants to merge 1 commit into
Open
feat: local LLM support via Ollama (OpenAI-compatible, behind ENABLE_OLLAMA)#34amal66 wants to merge 1 commit into
amal66 wants to merge 1 commit into
Conversation
…OLLAMA) Mechanical port from b3166dd (apps/api -> backend): - lib/llm/providers/ollama.ts: Ollama adapter registered through the provider registry, gated by ENABLE_OLLAMA (off by default) - lib/llm/baseUrl.ts: OPENAI_BASE_URL resolution with SSRF guard (OPENAI_ALLOW_LOCAL_BASE_URL) so the OpenAI adapter can point at a local Ollama server - lib/privateIp.ts: shared private/reserved IP classifier used by the base-URL guard - lib/llm/openai.ts: fetch openAIResponsesUrl() instead of the hard-coded api.openai.com constant - lib/llm/index.ts: register Ollama in registerBuiltinProviders() when ENABLE_OLLAMA=true - .env.example: document the opt-in Ollama configuration - tests for the ollama gate, base-URL resolution, and IP classifier Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CEguyEgXa9JjCciXCcVemC
This was referenced Jul 17, 2026
Closed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Adds opt-in local LLM support via Ollama, so a self-hosted deployment can run chat inference entirely on its own hardware — documents never leave the building, and with this flag the model inference doesn't either. Ollama is exposed through its OpenAI-compatible endpoint, so the existing OpenAI streaming/completion adapter does all the work; the Ollama "provider" is just a registry entry that routes local model IDs (llama3.3, phi4, qwen2.5, ...) through that adapter pointed at
http://localhost:11434/v1.Everything is behind
ENABLE_OLLAMA=true(defaultfalse): with the flag off, no Ollama models appear in the model set and nothing about existing behavior changes.Changes
backend/src/lib/llm/providers/ollama.ts— Ollama adapter registered through the provider registry:setupOllamaFromEnv()gates onENABLE_OLLAMA=true, registers a default local-model list plus optional extras fromOLLAMA_MODELS(comma-separated), and reuses the OpenAI adapter's stream/complete functions. Also registered as an API-key provider that reusesOPENAI_API_KEY(Ollama accepts any non-empty string).backend/src/lib/llm/index.ts—registerBuiltinProviders()now takes an injectableenvand registers Ollama when enabled.backend/src/lib/llm/baseUrl.ts—OPENAI_BASE_URLresolution for the OpenAI adapter with an SSRF guard: http/localhost/private-IP endpoints are rejected in production unlessOPENAI_ALLOW_LOCAL_BASE_URL=true. Defaults tohttps://api.openai.com/v1(unchanged behavior when unset).backend/src/lib/llm/openai.ts— fetchesopenAIResponsesUrl()instead of the hard-codedapi.openai.comconstant (same URL by default).backend/src/lib/privateIp.ts— shared private/reserved IP classifier (IPv4 + IPv6 incl. mapped/NAT64/6to4 forms) used by the base-URL guard.backend/.env.example— documents the opt-in configuration.providers/__tests__/ollama.test.ts(ENABLE_OLLAMA gate, custom models),__tests__/baseUrl.test.ts(default endpoint, normalization, production SSRF rejections),__tests__/privateIp.test.ts(classifier ranges).Why
ENABLE_OLLAMAdefaults tofalse, so cloud deployments see zero cost and zero behavior change — no new models in the picker, no new egress, and the OpenAI adapter still hitshttps://api.openai.com/v1.upstream-pr/provider-registry): the registry was built so a provider like this is a single self-contained file plus one gated registration call; this PR is the first external provider to use it.Testing
npm install && npm run build(tsc) inbackend/— clean on the committed tree.npx vitest runinbackend/— 6 test files, 53 tests, all passing. This includes the 3 new test files added by this PR (ollama gate: 3 tests, baseUrl: 7 tests, privateIp: 9 tests) alongside the existing registry/models/downloadTokens suites.setupOllamaFromEnv({})andENABLE_OLLAMA=false/"1"register nothing; only the exact string"true"registers the provider.Provenance
All changes are mechanical ports of code in amal66/mike@origin/main (commit b3166dd); exceptions:
baseUrl.ts: readsprocess.env.*directly instead of the fork's zod-validatedlib/envmodule (this repo has no such module); same defaults and semantics.baseUrl.test.ts/ollama.test.ts: dropped the fork'svi.mock("../env")blocks for the same reason (nothing to mock; env reads are lazy).llm/index.ts: only theENABLE_OLLAMAbranch of the fork's registration logic is ported (its air-gap branches are intentionally out of scope); the boot log usesconsole.log(this repo has nolib/logger); doc comments condensed to drop air-gap references..env.example: fork's Ollama block minus theOLLAMA_EMBEDDING_MODELSline (local embeddings are not part of this PR).privateIp.ts,privateIp.test.ts, andproviders/ollama.tsare byte-identical to the fork's files.Credits & prior art
GET /models/ollama) and live picker UX, which this PR does not attempt. The two are reconcilable — their model-discovery UX on top of this PR's registry gating would be strictly better than either alone.🤖 Generated with Claude Code
https://claude.ai/code/session_01CEguyEgXa9JjCciXCcVemC