Add `local` provider for any OpenAI-compatible self-hosted LLM

manueljpconde adds a fourth LLM provider to the abstraction layer, letting self-hosters point the backend at Ollama, LM Studio, or any `/v1/chat/completions`-compatible endpoint. The integration is entirely backend-configured; the browser never learns the URL, model ID, or API key.

infrastructuresecurity

Seven env vars control the feature: ENABLE_LOCAL_LLM, LOCAL_LLM_BASE_URL, LOCAL_LLM_MODEL, LOCAL_LLM_LABEL, LOCAL_LLM_API_KEY, LOCAL_LLM_SUPPORTS_TOOLS, and LOCAL_LLM_TIMEOUT_MS (default 120 000 ms). The browser gets a single canonical model id local:server, and the status object that reaches the frontend deliberately omits all sensitive fields - just the label, configured, and tool-support flags.

The LOCAL_LLM_SUPPORTS_TOOLS=false path is where the interesting engineering is. When tools are disabled, runLLMStream empties the active-tools array and injects a LOCAL MODEL LIMITATION clause into the system prompt explicitly warning the model not to claim it used tools. Tabular-review and document-editing workflows are hard-gated: serializeProfile, getUserModelSettings, and validateProfilePayload all refuse local:server for tabular unless supportsTools=true. Frontend dropdowns mirror this - the model only appears if the backend reports it configured, and it's excluded from the tabular dropdown unless tool support is on.

The diff also includes node:test unit tests for the SSE chunk extractor (extractChatCompletionSseJson), streamLocal, and getLocalLlmConfig, plus a new npm test --prefix backend script wired into the README Checks section.

Two things to flag before pulling. The .env.example diff switches OPENROUTER_API_KEY to OPENAI_API_KEY - unrelated to the local-LLM scope and easy to miss if your fork still routes through OpenRouter. Also, a useEffect that reset the API-key input on hasSavedKey change is removed from account/models/page.tsx without any connection to the local-LLM work; it appears to be an incidental UX fix.

So what Worth a look if you're running self-hosted models and want a clean, env-var-only integration that gates tool-dependent workflows properly. The provider boundary is tidy - new `local.ts` / `localConfig.ts`, three small touch points elsewhere - so it's not a large pull. Watch the `.env.example` key rename if your fork uses OpenRouter.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

2 commits from manueljpconde/mikept, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
c0361944 feat: add self-hosted local llm provider Manuel Conde 2026-05-09 ↗ GitHub
c8ef360f Merge pull request #2 from manueljpconde/codex/local-llm-provider Manuel Conde 2026-05-09 ↗ GitHub
Add self-hosted local LLM provider support

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-209.md from inside the repo you want the changes in.

⬇ Download capture-thread-209.md