diff --git a/.env.template b/.env.template index ed456e91..20dcae82 100644 --- a/.env.template +++ b/.env.template @@ -32,7 +32,9 @@ LOG_LEVEL=INFO # ============================================================================= # Connection URI for PostgreSQL database with pgvector support # Must use postgresql+psycopg prefix for SQLAlchemy compatibility -DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@localhost:5432/postgres +# Keep localhost for host-based runs; docker-compose.yml.example overrides the +# hostname to the "database" service inside containers. +DB_CONNECTION_URI=postgresql+psycopg://testuser:testpwd@localhost:5432/honcho # Optional database settings # DB_SCHEMA=public @@ -57,16 +59,19 @@ AUTH_USE_AUTH=false # AUTH_JWT_SECRET=your-secret-key-here # ============================================================================= -# LLM API Keys (REQUIRED for full functionality) +# LLM API Keys # ============================================================================= +# The shipped defaults need Anthropic and Gemini for startup. OpenAI is also +# needed if you keep the default embedding behavior +# (EMBED_MESSAGES=true and LLM_EMBEDDING_PROVIDER=openai). # OpenAI API key for embeddings LLM_OPENAI_API_KEY=your-openai-api-key-here -# Anthropic API key for dialectic and deriver functionality +# Anthropic API key for dialectic functionality LLM_ANTHROPIC_API_KEY=your-anthropic-api-key-here -# Google API key for summarization (if using Gemini) -# LLM_GEMINI_API_KEY=your-google-api-key-here +# Google API key for summary/deriver and Gemini-backed reasoning +LLM_GEMINI_API_KEY=your-google-api-key-here # Groq API key for query generation (if using Groq) # LLM_GROQ_API_KEY=your-groq-api-key-here diff --git a/README.md b/README.md index 1be34c29..e3d053a8 100644 --- a/README.md +++ b/README.md @@ -216,20 +216,30 @@ Honcho uses a `.env` file for managing runtime environment variables. A are not required and are only necessary for additional logging, monitoring, and security. -Below are the required configurations: +Below are the required configurations for the shipped defaults: ```env DB_CONNECTION_URI= # Connection uri for a postgres database (with postgresql+psycopg prefix) -# LLM Provider API Keys (at least one required depending on your configuration) -LLM_ANTHROPIC_API_KEY= # API Key for Anthropic (used for dialectic by default) -LLM_OPENAI_API_KEY= # API Key for OpenAI (optional, for embeddings if EMBED_MESSAGES=true) -LLM_GEMINI_API_KEY= # API Key for Google Gemini (used for summary/deriver by default) +# LLM Provider API Keys +LLM_ANTHROPIC_API_KEY= # Required to boot with default dialectic settings +LLM_GEMINI_API_KEY= # Required to boot with default summary/deriver and dialectic minimal/low settings +LLM_OPENAI_API_KEY= # Required if you keep the default embedding behavior (EMBED_MESSAGES=true and LLM_EMBEDDING_PROVIDER=openai) LLM_GROQ_API_KEY= # API Key for Groq (used for query generation by default) ``` > Note that the `DB_CONNECTION_URI` must have the prefix `postgresql+psycopg` to > function properly. This is a requirement brought by `sqlalchemy` +> +> If you change the default providers in `config.toml` or via environment +> variables, the required LLM keys change with them. +> +> Current limitation: embeddings have their own provider selector +> (`LLM_EMBEDDING_PROVIDER`), but other LLM features use per-feature +> `PROVIDER`/`MODEL` fields backed by a shared global client pool. In +> particular, `custom` and `vllm` are global OpenAI-compatible connection slots, +> so you cannot point two different features at two different custom endpoints at +> the same time. The template has the additional functionality disabled by default. To ensure that they are disabled you can verify the following environment variables are @@ -266,6 +276,10 @@ uv run alembic upgrade head This will create all tables for Honcho including workspaces, peers, sessions, messages, and the queue system. +> `uv run alembic ...` reads `DB_CONNECTION_URI` (or `[db].CONNECTION_URI` in +> `config.toml`) through Honcho's settings loader. The placeholder URL in +> `alembic.ini` is not the effective source of truth. + 6. **Launch Honcho** With everything set up, you can now launch a local instance of Honcho. In addition to the database, two @@ -347,8 +361,10 @@ As mentioned earlier a `docker-compose` template is included for running Honcho. As an alternative to running Honcho locally it can also be run with the compose template. -The docker-compose template is set to use an environment file called `.env`. -You can also copy the `.env.template` and fill with the appropriate values. +The self-hosting setup treats `.env.template` as the canonical source of truth +for local/manual defaults. `config.toml.example` mirrors the same settings in +TOML form. The compose example keeps those defaults and only overrides +container-local details such as service hostnames. Copy the template and update the appropriate environment variables before launching the service: @@ -356,11 +372,19 @@ launching the service: ```bash cd honcho cp .env.template .env -# update the file with openai key and other wanted environment variables +# keep DB_CONNECTION_URI pointed at localhost; docker-compose overrides the host +# to the internal "database" service automatically +# update the file with LLM keys and any other wanted environment variables cp docker-compose.yml.example docker-compose.yml docker compose up ``` +Once the stack is up, you can verify the API with: + +```bash +curl http://localhost:8000/openapi.json +``` + ### Deploy on Fly The API can also be deployed on fly.io. Follow the [Fly.io @@ -390,6 +414,9 @@ Honcho uses a flexible configuration system that supports both TOML files and en 3. `config.toml` file 4. Default values +`.env.template` is the canonical setup template. `config.toml.example` is the +equivalent TOML transcription of that template, not a separate set of defaults. + ### Using config.toml Copy the example configuration file to get started: @@ -398,7 +425,9 @@ Copy the example configuration file to get started: cp config.toml.example config.toml ``` -Then modify the values as needed. The TOML file is organized into sections: +Then modify the values as needed. Use `.env.template` as the source of truth +for placeholder values and required-vs-optional settings; `config.toml.example` +mirrors those same values in TOML form. The TOML file is organized into sections: - `[app]` - Application-level settings (log level, session limits, embedding settings, namespace) - `[db]` - Database connection and pool settings @@ -434,6 +463,14 @@ Examples: - `METRICS_ENABLED` - Enable Prometheus metrics - `TELEMETRY_ENABLED` - Enable CloudEvents telemetry +Canonical mappings used throughout the self-hosting docs: + +- `DB_CONNECTION_URI` ↔ `[db].CONNECTION_URI` +- `CACHE_ENABLED` ↔ `[cache].ENABLED` +- `CACHE_URL` ↔ `[cache].URL` +- `METRICS_ENABLED` ↔ `[metrics].ENABLED` +- `LLM_*` env vars ↔ `[llm]` keys + ### Configuration Priority When a configuration value is set in multiple places, Honcho uses this priority: @@ -455,14 +492,14 @@ If you have this in `config.toml`: ```toml [db] -CONNECTION_URI = "postgresql://localhost/honcho_dev" +CONNECTION_URI = "postgresql+psycopg://testuser:testpwd@localhost:5432/honcho" POOL_SIZE = 10 ``` You can override just the connection URI in production: ```bash -export DB_CONNECTION_URI="postgresql://prod-server/honcho_prod" +export DB_CONNECTION_URI="postgresql+psycopg://prod-server/honcho_prod" ``` The application will use the production connection URI while keeping the pool size from config.toml. diff --git a/config.toml.example b/config.toml.example index b6b407dc..34f9921a 100644 --- a/config.toml.example +++ b/config.toml.example @@ -22,7 +22,9 @@ NAMESPACE = "honcho" # Database settings [db] -CONNECTION_URI = "postgresql+psycopg://postgres:postgres@localhost:5432/postgres" +# Keep localhost for host-based runs; docker-compose.yml.example overrides the +# hostname to the "database" service inside containers. +CONNECTION_URI = "postgresql+psycopg://testuser:testpwd@localhost:5432/honcho" SCHEMA = "public" POOL_CLASS = "default" POOL_PRE_PING = true @@ -37,13 +39,13 @@ TRACING = false # Authentication settings [auth] USE_AUTH = false -JWT_SECRET = "your-secret-key-here" # Must be set if USE_AUTH is true +# JWT_SECRET = "your-secret-key-here" # Must be set if USE_AUTH is true # Sentry settings [sentry] ENABLED = false -DSN = "" -RELEASE = "" +# DSN = "your-sentry-dsn-here" +# RELEASE = "your-release-semver" ENVIRONMENT = "development" TRACES_SAMPLE_RATE = 0.1 PROFILES_SAMPLE_RATE = 0.1 @@ -56,6 +58,11 @@ MAX_TOOL_OUTPUT_CHARS = 10000 # Max chars for tool output (~2500 tokens) MAX_MESSAGE_CONTENT_CHARS = 2000 # Max chars per message in tool results # API Keys for LLM providers +# Required to boot with the shipped defaults: +# - ANTHROPIC_API_KEY for anthropic-backed dialectic levels +# - GEMINI_API_KEY for summary/deriver and Gemini-backed reasoning levels +# OPENAI_API_KEY is additionally required if you keep the default embedding +# behavior (EMBED_MESSAGES=true and EMBEDDING_PROVIDER="openai"). # ANTHROPIC_API_KEY = "your-api-key" # OPENAI_API_KEY = "your-api-key" # OPENAI_COMPATIBLE_API_KEY = "your-api-key" @@ -67,6 +74,11 @@ MAX_MESSAGE_CONTENT_CHARS = 2000 # Max chars per message in tool results # VLLM_API_KEY = "your-api-key" # VLLM_BASE_URL = "your-base-url" +# Current limitation: non-embedding LLM features choose PROVIDER/MODEL in their +# own sections, but "custom" and "vllm" connection settings come from these +# shared [llm] keys. You cannot configure two different custom OpenAI-compatible +# endpoints for two different features at the same time. + # Deriver settings [deriver] ENABLED = true @@ -121,6 +133,7 @@ MODEL = "claude-haiku-4-5" THINKING_BUDGET_TOKENS = 1024 MAX_TOOL_ITERATIONS = 2 # MAX_OUTPUT_TOKENS = 8192 # Optional: override global default +# TOOL_CHOICE = "any" # Optional: null/auto, "any", or "required" [dialectic.levels.high] PROVIDER = "anthropic" @@ -185,7 +198,7 @@ INCLUDE_LEVELS = ["explicit", "deductive"] # Webhook settings [webhook] -SECRET = "" +# SECRET = "your-webhook-signing-secret" MAX_WORKSPACE_LIMIT = 10 # Prometheus metrics settings (pull-based metrics) @@ -219,7 +232,7 @@ DEFAULT_LOCK_TTL_SECONDS = 5 TYPE = "pgvector" # Migration flag: set to true when migration from pgvector is complete MIGRATED = false -NAMESPACE = "honcho" +# NAMESPACE = "honcho" # Inherits from app.NAMESPACE if not set DIMENSIONS = 1536 # TURBOPUFFER_API_KEY = "your-turbopuffer-api-key" # TURBOPUFFER_REGION = "us-east-1" diff --git a/docker-compose.yml.example b/docker-compose.yml.example index 2ce42306..5a9e19b0 100644 --- a/docker-compose.yml.example +++ b/docker-compose.yml.example @@ -15,9 +15,15 @@ services: volumes: - .:/app - venv:/app/.venv + # .env.template remains the canonical host/manual configuration. + # Compose overrides service-local addresses and enables stack-local features. environment: - - DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres + - DB_CONNECTION_URI=postgresql+psycopg://testuser:testpwd@database:5432/honcho + - CACHE_ENABLED=true - CACHE_URL=redis://redis:6379/0?suppress=true + - METRICS_ENABLED=true + # Provide required LLM_* keys through .env or the shell environment when + # using the default provider configuration. env_file: - path: .env required: false @@ -35,9 +41,12 @@ services: - .:/app - venv:/app/.venv environment: - - DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres + - DB_CONNECTION_URI=postgresql+psycopg://testuser:testpwd@database:5432/honcho + - CACHE_ENABLED=true - CACHE_URL=redis://redis:6379/0?suppress=true - METRICS_ENABLED=true + # Provide required LLM_* keys through .env or the shell environment when + # using the default provider configuration. env_file: - path: .env required: false @@ -48,16 +57,16 @@ services: - 5432:5432 command: ["postgres", "-c", "max_connections=800"] environment: - - POSTGRES_DB=postgres - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=postgres + - POSTGRES_DB=honcho + - POSTGRES_USER=testuser + - POSTGRES_PASSWORD=testpwd - POSTGRES_HOST_AUTH_METHOD=trust - PGDATA=/var/lib/postgresql/data/pgdata volumes: - ./database/init.sql:/docker-entrypoint-initdb.d/init.sql - pgdata:/var/lib/postgresql/data/ healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"] + test: ["CMD-SHELL", "pg_isready -U testuser -d honcho"] interval: 5s timeout: 5s retries: 5 diff --git a/docs/v3/contributing/configuration.mdx b/docs/v3/contributing/configuration.mdx index 57b77e40..fe776aff 100644 --- a/docs/v3/contributing/configuration.mdx +++ b/docs/v3/contributing/configuration.mdx @@ -11,6 +11,14 @@ Honcho uses a flexible configuration system that supports both TOML files and en 3. `config.toml` file (base configuration) 4. Default values +`.env.template` is the canonical source of truth for local/self-hosted defaults. +`config.toml.example` mirrors the same values in TOML form. +The shipped defaults currently require `LLM_ANTHROPIC_API_KEY` and +`LLM_GEMINI_API_KEY` to boot. `LLM_OPENAI_API_KEY` is also required if you keep +the default embedding behavior (`EMBED_MESSAGES=true` and +`LLM_EMBEDDING_PROVIDER=openai`). If you change provider settings, those key +requirements change with them. + ## Recommended Configuration Approaches ### Option 1: Environment Variables Only (Production) @@ -44,7 +52,9 @@ Copy the example configuration file to get started: cp config.toml.example config.toml ``` -Then modify the values as needed. The TOML file is organized into sections: +Then modify the values as needed. Use `.env.template` for the canonical default +values and placeholders; `config.toml.example` mirrors those same settings in +TOML form. The TOML file is organized into sections: - `[app]` - Application-level settings (log level, session limits, embedding settings, Langfuse integration, local metrics collection, namespace) - `[db]` - Database connection and pool settings (connection URI, pool size, timeouts, connection recycling) @@ -80,6 +90,14 @@ Examples: - `DIALECTIC_LEVELS__minimal__PROVIDER` → `[dialectic.levels.minimal].PROVIDER` - `DREAM_SURPRISAL__ENABLED` → `[dream.surprisal].ENABLED` +Canonical mappings used throughout the self-hosting docs: + +- `DB_CONNECTION_URI` ↔ `[db].CONNECTION_URI` +- `CACHE_ENABLED` ↔ `[cache].ENABLED` +- `CACHE_URL` ↔ `[cache].URL` +- `METRICS_ENABLED` ↔ `[metrics].ENABLED` +- `LLM_*` env vars ↔ `[llm]` keys + ### Configuration Priority When a configuration value is set in multiple places, Honcho uses this priority: @@ -101,14 +119,14 @@ If you have this in `config.toml`: ```toml [db] -CONNECTION_URI = "postgresql://localhost/honcho_dev" +CONNECTION_URI = "postgresql+psycopg://testuser:testpwd@localhost:5432/honcho" POOL_SIZE = 10 ``` You can override just the connection URI in production: ```bash -export DB_CONNECTION_URI="postgresql://prod-server/honcho_prod" +export DB_CONNECTION_URI="postgresql+psycopg://prod-server/honcho_prod" ``` The application will use the production connection URI while keeping the pool size from config.toml. @@ -161,7 +179,7 @@ REASONING_TRACES_FILE=traces.jsonl DB_CONNECTION_URI=postgresql+psycopg://username:password@host:port/database # Example for local development -DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@localhost:5432/honcho +DB_CONNECTION_URI=postgresql+psycopg://testuser:testpwd@localhost:5432/honcho # Example for production DB_CONNECTION_URI=postgresql+psycopg://honcho_user:secure_password@db.example.com:5432/honcho_prod @@ -190,19 +208,23 @@ services: database: image: pgvector/pgvector:pg15 environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres + POSTGRES_USER: testuser + POSTGRES_PASSWORD: testpwd POSTGRES_DB: honcho ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data - - ./init.sql:/docker-entrypoint-initdb.d/init.sql + - ./database/init.sql:/docker-entrypoint-initdb.d/init.sql volumes: postgres_data: ``` +When you use `docker-compose.yml.example`, keep `.env`/`config.toml` on the +host-style `localhost` URI. Compose overrides only the hostname to `database` +inside containers and enables stack-local caching/metrics there. + ### Authentication Configuration **JWT Authentication:** @@ -290,6 +312,41 @@ LLM_MAX_TOOL_OUTPUT_CHARS=10000 # ~2500 tokens at 4 chars/token LLM_MAX_MESSAGE_CONTENT_CHARS=2000 # Max chars per message in tool results ``` +### Current Provider Resolution Model + +Honcho currently has two different configuration paths for LLM usage: + +- Embeddings are configured through `[llm]` with `LLM_EMBEDDING_PROVIDER`. +- Other LLM-backed features such as `summary`, `deriver`, `dream`, and + `dialectic` choose `PROVIDER` and `MODEL` in their own sections. + +This means the current behavior is: + +- `LLM_EMBEDDING_PROVIDER=openai` uses `LLM_OPENAI_API_KEY` +- `LLM_EMBEDDING_PROVIDER=gemini` uses `LLM_GEMINI_API_KEY` +- `LLM_EMBEDDING_PROVIDER=openrouter` uses + `LLM_OPENAI_COMPATIBLE_API_KEY` and `LLM_OPENAI_COMPATIBLE_BASE_URL` + (defaulting to OpenRouter's API URL if the base URL is omitted) +- `SUMMARY.PROVIDER`, `DERIVER.PROVIDER`, `DREAM.PROVIDER`, and each + `DIALECTIC.LEVELS.*.PROVIDER` select from the shared provider pool built from + `[llm]` + +There is an important current limitation: + +- `custom` and `vllm` in feature-specific `PROVIDER` fields are not separate + per-feature providers. They are global OpenAI-compatible client slots backed + by one shared `[llm]` configuration each. +- As a result, you cannot configure two different custom OpenAI-compatible + endpoints for two different features at the same time. +- Similarly, if embeddings use `openrouter`, they consume the same + `LLM_OPENAI_COMPATIBLE_*` settings that a feature using `PROVIDER="custom"` + would use. + +`SupportedProviders` therefore represents a mix of provider identities +(`anthropic`, `google`, `openai`, `groq`) and transport/client styles +(`custom`, `vllm`). That is the current implementation contract, even though it +is more constrained than a fully per-feature provider configuration model. + ### Feature-Specific Model Configuration Different features can use different providers and models: @@ -615,7 +672,7 @@ EMBED_MESSAGES = false NAMESPACE = "honcho-dev" [db] -CONNECTION_URI = "postgresql+psycopg://postgres:postgres@localhost:5432/honcho_dev" +CONNECTION_URI = "postgresql+psycopg://testuser:testpwd@localhost:5432/honcho" POOL_SIZE = 5 [auth] @@ -698,7 +755,7 @@ ENABLED = false ```bash # .env.development LOG_LEVEL=DEBUG -DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@localhost:5432/honcho_dev +DB_CONNECTION_URI=postgresql+psycopg://testuser:testpwd@localhost:5432/honcho AUTH_USE_AUTH=false CACHE_ENABLED=false @@ -851,6 +908,10 @@ uv run alembic downgrade revision_id uv run alembic revision --autogenerate -m "Description of changes" ``` +Alembic uses Honcho's application settings for the database URL. Set +`DB_CONNECTION_URI` or `[db].CONNECTION_URI`; do not rely on the placeholder +value in `alembic.ini`. + ## Troubleshooting **Common Configuration Issues:** diff --git a/docs/v3/contributing/self-hosting.mdx b/docs/v3/contributing/self-hosting.mdx index b6f47e94..b7bed620 100644 --- a/docs/v3/contributing/self-hosting.mdx +++ b/docs/v3/contributing/self-hosting.mdx @@ -51,15 +51,24 @@ Copy the example environment file and configure it: cp .env.template .env ``` -Edit `.env` and set your API keys (if using LLM features): +`.env.template` is the canonical self-hosting template. If you prefer TOML, you +can also copy `config.toml.example` to `config.toml`; it mirrors the same +settings in TOML form. + +Edit `.env` and set the API keys required by the shipped defaults: ```bash -# Optional API keys (required for LLM features) -OPENAI_API_KEY=your-openai-api-key -ANTHROPIC_API_KEY=your-anthropic-api-key +# Required to boot with the shipped defaults +LLM_ANTHROPIC_API_KEY=your-anthropic-api-key +LLM_GEMINI_API_KEY=your-google-api-key -# Database will be created automatically by Docker -DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/honcho +# Also required if you keep default embeddings enabled +LLM_OPENAI_API_KEY=your-openai-api-key + +# Keep localhost here for host/manual runs. +# docker-compose.yml.example overrides the hostname to the internal "database" +# service automatically, so you do not need to edit this value for Docker. +DB_CONNECTION_URI=postgresql+psycopg://testuser:testpwd@localhost:5432/honcho # Disable auth for local development AUTH_USE_AUTH=false @@ -86,10 +95,19 @@ docker compose ps Test the Honcho API: ```bash -curl http://localhost:8000/health +curl http://localhost:8000/openapi.json ``` -You should see a response indicating the service is healthy. +You should see the OpenAPI document for the running server. + +If you change the default providers in `config.toml` or via environment +variables, the required LLM keys change with them. + +Current limitation: embeddings use `LLM_EMBEDDING_PROVIDER`, but other LLM +features use per-feature `PROVIDER`/`MODEL` values backed by shared global +client settings in `[llm]`. That means `custom` and `vllm` each represent one +global OpenAI-compatible endpoint at a time, not a separate endpoint per +feature. ## Manual Setup @@ -135,23 +153,21 @@ Download from [postgresql.org](https://www.postgresql.org/download/windows/) ```bash docker run --name honcho-db \ -e POSTGRES_DB=honcho \ - -e POSTGRES_USER=postgres \ - -e POSTGRES_PASSWORD=postgres \ + -e POSTGRES_USER=testuser \ + -e POSTGRES_PASSWORD=testpwd \ -p 5432:5432 \ -d pgvector/pgvector:pg15 ``` ### 3. Create Database and Enable Extensions -Connect to PostgreSQL and set up the database: +If you used the Docker command above, the `honcho` database and `testuser` +role are created automatically. For a local PostgreSQL installation, create a +matching role/database first or update `.env.template` and `config.toml` +consistently. Then connect and enable extensions: ```bash -# Connect to PostgreSQL -psql -U postgres - -# Create database and enable extensions -CREATE DATABASE honcho; -\c honcho +PGPASSWORD=testpwd psql -h localhost -p 5432 -U testuser -d honcho CREATE EXTENSION IF NOT EXISTS vector; CREATE EXTENSION IF NOT EXISTS pg_trgm; \q @@ -165,15 +181,21 @@ Create a `.env` file with your settings: cp .env.template .env ``` +You can alternatively copy `config.toml.example` to `config.toml`; it mirrors +the same settings as `.env.template`. + Edit `.env` with your configuration: ```bash # Database connection -DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@localhost:5432/honcho +DB_CONNECTION_URI=postgresql+psycopg://testuser:testpwd@localhost:5432/honcho -# Optional API keys (required for LLM features) -OPENAI_API_KEY=your-openai-api-key -ANTHROPIC_API_KEY=your-anthropic-api-key +# Required to boot with the shipped defaults +LLM_ANTHROPIC_API_KEY=your-anthropic-api-key +LLM_GEMINI_API_KEY=your-google-api-key + +# Also required if you keep default embeddings enabled +LLM_OPENAI_API_KEY=your-openai-api-key # Development settings AUTH_USE_AUTH=false @@ -187,6 +209,10 @@ LOG_LEVEL=DEBUG uv run alembic upgrade head ``` +> Honcho's Alembic environment reads `DB_CONNECTION_URI` or +> `[db].CONNECTION_URI` from the application config. `alembic.ini` is not the +> effective source of truth for the database URL. + ### 6. Start the Server ```bash @@ -209,7 +235,8 @@ If you prefer to use a managed PostgreSQL service: CREATE EXTENSION IF NOT EXISTS pg_trgm; ``` 3. **Get your connection string** from Settings > Database -4. **Update your `.env` file** with the connection string +4. **Update your `.env` file** with the connection string or set the equivalent + `[db].CONNECTION_URI` value in `config.toml` ### Railway @@ -226,7 +253,7 @@ Once your Honcho server is running, verify everything is working: ### 1. Health Check ```bash -curl http://localhost:8000/health +curl http://localhost:8000/openapi.json ``` ### 2. API Documentation diff --git a/src/config.py b/src/config.py index f01c451b..78d5886d 100644 --- a/src/config.py +++ b/src/config.py @@ -16,9 +16,9 @@ from pydantic_settings import ( from src.utils.types import SupportedProviders -# Load .env file for local development. -# Make sure this is called before AppSettings is instantiated if you rely on .env for AppSettings construction. -load_dotenv(override=True) +# Load .env file for local development without overriding explicit environment +# variables from the process (for example Docker Compose container overrides). +load_dotenv(override=False) logger = logging.getLogger(__name__) @@ -156,7 +156,7 @@ class DBSettings(HonchoSettings): model_config = SettingsConfigDict(env_prefix="DB_", extra="ignore") # pyright: ignore CONNECTION_URI: str = ( - "postgresql+psycopg://postgres:postgres@localhost:5432/postgres" + "postgresql+psycopg://testuser:testpwd@localhost:5432/honcho" ) SCHEMA: str = "public" POOL_CLASS: str = "default" diff --git a/tests/setup_examples/conftest.py b/tests/setup_examples/conftest.py new file mode 100644 index 00000000..499a087b --- /dev/null +++ b/tests/setup_examples/conftest.py @@ -0,0 +1,7 @@ +import pytest + + +@pytest.fixture(autouse=True) +def mock_tracked_db(): + """Disable the DB-heavy tracked_db autouse fixture for setup example tests.""" + yield diff --git a/tests/setup_examples/test_setup_examples.py b/tests/setup_examples/test_setup_examples.py new file mode 100644 index 00000000..60cb4a11 --- /dev/null +++ b/tests/setup_examples/test_setup_examples.py @@ -0,0 +1,116 @@ +import json +import os +import subprocess +import sys +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] + + +def _load_db_connection_uri(tmp_path: Path, env_overrides: dict[str, str]) -> str: + env = os.environ.copy() + env["PYTHONPATH"] = str(REPO_ROOT) + env.pop("DB_CONNECTION_URI", None) + env.update(env_overrides) + + result = subprocess.run( + [ + sys.executable, + "-c", + ( + "from src.config import settings; " + "import json; " + "print(json.dumps({'db_uri': settings.DB.CONNECTION_URI}))" + ), + ], + cwd=tmp_path, + env=env, + capture_output=True, + text=True, + check=True, + ) + return json.loads(result.stdout)["db_uri"] + + +def test_env_var_overrides_dotenv_and_toml(tmp_path: Path) -> None: + (tmp_path / ".env").write_text( + "DB_CONNECTION_URI=postgresql+psycopg://dotenv:dotenv@localhost:5432/dotenv\n" + ) + (tmp_path / "config.toml").write_text( + '[db]\nCONNECTION_URI = "postgresql+psycopg://toml:toml@localhost:5432/toml"\n' + ) + + db_uri = _load_db_connection_uri( + tmp_path, + { + "DB_CONNECTION_URI": "postgresql+psycopg://env:env@localhost:5432/env", + }, + ) + + assert db_uri == "postgresql+psycopg://env:env@localhost:5432/env" + + +def test_dotenv_overrides_toml(tmp_path: Path) -> None: + (tmp_path / ".env").write_text( + "DB_CONNECTION_URI=postgresql+psycopg://dotenv:dotenv@localhost:5432/dotenv\n" + ) + (tmp_path / "config.toml").write_text( + '[db]\nCONNECTION_URI = "postgresql+psycopg://toml:toml@localhost:5432/toml"\n' + ) + + db_uri = _load_db_connection_uri(tmp_path, {}) + + assert db_uri == "postgresql+psycopg://dotenv:dotenv@localhost:5432/dotenv" + + +def test_toml_overrides_builtin_defaults(tmp_path: Path) -> None: + (tmp_path / "config.toml").write_text( + '[db]\nCONNECTION_URI = "postgresql+psycopg://toml:toml@localhost:5432/toml"\n' + ) + + db_uri = _load_db_connection_uri(tmp_path, {}) + + assert db_uri == "postgresql+psycopg://toml:toml@localhost:5432/toml" + + +def test_examples_share_the_canonical_local_database_contract() -> None: + env_template = (REPO_ROOT / ".env.template").read_text() + config_example = (REPO_ROOT / "config.toml.example").read_text() + + assert ( + "DB_CONNECTION_URI=postgresql+psycopg://testuser:testpwd@localhost:5432/honcho" + in env_template + ) + assert ( + 'CONNECTION_URI = "postgresql+psycopg://testuser:testpwd@localhost:5432/honcho"' + in config_example + ) + + +def test_compose_example_matches_the_canonical_contract() -> None: + compose_example = (REPO_ROOT / "docker-compose.yml.example").read_text() + + assert ( + "DB_CONNECTION_URI=postgresql+psycopg://testuser:testpwd@database:5432/honcho" + in compose_example + ) + assert "CACHE_ENABLED=true" in compose_example + assert compose_example.count("METRICS_ENABLED=true") == 2 + assert ( + "./database/init.sql:/docker-entrypoint-initdb.d/init.sql" in compose_example + ) + assert (REPO_ROOT / "database" / "init.sql").exists() + + +def test_examples_surface_default_llm_requirements() -> None: + env_template = (REPO_ROOT / ".env.template").read_text() + config_example = (REPO_ROOT / "config.toml.example").read_text() + compose_example = (REPO_ROOT / "docker-compose.yml.example").read_text() + + assert "LLM_OPENAI_API_KEY=your-openai-api-key-here" in env_template + assert "LLM_ANTHROPIC_API_KEY=your-anthropic-api-key-here" in env_template + assert "LLM_GEMINI_API_KEY=your-google-api-key-here" in env_template + assert "Required to boot with the shipped defaults:" in config_example + assert "two different custom OpenAI-compatible" in config_example + assert "Provide required LLM_* keys" in compose_example