# [[How to set up local LLMs]] ![[How to set up local LLMs.svg]] This guide sets up the following to run on your local machine: - [[Ollama]] (runs local LLMs) - 3 models: - [[Qwen|qwen3:8b]] (general) - [[Qwen|qwen2.5-coder:7b]] (coding) - [[Llama|llama3.2:3b]] (fast + classifier) - A router API (Python + FastAPI) - [[Open WebUI]] (chat UI) --- ## Prerequisites - Install [[Docker Desktop]] and make sure it is running --- ## Set up environment ### Create project folder From your terminal ```bash mkdir -p ~/local-ai-compose cd ~/local-ai-compose ``` ### Create requirements.txt ```bash cat > requirements.txt <<'EOF' fastapi==0.115.12 uvicorn[standard]==0.34.0 requests==2.32.3 EOF ``` ### Create Dockerfile.router ```bash cat > Dockerfile.router <<'EOF' FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY router_server.py . EXPOSE 8000 CMD ["uvicorn", "router_server:app", "--host", "0.0.0.0", "--port", "8000"] EOF ``` ### Create `router_server.py` ```bash cat > router_server.py <<'EOF' import json import os import requests from fastapi import FastAPI from pydantic import BaseModel from typing import List, Optional OLLAMA_URL = os.environ.get("OLLAMA_URL", "http://ollama:11434/api/chat") MODEL_MAP = { "general": "qwen3:8b", "code": "qwen2.5-coder:7b", "fast": "llama3.2:3b", } SYSTEM_PROMPTS = { "general": "You are a helpful assistant.", "code": "You are a senior software engineer. Write correct, runnable code and explain briefly.", "fast": "Summarize or extract information concisely. Be brief and structured.", } CLASSIFIER_MODEL = "llama3.2:3b" app = FastAPI() class Message(BaseModel): role: str content: str class ChatRequest(BaseModel): model: Optional[str] = None messages: List[Message] temperature: Optional[float] = None stream: Optional[bool] = False def ollama_chat(model: str, messages: list[dict], format_schema=None) -> str: payload = { "model": model, "messages": messages, "stream": False, } if format_schema is not None: payload["format"] = format_schema response = requests.post(OLLAMA_URL, json=payload, timeout=300) response.raise_for_status() data = response.json() return data["message"]["content"] def classify(prompt: str) -> str: lowered = prompt.lower() obvious_code_hints = [ "python", "javascript", "typescript", "bash", "shell", "stack trace", "traceback", "refactor", "debug", "write code", "fix this code", "sql", "api", "function", "class", "script", "regex" ] if "```" in prompt or any(hint in lowered for hint in obvious_code_hints): return "code" schema = { "type": "object", "properties": { "route": { "type": "string", "enum": ["general", "code", "fast"] }, "reason": {"type": "string"} }, "required": ["route", "reason"] } system = """Classify the user's request into exactly one of: - general - code - fast Rules: - code: programming, debugging, scripts, APIs, config files - fast: summarization, extraction, classification, short rewriting - general: everything else Return only valid JSON.""" try: raw = ollama_chat( CLASSIFIER_MODEL, [ {"role": "system", "content": system}, {"role": "user", "content": prompt}, ], format_schema=schema, ) parsed = json.loads(raw) route = parsed.get("route", "general") if route not in MODEL_MAP: return "general" return route except Exception: return "general" def ask(prompt: str) -> dict: route = classify(prompt) model = MODEL_MAP[route] content = ollama_chat( model, [ {"role": "system", "content": SYSTEM_PROMPTS[route]}, {"role": "user", "content": prompt}, ], ) return { "route": route, "model": model, "content": content, } @app.get("/v1/models") def list_models(): return { "object": "list", "data": [ { "id": "auto-router", "object": "model", "owned_by": "local", } ], } @app.post("/v1/chat/completions") def chat(req: ChatRequest): user_messages = [m for m in req.messages if m.role == "user"] prompt = user_messages[-1].content if user_messages else req.messages[-1].content result = ask(prompt) return { "id": "chatcmpl-local-router", "object": "chat.completion", "created": 0, "model": "auto-router", "choices": [ { "index": 0, "message": { "role": "assistant", "content": f"[{result['route']} -> {result['model']}]\n\n{result['content']}" }, "finish_reason": "stop", } ], } EOF ``` ### Create `docker-compose.yml` ```bash cat > docker-compose.yml <<'EOF' services: ollama: image: ollama/ollama:latest container_name: ollama ports: - "11434:11434" volumes: - ollama-data:/root/.ollama router: build: context: . dockerfile: Dockerfile.router container_name: local-router depends_on: - ollama environment: OLLAMA_URL: http://ollama:11434/api/chat ports: - "8000:8000" open-webui: image: ghcr.io/open-webui/open-webui:main container_name: open-webui depends_on: - router ports: - "3001:8080" volumes: - open-webui-data:/app/backend/data environment: OPENAI_API_BASE_URL: http://router:8000/v1 OPENAI_API_KEY: anything volumes: ollama-data: open-webui-data: EOF ``` ## Verify setup ### Start everything ```bash docker compose up --build -d ``` ### Check containers ```bash docker compose ps ``` You should see: - ollama - local-router - open-webui ### Download models ```bash docker exec -it ollama ollama pull qwen3:8b ``` ```bash docker exec -it ollama ollama pull qwen2.5-coder:7b ``` ```bash docker exec -it ollama ollama pull llama3.2:3b ``` ### Test router ```bash curl http://localhost:8000/v1/models ``` ## Setting up Open WebUI ### Logging into Open WebUI Go to: ``` http://localhost:3001 ``` Create any account. ### Configure Open WebUI From the UI: 1. Click profile (top right). 2. Go to **Connections**. 3. Click **Add Connection**. 4. Choose **OpenAI-compatible**. Fill in: - Name: `Local Router` - Base URL: `http://host.docker.internal:8000/v1` - API key: `anything` Then hit Save. ### Using Open WebUI Click on **New Chat** on the top left. If it's not already selected for you, select `auto-router` as a model. Try a prompt like: ``` Write a Python script to read a CSV file ``` If you're curious which model that prompt was routed to, check the output of your router server. You should see something like this: ```bash INFO: 127.0.0.1:55689 - "POST /v1/chat/completions HTTP/1.1" 200 OK [classifier] route=general reason=not categorized under specific area [router] route=general model=qwen3:8b INFO: 127.0.0.1:55718 - "POST /v1/chat/completions HTTP/1.1" 200 OK [router] route=code model=qwen2.5-coder:7b INFO: 127.0.0.1:55774 - "POST /v1/chat/completions HTTP/1.1" 200 OK [router] route=code model=qwen2.5-coder:7b INFO: 127.0.0.1:55814 - "POST /v1/chat/completions HTTP/1.1" 200 OK [router] route=code model=qwen2.5-coder:7b INFO: 127.0.0.1:55828 - "POST /v1/chat/completions HTTP/1.1" 200 OK ``` ## Next time ### Start it ```bash cd ~/local-ai-compose docker compose up -d ``` ### Stop it ```bash docker compose down ``` ==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠== You can decompress Drawing data with the command palette: 'Decompress current Excalidraw file'. For more info check in plugin settings under 'Saving' # Excalidraw Data ## Text Elements Open WebUI ^jUmTMGvg router.py ^PnGTfVEQ Ollama ^uBchXPNa qwen 3:8B ^psF2bqjv qwen 2.5-coder:7b ^MCST8eop llama 3.2:3b ^zTd8KrUT localhost:3001 ^YYW8Htx2 localhost:8000/v1 ^S0OUWo6U localhost:11434 ^qiN8jUB3 %% ## Drawing ```compressed-json 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= ``` %%