From 7f0a5cd06ae18d8593bbb557f29eb09a1097e252 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Tue, 21 Apr 2026 01:43:20 -0700 Subject: [PATCH 1/4] fix(hitl-edit-panel): move duplicate-tag check into functional setTags Fixes #1248 handleAddTag had tags in its useCallback dependency array only so the closure-level duplicate check could read it, which forced the callback to re-create on every tag mutation and compared new additions against a potentially-stale closure value. Collapse the duplicate check into the functional setTags updater so the check always runs against the latest state, and drop tags from the dependency array - the callback is stable for the component's lifetime and downstream memoization won't get invalidated on every keystroke. --- .../hitl-edit-panel/hitl-edit-panel.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/surfsense_web/components/hitl-edit-panel/hitl-edit-panel.tsx b/surfsense_web/components/hitl-edit-panel/hitl-edit-panel.tsx index bd36431e9..b33392f38 100644 --- a/surfsense_web/components/hitl-edit-panel/hitl-edit-panel.tsx +++ b/surfsense_web/components/hitl-edit-panel/hitl-edit-panel.tsx @@ -65,16 +65,15 @@ function EmailsTagField({ setTags((prev) => (typeof newTags === "function" ? newTags(prev) : newTags)); }, []); - const handleAddTag = useCallback( - (text: string) => { - const trimmed = text.trim(); - if (!trimmed) return; - if (tags.some((tag) => tag.text === trimmed)) return; + const handleAddTag = useCallback((text: string) => { + const trimmed = text.trim(); + if (!trimmed) return; + setTags((prev) => { + if (prev.some((tag) => tag.text === trimmed)) return prev; const newTag: TagType = { id: Date.now().toString(), text: trimmed }; - setTags((prev) => [...prev, newTag]); - }, - [tags] - ); + return [...prev, newTag]; + }); + }, []); return ( Date: Tue, 21 Apr 2026 21:03:38 +0530 Subject: [PATCH 2/4] feat: add internal backend URL configuration --- docker/.env.example | 1 + docker/docker-compose.yml | 1 + surfsense_web/app/api/zero/query/route.ts | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docker/.env.example b/docker/.env.example index a975ed8e7..95de0cf85 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -71,6 +71,7 @@ EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2 # BACKEND_URL=https://api.yourdomain.com # NEXT_PUBLIC_FASTAPI_BACKEND_URL=https://api.yourdomain.com # NEXT_PUBLIC_ZERO_CACHE_URL=https://zero.yourdomain.com +# FASTAPI_BACKEND_INTERNAL_URL=http://backend:8000 # ------------------------------------------------------------------------------ # Zero-cache (real-time sync) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 549190947..93d725979 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -198,6 +198,7 @@ services: NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE: ${AUTH_TYPE:-LOCAL} NEXT_PUBLIC_ETL_SERVICE: ${ETL_SERVICE:-DOCLING} NEXT_PUBLIC_DEPLOYMENT_MODE: ${DEPLOYMENT_MODE:-self-hosted} + FASTAPI_BACKEND_INTERNAL_URL: ${FASTAPI_BACKEND_INTERNAL_URL:-http://backend:8000} labels: - "com.centurylinklabs.watchtower.enable=true" depends_on: diff --git a/surfsense_web/app/api/zero/query/route.ts b/surfsense_web/app/api/zero/query/route.ts index 3d8ff0d33..a91edcd6f 100644 --- a/surfsense_web/app/api/zero/query/route.ts +++ b/surfsense_web/app/api/zero/query/route.ts @@ -5,7 +5,10 @@ import type { Context } from "@/types/zero"; import { queries } from "@/zero/queries"; import { schema } from "@/zero/schema"; -const backendURL = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000"; +const backendURL = + process.env.FASTAPI_BACKEND_INTERNAL_URL || + process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || + "http://localhost:8000"; async function authenticateRequest( request: Request From 59f8696eacbb49c0a54a7af8d0fb33d12e0fdf4a Mon Sep 17 00:00:00 2001 From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com> Date: Tue, 21 Apr 2026 23:17:26 +0530 Subject: [PATCH 3/4] feat: add extra_hosts configuration for Docker services --- docker/docker-compose.dev.yml | 4 ++++ docker/docker-compose.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index c7922e3ef..bbe758d4f 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -77,6 +77,8 @@ services: - shared_temp:/shared_tmp env_file: - ../surfsense_backend/.env + extra_hosts: + - "host.docker.internal:host-gateway" environment: - DATABASE_URL=${DATABASE_URL:-postgresql+asyncpg://${DB_USER:-postgres}:${DB_PASSWORD:-postgres}@${DB_HOST:-db}:${DB_PORT:-5432}/${DB_NAME:-surfsense}} - CELERY_BROKER_URL=${REDIS_URL:-redis://redis:6379/0} @@ -118,6 +120,8 @@ services: - shared_temp:/shared_tmp env_file: - ../surfsense_backend/.env + extra_hosts: + - "host.docker.internal:host-gateway" environment: - DATABASE_URL=${DATABASE_URL:-postgresql+asyncpg://${DB_USER:-postgres}:${DB_PASSWORD:-postgres}@${DB_HOST:-db}:${DB_PORT:-5432}/${DB_NAME:-surfsense}} - CELERY_BROKER_URL=${REDIS_URL:-redis://redis:6379/0} diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 93d725979..10cace249 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -60,6 +60,8 @@ services: - shared_temp:/shared_tmp env_file: - .env + extra_hosts: + - "host.docker.internal:host-gateway" environment: DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://${DB_USER:-surfsense}:${DB_PASSWORD:-surfsense}@${DB_HOST:-db}:${DB_PORT:-5432}/${DB_NAME:-surfsense}} CELERY_BROKER_URL: ${REDIS_URL:-redis://redis:6379/0} @@ -100,6 +102,8 @@ services: - shared_temp:/shared_tmp env_file: - .env + extra_hosts: + - "host.docker.internal:host-gateway" environment: DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://${DB_USER:-surfsense}:${DB_PASSWORD:-surfsense}@${DB_HOST:-db}:${DB_PORT:-5432}/${DB_NAME:-surfsense}} CELERY_BROKER_URL: ${REDIS_URL:-redis://redis:6379/0} From 7c23f31dc001f2272e4b82f9350705ac57f48a58 Mon Sep 17 00:00:00 2001 From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com> Date: Tue, 21 Apr 2026 23:25:43 +0530 Subject: [PATCH 4/4] feat: add Ollama setup guide to documentation --- surfsense_web/content/docs/how-to/meta.json | 2 +- surfsense_web/content/docs/how-to/ollama.mdx | 90 ++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 surfsense_web/content/docs/how-to/ollama.mdx diff --git a/surfsense_web/content/docs/how-to/meta.json b/surfsense_web/content/docs/how-to/meta.json index 477fcafc4..329b7172e 100644 --- a/surfsense_web/content/docs/how-to/meta.json +++ b/surfsense_web/content/docs/how-to/meta.json @@ -1,6 +1,6 @@ { "title": "How to", - "pages": ["zero-sync", "realtime-collaboration", "web-search"], + "pages": ["zero-sync", "realtime-collaboration", "web-search", "ollama"], "icon": "Compass", "defaultOpen": false } diff --git a/surfsense_web/content/docs/how-to/ollama.mdx b/surfsense_web/content/docs/how-to/ollama.mdx new file mode 100644 index 000000000..48b231705 --- /dev/null +++ b/surfsense_web/content/docs/how-to/ollama.mdx @@ -0,0 +1,90 @@ +--- +title: Connect Ollama +description: Simple setup guide for using Ollama with SurfSense across local, Docker, remote, and cloud setups +--- + +# Connect Ollama + +Use this page to choose the correct **API Base URL** when adding an Ollama provider in SurfSense. + +## 1) Pick your API Base URL + +| Ollama location | SurfSense location | API Base URL | +|---|---|---| +| Same machine | No Docker | `http://localhost:11434` | +| Host machine (macOS/Windows) | Docker Desktop | `http://host.docker.internal:11434` | +| Host machine (Linux) | Docker Compose | `http://host.docker.internal:11434` | +| Same Docker Compose stack | Docker Compose | `http://ollama:11434` | +| Another machine in your network | Any | `http://:11434` | +| Public Ollama endpoint / proxy / cloud | Any | `http(s)://` | + +If SurfSense runs in Docker, do not use `localhost` unless Ollama is in the same container. + +## 2) Add Ollama in SurfSense + +Go to **Search Space Settings -> Agent Models -> Add Model** and set: + +- Provider: `OLLAMA` +- Model name: your model tag, for example `llama3.2` or `qwen3:8b` +- API Base URL: from the table above +- API key: + - local/self-hosted Ollama: any non-empty value + - Ollama cloud/proxied auth: real key or token required by that endpoint + +Save. SurfSense validates the connection immediately. + +## 3) Common setups + +### A) SurfSense in Docker Desktop, Ollama on your host + +Use: + +```text +http://host.docker.internal:11434 +``` + +### B) Ollama as a service in the same Compose + +Use API Base URL: + +```text +http://ollama:11434 +``` + +Minimal service example: + +```yaml +ollama: + image: ollama/ollama:latest + volumes: + - ollama_data:/root/.ollama + ports: + - "11434:11434" +``` + +### C) Ollama on another machine + +Ollama binds to `127.0.0.1` by default. Make it reachable on the network: + +- Set `OLLAMA_HOST=0.0.0.0:11434` on the machine/service running Ollama +- Open firewall port `11434` +- Use `http://:11434` in SurfSense's API Base URL + +## 4) Quick troubleshooting + +| Error | Cause | Fix | +|---|---|---| +| `Cannot connect to host localhost:11434` | Wrong URL from Dockerized backend | Use `host.docker.internal` or `ollama` | +| `Cannot connect to host :11434` | Ollama not exposed on network or firewall blocked | Set `OLLAMA_HOST=0.0.0.0:11434`, allow port 11434 | +| URL starts with `/%20http://...` | Leading space in URL | Re-enter API Base URL without spaces | +| `model not found` | Model not pulled on Ollama | Run `ollama pull ` | + +If needed, test from the backend container using the same host you put in **API Base URL**: + +```bash +docker compose exec backend curl -v /api/tags +``` + +## See also + +- [Docker Installation](/docs/docker-installation/docker-compose) \ No newline at end of file