diff --git a/.env.example b/.env.example index 20c8f98..61f1e7d 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,9 @@ # OpenAI-compatible API configuration LLM_BASE_URL=http://localhost:11434/v1 LLM_MODEL=llama + +# API key for the LLM provider (leave empty for local servers like Ollama) +LLM_API_KEY= + +# Logfire observability (set your token to enable) +LOGFIRE_TOKEN= diff --git a/Makefile b/Makefile index 572bf09..d91de4f 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,11 @@ .PHONY: install run dev clean setup \ - frontend-install frontend-dev frontend-build + frontend-install frontend-dev frontend-build \ + compose-up compose-down compose-build compose-logs compose-rebuild + +# ── Container runtime detection (Docker / Podman) ───────────────────────── +# Uses Docker if available, otherwise falls back to Podman. +DOCKER := $(shell command -v docker 2>/dev/null || command -v podman 2>/dev/null) +DOCKER_COMPOSE := $(shell command -v docker-compose 2>/dev/null || (command -v docker 2>/dev/null && echo "docker compose") || (command -v podman 2>/dev/null && echo "podman compose")) # ── Backend Setup ────────────────────────────────────────────────────────── @@ -32,6 +38,23 @@ frontend-dev: ## Start the Vite dev server frontend-build: ## Build frontend for production cd frontend && bun run build +# ── Containers (Docker / Podman) ─────────────────────────────────────────── + +compose-up: ## Start all containers in detached mode + $(DOCKER_COMPOSE) up -d + +compose-down: ## Stop and remove containers + $(DOCKER_COMPOSE) down + +compose-build: ## Build (or rebuild) all container images + $(DOCKER_COMPOSE) build + +compose-rebuild: ## Rebuild images & restart containers + $(DOCKER_COMPOSE) up -d --build + +compose-logs: ## Follow container logs + $(DOCKER_COMPOSE) logs -f + # ── Utilities ────────────────────────────────────────────────────────────── clean: ## Remove caches and build artifacts diff --git a/backend/core/agent.py b/backend/core/agent.py index 43ebc0b..5d076a8 100644 --- a/backend/core/agent.py +++ b/backend/core/agent.py @@ -32,6 +32,7 @@ class AgentService: def __init__(self, settings: Settings) -> None: self._settings = settings self._agent: Agent | None = None + self._model: OpenAIResponsesModel | None = None # ── Lifecycle ────────────────────────────────────────────────────────── @@ -40,16 +41,23 @@ class AgentService: if self._agent is not None: return - logfire.configure() - logfire.instrument_pydantic_ai() + if self._settings.logfire_token: + try: + logfire.configure(token=self._settings.logfire_token) + logfire.instrument_pydantic_ai() + except Exception: + pass # Logfire is optional — don't crash if it fails - model = OpenAIResponsesModel( + self._model = OpenAIResponsesModel( self._settings.llm_model, - provider=OpenAIProvider(base_url=self._settings.llm_base_url), + provider=OpenAIProvider( + base_url=self._settings.llm_base_url, + api_key=self._settings.llm_api_key or "no-key-required", + ), ) self._agent = Agent( - model, + self._model, capabilities=self._build_capabilities(), ) @@ -69,8 +77,7 @@ class AgentService: # ── Internal helpers ─────────────────────────────────────────────────── - @staticmethod - def _build_capabilities() -> list: + def _build_capabilities(self) -> list: """Assemble the full list of agent capabilities.""" return [ CodeMode(), @@ -89,7 +96,8 @@ class AgentService: description="Deep research on a topic", instructions="You are a thorough research assistant.", ), - ] + ], + default_model=self._model, ), TodoCapability(enable_subtasks=True), CostTracking(budget_usd=5.0), diff --git a/backend/core/config.py b/backend/core/config.py index f09f0b6..60647b8 100644 --- a/backend/core/config.py +++ b/backend/core/config.py @@ -22,6 +22,8 @@ class Settings(BaseSettings): llm_base_url: str = "http://localhost:11434/v1" llm_model: str = "llama" + llm_api_key: str = "" + logfire_token: str = "" # ── Factories ────────────────────────────────────────────────────────── diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 443b4c5..6360c67 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -5,14 +5,22 @@ server { root /usr/share/nginx/html; index index.html; + # Podman/Docker DNS — forces runtime resolution so nginx picks up + # container IP changes after restarts. + resolver 10.89.6.1 valid=10s; + # ── API proxy ───────────────────────────────────────────────── + # Use a variable so nginx resolves DNS at runtime, not at startup. + set $backend_upstream http://backend:8000; location /api/ { - proxy_pass http://backend:8000; + proxy_pass $backend_upstream; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - proxy_read_timeout 120s; + proxy_connect_timeout 30s; + proxy_read_timeout 200s; + proxy_send_timeout 200s; } # ── Static files ──────────────────────────────────────────────