mirror of
https://github.com/LearningCircuit/local-deep-research.git
synced 2026-06-16 03:51:07 +03:00
* feat(lmstudio): add optional API key support for authenticated instances Recent LM Studio versions can require an API key on the local server. LDR previously hardcoded "not-required", making authenticated instances fail silently. This adds an optional `llm.lmstudio.api_key` setting (UI field on the home page + auto-rendered Settings → LLM field + `LDR_LLM_LMSTUDIO_API_KEY` env var), mirroring the Ollama optional-key pattern. Backward compatible: an empty/whitespace key falls back to the existing placeholder so unauthenticated installs require no change. `is_available()` now sends the key as an `Authorization: Bearer` header so authenticated LM Studio instances are correctly detected as available. The parallel `get_llm()` direct-construction path in `llm_config.py` is also updated so the user's key flows through both code paths. The path allowlist in `.gitleaks.toml` is extended to cover `llm_config.py` — this file already legitimately holds `api_key=` kwargs for all providers, matching the existing allowlist for `llm/providers/`. Closes #3573 * test(lmstudio): cover llm_config.py direct get_llm path for api key The provider-class path (LMStudioProvider.create_llm) is already covered in test_lmstudio_provider.py, but the parallel get_llm("lmstudio", ...) branch in llm_config.py had no test asserting the user's key flows through to ChatOpenAI. Adds two tests mirroring the existing test_openai_endpoint_without_api_key_uses_placeholder pattern: one for configured-key passthrough, one for the empty-key fallback to "not-required". Closes a coverage gap surfaced during PR review. * feat(lmstudio): override list_models_for_api + FAQ entry for save UX Folds two of the three deferred follow-ups into the PR: 1. Override `LMStudioProvider.list_models_for_api` so the optional API key is read from settings on the high-level `list_models()` path, matching what the settings route already does. Caller-provided keys (route path) short-circuit the settings read via an `if not api_key:` guard, so the route remains untouched. Eliminates the drift hazard between the two key-resolution paths. 2. Add a FAQ entry documenting the save-then-poll UX: the password field saves on blur, so pasting + immediately clicking refresh can produce an empty model list. Recovery is to tab out then refresh. Adds 3 new tests for the override (dummy-key fallback, user-key passthrough from settings, route-provided key not overwritten by settings read). The third deferred item (`llm.ollama.api_key` JSON entry) stays as a separate cleanup PR — different provider, different feature.
246 lines
12 KiB
YAML
246 lines
12 KiB
YAML
# ============================================================================
|
|
# DOCKER COMPOSE BASE CONFIGURATION (CPU-only, works on all platforms)
|
|
# ============================================================================
|
|
# This base configuration works on:
|
|
# - macOS (M1/M2/M3/M4 Apple Silicon and Intel)
|
|
# - Windows
|
|
# - Linux (with or without GPU)
|
|
# - Unraid (via Docker Compose Manager plugin)
|
|
#
|
|
# To add NVIDIA GPU acceleration:
|
|
# docker compose -f docker-compose.yml -f docker-compose.gpu.override.yml up -d
|
|
#
|
|
# See docker-compose.gpu.override.yml for GPU setup details.
|
|
# Future: docker-compose.amd.override.yml will support AMD GPUs.
|
|
#
|
|
# For Unraid users: See docs/deployment/unraid.md for complete setup guide
|
|
# ============================================================================
|
|
|
|
services:
|
|
local-deep-research:
|
|
image: localdeepresearch/local-deep-research:latest
|
|
|
|
networks:
|
|
- ldr-network
|
|
|
|
# Enable host.docker.internal on Linux (already works on Mac/Windows)
|
|
# This allows containers to reach services on the host machine (e.g., LM Studio)
|
|
extra_hosts:
|
|
- "host.docker.internal:host-gateway"
|
|
|
|
ports:
|
|
- "5000:5000"
|
|
environment:
|
|
# ============================================================================
|
|
# Full configuration reference: docs/CONFIGURATION.md
|
|
# ============================================================================
|
|
# IMPORTANT: Almost all settings can be configured through the web interface.
|
|
#
|
|
# Environment variables FORCE settings and prevent ANY changes through the UI.
|
|
# These are NOT defaults - they are permanent overrides that lock the setting.
|
|
# Only use environment variables if you want to enforce specific settings
|
|
# that users should NEVER be able to change.
|
|
#
|
|
# After starting the container, configure your settings at:
|
|
# http://localhost:5000/settings
|
|
# ============================================================================
|
|
|
|
# ============================================================================
|
|
# DOCKER-SPECIFIC SETTINGS (Forced for good reasons!)
|
|
# These settings are forced to ensure proper Docker operation:
|
|
# - Prevents accidental misconfiguration that would break the container
|
|
# - Ensures consistency between Docker configuration and application settings
|
|
# - Users can still change ports via Docker's port mapping (see below)
|
|
# ============================================================================
|
|
- LDR_WEB_HOST=0.0.0.0 # Must bind to all interfaces for Docker networking
|
|
- LDR_WEB_PORT=5000 # Internal port must match Docker's expectation
|
|
- LDR_DATA_DIR=/data # Must match the volume mount point
|
|
|
|
# ============================================================================
|
|
# TO CHANGE PORTS: Use Docker port mapping, NOT the settings above!
|
|
# Example - to expose on port 8080 instead of 5000:
|
|
# ports:
|
|
# - "8080:5000" # Maps host port 8080 to container port 5000
|
|
#
|
|
# This is the Docker way and avoids configuration mismatches.
|
|
# ============================================================================
|
|
|
|
# ============================================================================
|
|
# DOCKER-COMPOSE SERVICE ENDPOINTS (Forced for proper container networking)
|
|
# These are required for containers to communicate with each other.
|
|
# Docker containers cannot use 'localhost' to reach other containers.
|
|
# These settings override UI configuration for Docker Compose deployments.
|
|
# Note: pip/local installs use localhost defaults and are unaffected.
|
|
# ============================================================================
|
|
- LDR_LLM_OLLAMA_URL=http://ollama:11434
|
|
- LDR_SEARCH_ENGINE_WEB_SEARXNG_DEFAULT_PARAMS_INSTANCE_URL=http://searxng:8080
|
|
|
|
# ============================================================================
|
|
# SECURITY: Disable user registration (recommended for public deployments)
|
|
# When set to false, no new accounts can be created through the web UI.
|
|
# Register your initial account FIRST, then set this to false.
|
|
# ============================================================================
|
|
# - LDR_APP_ALLOW_REGISTRATIONS=false
|
|
|
|
# ============================================================================
|
|
# ADVANCED: Uncomment ONLY if you need to enforce/lock these settings
|
|
# WARNING: This will disable UI configuration for these settings!
|
|
# ============================================================================
|
|
# - LDR_LLM_PROVIDER=ollama # Lock the LLM provider
|
|
# - LDR_LLM_MODEL=gemma3:12b # Lock the model selection
|
|
# - LDR_LLM_OPENAI_API_KEY=<your-api-key> # Lock OpenAI API key
|
|
# - LDR_LLM_ANTHROPIC_API_KEY=<your-api-key> # Lock Anthropic API key
|
|
|
|
# ============================================================================
|
|
# OPENROUTER CONFIGURATION
|
|
# To use OpenRouter (100+ models via one API), uncomment these lines:
|
|
# Get your API key from https://openrouter.ai/
|
|
# Browse models at https://openrouter.ai/models
|
|
# ============================================================================
|
|
# - LDR_LLM_PROVIDER=openai_endpoint
|
|
# - LDR_LLM_OPENAI_ENDPOINT_URL=https://openrouter.ai/api/v1
|
|
# - LDR_LLM_OPENAI_ENDPOINT_API_KEY=<your-api-key>
|
|
# - LDR_LLM_MODEL=anthropic/claude-3.5-sonnet # or any OpenRouter model
|
|
|
|
# ============================================================================
|
|
# OTHER OPENAI-COMPATIBLE APIS
|
|
# The same pattern works for any OpenAI-compatible service:
|
|
# ============================================================================
|
|
# - LDR_LLM_PROVIDER=openai_endpoint
|
|
# - LDR_LLM_OPENAI_ENDPOINT_URL=https://your-provider.com/v1
|
|
# - LDR_LLM_OPENAI_ENDPOINT_API_KEY=<your-api-key>
|
|
# - LDR_LLM_MODEL=<your-model-name>
|
|
|
|
# ============================================================================
|
|
# LM STUDIO (Running on Host Machine)
|
|
# If using LM Studio on your host, use host.docker.internal to reach it.
|
|
# The extra_hosts setting above enables this on Linux.
|
|
# ============================================================================
|
|
# - LDR_LLM_PROVIDER=lmstudio
|
|
# - LDR_LLM_LMSTUDIO_URL=http://host.docker.internal:1234
|
|
# - LDR_LLM_LMSTUDIO_API_KEY=<api-key-if-required> # optional; leave out for unauth instances
|
|
# - LDR_LLM_MODEL=<your-loaded-model-name>
|
|
volumes:
|
|
- ldr_data:/data
|
|
- ldr_scripts:/scripts
|
|
# ============================================================================
|
|
# NOTE: Do NOT add resource limits (deploy.resources.limits) here.
|
|
# Resource needs vary by deployment (RAM, CPU, workload). Deployers should
|
|
# use docker-compose.override.yml to set limits for their environment.
|
|
# ============================================================================
|
|
# NOTE: Local document collections are managed via the Collections UI,
|
|
# not via Docker volume mounts. Do not add local_collections mounts here.
|
|
# ============================================================================
|
|
# Allow SQLCipher mlock() without noisy kernel warnings
|
|
ulimits:
|
|
memlock:
|
|
soft: -1
|
|
hard: -1
|
|
# ============================================================================
|
|
# CONTAINER SECURITY — Principle of Least Privilege
|
|
# ============================================================================
|
|
# The entrypoint (scripts/ldr_entrypoint.sh) runs as root to fix Docker
|
|
# volume ownership, then drops to ldruser (UID 1000) via setpriv before
|
|
# starting the application. The privilege timeline is:
|
|
#
|
|
# 1. Root phase (brief): mkdir, chmod, chown on /data and /home/ldruser
|
|
# 2. setpriv exec: irreversible switch to ldruser (empty capability set)
|
|
# 3. App phase: runs as ldruser with zero capabilities
|
|
#
|
|
# We drop ALL capabilities, then add back only what the root phase needs.
|
|
# no-new-privileges prevents re-escalation after setpriv drops privileges.
|
|
#
|
|
# WARNING: Removing cap_add entries will break container startup — the
|
|
# entrypoint will fail with "Operation not permitted" on chown/chmod.
|
|
# This is especially visible in restricted environments (Proxmox LXC).
|
|
# ============================================================================
|
|
security_opt:
|
|
- "no-new-privileges:true"
|
|
cap_drop:
|
|
- ALL
|
|
cap_add:
|
|
- CHOWN # chown -R ldruser:ldruser /data
|
|
- FOWNER # chmod on files not owned by root (after chown to ldruser)
|
|
- DAC_OVERRIDE # mkdir in /home/ldruser (.config dir, mode 755 = no write for others)
|
|
- SETUID # setpriv setuid() syscall to switch to ldruser
|
|
- SETGID # setpriv setgid() syscall to switch group
|
|
restart: unless-stopped
|
|
depends_on:
|
|
ollama:
|
|
condition: service_healthy
|
|
|
|
searxng:
|
|
condition: service_started
|
|
|
|
|
|
ollama:
|
|
image: ollama/ollama:latest@sha256:8850b8b33936b9fb246e7b3e02849941f1151ea847e5fb15511f17de9589aea1
|
|
# Note: GPU acceleration disabled by default (CPU-only base)
|
|
# For NVIDIA GPU support, use docker-compose.gpu.override.yml
|
|
# For AMD GPU support (future), use docker-compose.amd.override.yml
|
|
|
|
container_name: ollama_service
|
|
# No custom entrypoint — uses the upstream image default (`ollama serve`).
|
|
# Considered-and-rejected: pre-pulling ${MODEL:-gemma3:12b} via
|
|
# scripts/ollama_entrypoint.sh on container start. That had three
|
|
# problems: (a) wasted ~5-10 min and ~7-8 GB on every fresh boot,
|
|
# (b) penalised users running LM Studio / OpenAI / llama.cpp who don't
|
|
# use ollama at all, and (c) coupled the compose to an opinionated
|
|
# default model. Users who choose ollama pull their model explicitly
|
|
# (`docker exec ollama_service ollama pull <model>`) or let LDR pull on
|
|
# first use. Healthcheck below probes the daemon (model-agnostic, #3885).
|
|
healthcheck:
|
|
test: [ "CMD", "ollama", "list" ]
|
|
interval: 10s
|
|
timeout: 5s
|
|
start_period: 30s
|
|
retries: 5
|
|
environment:
|
|
OLLAMA_KEEP_ALIVE: '30m'
|
|
volumes:
|
|
- ollama_data:/root/.ollama
|
|
networks:
|
|
- ldr-network
|
|
security_opt:
|
|
- "no-new-privileges:true"
|
|
cap_drop:
|
|
- ALL
|
|
# WARNING: Do NOT add ports: - "11434:11434" — Ollama has no authentication.
|
|
# Access is intentionally restricted to the ldr-network internal network.
|
|
restart: unless-stopped
|
|
|
|
searxng:
|
|
image: searxng/searxng:latest@sha256:6dd0dffc05a75d92bbacd858953b4e93b8f709403c3fb1fb8a33ca8fd02e40a4
|
|
container_name: searxng
|
|
networks:
|
|
- ldr-network
|
|
security_opt:
|
|
- "no-new-privileges:true"
|
|
# Note: no cap_drop here — upstream removed cap_drop: ALL from their
|
|
# official compose in searxng/searxng-docker@31acd45 ("[fix] remove
|
|
# security cap", 2025-05-20), and the new container template at
|
|
# searxng/searxng/container/docker-compose.yml ships with no cap_drop /
|
|
# cap_add at all. The searxng entrypoint runs as root, copies config
|
|
# into the volume, and drops to the searxng user — chasing the exact
|
|
# cap_add list across uWSGI→Granian and future entrypoint changes is
|
|
# not worth the maintenance cost. no-new-privileges and the pinned
|
|
# digest are the meaningful guardrails. See #3874.
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-qO-", "http://localhost:8080/healthz"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
start_period: 30s
|
|
retries: 3
|
|
volumes:
|
|
- searxng_data:/etc/searxng
|
|
restart: unless-stopped
|
|
|
|
|
|
volumes:
|
|
ldr_data:
|
|
ldr_scripts:
|
|
ollama_data:
|
|
searxng_data:
|
|
networks:
|
|
ldr-network:
|