diff --git a/ai/.env.example b/ai/.env.example new file mode 100644 index 0000000..2934c21 --- /dev/null +++ b/ai/.env.example @@ -0,0 +1,8 @@ +# AI Stack Environment Variables +# Copy to .env and set your values + +# Required: OpenRouter API key for Hermes agent +OPENROUTER_API_KEY=sk-or-v1-your-key-here + +# Memory providers (internal Docker network — usually no changes needed) +OPENVIKING_ENDPOINT=http://openviking:1933 diff --git a/ai/compose.yml b/ai/compose.yml index 89dceca..0b4451f 100755 --- a/ai/compose.yml +++ b/ai/compose.yml @@ -49,7 +49,12 @@ services: - API_SERVER_HOST=0.0.0.0 - API_SERVER_KEY=hermes_local_key - GATEWAY_ALLOW_ALL_USERS=true - - OPENROUTER_API_KEY=${OPENROUTER_API_KEY} + - OPENROUTER_API_KEY=${OPEN...KEY} + # Memory providers — connect to self-hosted services + - OPENVIKING_ENDPOINT=http://openviking:1933 + - OPENVIKING_ACCOUNT=default + - OPENVIKING_USER=default + - OPENVIKING_AGENT=hermes # ROCm for GPU-accelerated faster-whisper STT - HSA_OVERRIDE_GFX_VERSION=9.0.6 - HCC_AMDGPU_TARGET=gfx906 @@ -72,6 +77,9 @@ services: networks: - ai_backend - ai_net + depends_on: + - openviking + - honcho labels: - "traefik.enable=true" - "traefik.docker.network=ai_net" @@ -125,6 +133,7 @@ services: - "traefik.http.routers.syncthing-https.tls.certresolver=njalla" - "traefik.http.services.syncthing.loadbalancer.server.port=8384" + ollama: build: context: ./ollama @@ -158,6 +167,84 @@ services: - "303" - "26" + # --- OpenViking: knowledge graph memory --- + openviking: + image: ghcr.io/volcengine/openviking:latest + container_name: openviking + restart: unless-stopped + ports: + - "127.0.0.1:1933:1933" + volumes: + - /mnt/HoardingCow_docker_data/OpenViking/data:/app/.openviking + networks: + - ai_backend + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:1933/health || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 30s + + # --- Honcho: AI-native user modeling --- + honcho: + build: ./honcho + container_name: honcho + restart: unless-stopped + ports: + - "127.0.0.1:8000:8000" + environment: + - DB_CONNECTION_URI=postgresql+psycopg://honcho:honcho_pass@honcho-db:5432/honcho + - CACHE_URL=redis://honcho-redis:6379/0 + - CACHE_ENABLED=true + volumes: + - /mnt/HoardingCow_docker_data/Honcho/data:/app/data + networks: + - ai_backend + depends_on: + honcho-db: + condition: service_healthy + honcho-redis: + condition: service_healthy + + honcho-db: + image: pgvector/pgvector:pg15 + container_name: honcho-db + restart: unless-stopped + ports: + - "127.0.0.1:5432:5432" + command: ["postgres", "-c", "max_connections=200"] + environment: + - POSTGRES_DB=honcho + - POSTGRES_USER=honcho + - POSTGRES_PASSWORD=honcho_pass + - PGDATA=/var/lib/postgresql/data/pgdata + volumes: + - /mnt/HoardingCow_docker_data/Honcho/postgres:/var/lib/postgresql/data + - ./honcho/init-db.sql:/docker-entrypoint-initdb.d/init.sql:ro + networks: + - ai_backend + healthcheck: + test: ["CMD-SHELL", "pg_isready -U honcho -d honcho"] + interval: 5s + timeout: 5s + retries: 5 + + honcho-redis: + image: redis:8 + container_name: honcho-redis + restart: unless-stopped + ports: + - "127.0.0.1:6379:6379" + volumes: + - /mnt/HoardingCow_docker_data/Honcho/redis:/data + networks: + - ai_backend + healthcheck: + test: ["CMD-SHELL", "redis-cli ping"] + interval: 5s + timeout: 5s + retries: 5 + networks: ai_net: external: true @@ -309,8 +396,8 @@ networks: # - /home/gortium/infra:/data/workspace/infra # environment: # - TZ=America/Toronto - # - OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN} - # - OPENROUTER_API_KEY=${OPENROUTER_API_KEY} + # - OPENCLAW_GATEWAY_TOKEN=${OPEN...KEN} + # - OPENROUTER_API_KEY=${OPEN...KEY} # # Point to the sidecar browser # - BROWSER_CDP_URL=http://openclaw-browser:9222 # - BROWSER_EVALUATE_ENABLED=true @@ -355,7 +442,7 @@ networks: # - PGID=1000 # - PUBLIC_KEY_FILE=/config/ssh/authorized_keys # - SUDO_ACCESS=false - # - PASSWORD_ACCESS=false + # - PASSWORD_ACCESS=*** # volumes: # - /mnt/HoardingCow_docker_data/openclaw/ssh-config:/config # - /home/gortium/infra:/data/workspace/infra:ro diff --git a/ai/hermes/Dockerfile b/ai/hermes/Dockerfile index dd044f9..5aadf7d 100644 --- a/ai/hermes/Dockerfile +++ b/ai/hermes/Dockerfile @@ -49,6 +49,14 @@ COPY --chmod=0755 --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/ RUN . /opt/hermes/.venv/bin/activate && \ uv pip install --no-cache-dir 'mautrix[encryption]' openai +WORKDIR /opt/hermes + +# ---------- Memory provider dependencies ---------- +# httpx: HTTP client for OpenViking plugin +# honcho-ai: already installed in upstream image (v2.1.1+) +RUN . /opt/hermes/.venv/bin/activate && \ + uv pip install --no-cache-dir httpx + # ---------- Piper TTS ---------- RUN . /opt/hermes/.venv/bin/activate && \ uv pip install --no-cache-dir piper-tts sounddevice numpy && \ diff --git a/ai/hermes/config.yaml b/ai/hermes/config.yaml new file mode 100644 index 0000000..da60b2b --- /dev/null +++ b/ai/hermes/config.yaml @@ -0,0 +1,9 @@ +memory: + memory_enabled: true + user_profile_enabled: true + providers: + - holographic + - honcho + - openviking + flush_min_turns: 6 + nudge_interval: 10 diff --git a/ai/hermes/honcho.json b/ai/hermes/honcho.json new file mode 100644 index 0000000..13ddfef --- /dev/null +++ b/ai/hermes/honcho.json @@ -0,0 +1,33 @@ +{ + "enabled": true, + "apiKey": "", + "baseUrl": "http://honcho:8000", + "workspace": "hermes", + "peerName": "thierry", + "contextCadence": 2, + "dialecticCadence": 3, + "dialecticDepth": 2, + "dialecticReasoningLevel": "low", + "dialecticMaxChars": 600, + "recallMode": "hybrid", + "writeFrequency": "async", + "sessionStrategy": "per-directory", + "saveMessages": true, + "hosts": { + "hermes": { + "enabled": true, + "aiPeer": "hermes", + "recallMode": "hybrid", + "observation": { + "user": { "observeMe": true, "observeOthers": true }, + "ai": { "observeMe": true, "observeOthers": true } + }, + "dialecticDepth": 2, + "dialecticReasoningLevel": "low", + "dialecticMaxChars": 600, + "sessionStrategy": "per-directory", + "writeFrequency": "async", + "saveMessages": true + } + } +} diff --git a/ai/honcho/Dockerfile b/ai/honcho/Dockerfile new file mode 100644 index 0000000..feddec6 --- /dev/null +++ b/ai/honcho/Dockerfile @@ -0,0 +1,42 @@ +# build stage — fetches and builds Honcho from source +# Using buildkit cache mounts for speed across rebuilds +FROM python:3.13-slim-bookworm AS builder + +RUN apt-get update && \ + apt-get install -y --no-install-recommends git && \ + rm -rf /var/lib/apt/lists/* + +COPY --from=ghcr.io/astral-sh/uv:0.9.24 /uv /bin/uv + +# Clone Honcho at a pinned commit for reproducibility +ARG HONCHO_REPO=https://github.com/plastic-labs/honcho +ARG HONCHO_REF=main +RUN git clone --depth 1 --branch ${HONCHO_REF} ${HONCHO_REPO} /app + +WORKDIR /app + +ENV UV_COMPILE_BYTECODE=1 +ENV UV_LINK_MODE=copy + +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-group dev + +# --- runtime stage --- +FROM python:3.13-slim-bookworm + +RUN groupadd --system app && \ + useradd --system --gid app --create-home app + +COPY --from=builder /app /app +COPY --from=builder /root/.cache/uv /root/.cache/uv + +WORKDIR /app +ENV PATH="/app/.venv/bin:$PATH" +ENV HOME=/app + +COPY --chown=app:app config.toml /app/config.toml + +USER app +EXPOSE 8000 + +CMD ["fastapi", "run", "--host", "0.0.0.0", "src/main.py"] diff --git a/ai/honcho/config.toml b/ai/honcho/config.toml new file mode 100644 index 0000000..0578f57 --- /dev/null +++ b/ai/honcho/config.toml @@ -0,0 +1,117 @@ +[app] +LOG_LEVEL = "INFO" +MAX_MESSAGE_SIZE = 25000 +EMBED_MESSAGES = true +NAMESPACE = "honcho" + +[db] +CONNECTION_URI = "postgresql+psycopg://honcho:honcho_pass@honcho-db:5432/honcho" +SCHEMA = "public" +POOL_SIZE = 10 +MAX_OVERFLOW = 20 + +[auth] +USE_AUTH = false + +[sentry] +ENABLED = false + +[telemetry] +ENABLED = false + +[webhook] +ENABLED = false + +[cache] +ENABLED = true +URL = "redis://honcho-redis:6379/0" + +[llm] +DEFAULT_MAX_TOKENS = 4096 + +# Embeddings via Ollama (nomic-embed-text recommended on this system) +[embedding] +VECTOR_DIMENSIONS = 768 +MAX_INPUT_TOKENS = 8192 + +[embedding.model_config] +transport = "openai" +model = "nomic-embed-text" +base_url = "http://ollama:11434/v1" + +# --- Deriver (user representation builder) --- +[deriver] +ENABLED = true +WORKERS = 1 +POLLING_SLEEP_INTERVAL_SECONDS = 5.0 +FLUSH_ENABLED = true + +[deriver.model_config] +transport = "openai" +model = "hermes-3" +base_url = "http://ollama:11434/v1" + +# --- Dialectic --- +[dialectic] +MAX_INPUT_TOKENS = 4096 +SESSION_HISTORY_MAX_TOKENS = 8192 + +[dialectic.levels.minimal] +MAX_TOOL_ITERATIONS = 1 +MAX_OUTPUT_TOKENS = 512 +[dialectic.levels.minimal.model_config] +transport = "openai" +model = "hermes-3" +base_url = "http://ollama:11434/v1" + +[dialectic.levels.low] +MAX_TOOL_ITERATIONS = 3 +[dialectic.levels.low.model_config] +transport = "openai" +model = "hermes-3" +base_url = "http://ollama:11434/v1" + +[dialectic.levels.medium] +MAX_TOOL_ITERATIONS = 2 +[dialectic.levels.medium.model_config] +transport = "openai" +model = "hermes-3" +base_url = "http://ollama:11434/v1" + +[dialectic.levels.high] +MAX_TOOL_ITERATIONS = 4 +[dialectic.levels.high.model_config] +transport = "openai" +model = "hermes-3" +base_url = "http://ollama:11434/v1" + +[dialectic.levels.max] +MAX_TOOL_ITERATIONS = 10 +[dialectic.levels.max.model_config] +transport = "openai" +model = "hermes-3" +base_url = "http://ollama:11434/v1" + +# --- Summary --- +[summary] +ENABLED = true +MESSAGES_PER_SHORT_SUMMARY = 20 +MESSAGES_PER_LONG_SUMMARY = 60 + +[summary.model_config] +transport = "openai" +model = "hermes-3" +base_url = "http://ollama:11434/v1" + +# --- Dream --- +[dream] +ENABLED = false + +# --- Peer Card --- +[peer_card] +ENABLED = true + +# --- Vector Store --- +[vector_store] +TYPE = "pgvector" +DIMENSIONS = 768 diff --git a/ai/honcho/init-db.sql b/ai/honcho/init-db.sql new file mode 100644 index 0000000..0aa0fc2 --- /dev/null +++ b/ai/honcho/init-db.sql @@ -0,0 +1 @@ +CREATE EXTENSION IF NOT EXISTS vector;