Skip to content

Commit 2accbbe

Browse files
Merge pull request #3 from offendingcommit/claude/resolve-pr-conflicts-J6GYN
2 parents a3e8000 + 989e513 commit 2accbbe

26 files changed

Lines changed: 2933 additions & 111 deletions

.github/workflows/docker-build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
name: Build and Push Docker Image
22

33
on:
4+
workflow_dispatch:
45
push:
56
branches:
67
- main

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,5 @@ metrics.jsonl
193193
AGENTS.md
194194
lancedb_data/
195195
grafana-data/
196+
.codex_honcho_runtime/
197+
.codex_honcho_setup/

CLAUDE.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,44 @@ All API routes follow the pattern: `/v1/{resource}/{id}/{action}`
8484
- Typechecking: `uv run basedpyright`
8585
- Format code: `uv run ruff format src/`
8686

87+
### LLM provider gotchas (learned 2026-04-16 in k8s deploy)
88+
89+
- **Structured outputs (`response_format={"type": "json_schema"}`) only work on providers whose upstream API natively honors them.** Google Gemini does (route via `cf` provider with base_url ending in `/openai`). Ollama Cloud (reached via the `custom` provider + `custom-ollama` CF gateway endpoint, or any direct Ollama endpoint) does **not** translate `response_format` into Ollama's native JSON-mode — every Ollama Cloud model (GLM-5.1, nemotron-3-nano, qwen3.5, devstral-small-2 confirmed) returns free-form text/markdown when a schema is requested, and `honcho_llm_call` bubbles a `ValidationError: Invalid JSON` out of pydantic parsing.
90+
- **Therefore: deriver (`src/deriver/deriver.py:126`) and summary (`src/utils/summarizer.py`) must stay on a Gemini-backed `cf` provider.** Dream, dialectic, and any free-form / tool-call path is free to use the `custom` provider.
91+
- **Gemini `thoughtSignature` round-tripping breaks on the CF `openai`-compat route.** Any call with `maxToolIterations > 1` AND `thinkingBudgetTokens > 0` will return `400 Function call is missing a thought_signature` on iteration 2+. If you need thinking on a multi-iteration tool loop, use the native Gemini provider, not the OpenAI-compat route — or set `thinkingBudgetTokens=0`.
92+
- **None of this is Cloudflare's fault.** CF AI Gateway is a transparent proxy in both the `openai` and `custom-ollama` routes. The limitations live at the upstream provider (Ollama Cloud's OpenAI-compat layer).
93+
94+
### Local LM Studio Setup
95+
96+
- Honcho can use LM Studio for generation through the `custom` provider path.
97+
- Keep `LLM_OPENAI_API_KEY` configured for embeddings unless embedding support is added for local models.
98+
- For Docker Compose, `LLM_OPENAI_COMPATIBLE_BASE_URL` must be `http://host.docker.internal:1234/v1`, not `http://localhost:1234/v1`.
99+
- `LLM_OPENAI_COMPATIBLE_API_KEY=lm-studio` is sufficient for local use.
100+
- Current local default model is `qwen2.5-14b-instruct`.
101+
- When overriding `DIALECTIC_LEVELS__*` via env vars, each level needs its full required settings, not just `PROVIDER` and `MODEL`. Include `THINKING_BUDGET_TOKENS` and `MAX_TOOL_ITERATIONS`, and optionally `MAX_OUTPUT_TOKENS`.
102+
- Docker should own the runtime environment completely. Do not mount the repo onto `/app` and do not mount a named volume onto `/app/.venv`, or the image-built environment can be hidden and replaced with incompatible artifacts.
103+
- If Docker services fail with missing Python modules or incompatible native extensions, rebuild the image instead of trying to repair the environment in-place:
104+
105+
```bash
106+
docker compose build --no-cache api deriver
107+
docker compose up -d --force-recreate api deriver
108+
```
109+
110+
- Verify LM Studio from the host with:
111+
112+
```bash
113+
curl -sS http://localhost:1234/v1/models
114+
```
115+
116+
- Verify LM Studio from Docker with:
117+
118+
```bash
119+
docker compose run --rm --entrypoint sh api -lc 'python - <<\"PY\"
120+
import urllib.request
121+
print(urllib.request.urlopen(\"http://host.docker.internal:1234/v1/models\", timeout=5).status)
122+
PY'
123+
```
124+
87125
### SDK Testing
88126

89127
#### TypeScript SDK

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ COPY --chown=app:app alembic.ini /app/alembic.ini
4646
# Copy config files - this will copy config.toml if it exists, and config.toml.example
4747
COPY --chown=app:app config.toml* /app/
4848

49+
RUN chmod +x /app/docker/entrypoint.sh
50+
4951
# Switch to non-root user
5052
USER app
5153

docker-compose.yml.example

Lines changed: 75 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,73 +4,86 @@
44
# cp docker-compose.yml.example docker-compose.yml
55
# cp .env.template .env # edit with your provider config
66
# docker compose up -d --build
7-
#
8-
# By default, ports are bound to 127.0.0.1 (localhost only).
9-
# For development, uncomment the source mounts and monitoring services below.
107

118
services:
9+
traefik:
10+
image: traefik:v3.2
11+
command:
12+
- --api.dashboard=true
13+
- --api.insecure=true
14+
- --providers.file.filename=/etc/traefik/dynamic.yml
15+
- --providers.file.watch=true
16+
- --entrypoints.web.address=:8000
17+
- --ping=true
18+
ports:
19+
- ${HONCHO_HTTP_PORT:-8000}:8000
20+
- ${TRAEFIK_DASHBOARD_PORT:-8080}:8080
21+
volumes:
22+
- ./docker/traefik/dynamic.yml:/etc/traefik/dynamic.yml:ro
23+
networks:
24+
- honcho
25+
1226
api:
27+
image: honcho:latest
1328
build:
1429
context: .
1530
dockerfile: Dockerfile
16-
entrypoint: ["sh", "docker/entrypoint.sh"]
31+
entrypoint: ["/app/docker/entrypoint.sh"]
1732
depends_on:
1833
database:
1934
condition: service_healthy
2035
redis:
2136
condition: service_healthy
22-
ports:
23-
- "127.0.0.1:8000:8000"
24-
# -- Development: mount source for live reload --
25-
# volumes:
26-
# - .:/app
27-
# - venv:/app/.venv
37+
traefik:
38+
condition: service_started
39+
expose:
40+
- 8000
2841
environment:
2942
- DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
3043
- CACHE_URL=redis://redis:6379/0?suppress=true
31-
- CACHE_ENABLED=true
3244
env_file:
3345
- path: .env
3446
required: false
35-
restart: unless-stopped
47+
networks:
48+
- honcho
3649

3750
deriver:
3851
build:
3952
context: .
4053
dockerfile: Dockerfile
41-
entrypoint: ["/app/.venv/bin/python", "-m", "src.deriver"]
54+
entrypoint: ["python", "-m", "src.deriver"]
4255
depends_on:
4356
database:
4457
condition: service_healthy
4558
redis:
4659
condition: service_healthy
47-
# -- Development: mount source for live reload --
48-
# volumes:
49-
# - .:/app
50-
# - venv:/app/.venv
5160
environment:
5261
- DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
5362
- CACHE_URL=redis://redis:6379/0?suppress=true
54-
- CACHE_ENABLED=true
63+
- METRICS_ENABLED=true
5564
env_file:
5665
- path: .env
5766
required: false
58-
restart: unless-stopped
67+
networks:
68+
- honcho
5969

6070
database:
6171
image: pgvector/pgvector:pg15
62-
restart: unless-stopped
72+
restart: always
6373
ports:
64-
- "127.0.0.1:5432:5432"
65-
command: ["postgres", "-c", "max_connections=200"]
74+
- 5432:5432
75+
command: ["postgres", "-c", "max_connections=800"]
6676
environment:
6777
- POSTGRES_DB=postgres
6878
- POSTGRES_USER=postgres
6979
- POSTGRES_PASSWORD=postgres
80+
- POSTGRES_HOST_AUTH_METHOD=trust
7081
- PGDATA=/var/lib/postgresql/data/pgdata
7182
volumes:
7283
- ./database/init.sql:/docker-entrypoint-initdb.d/init.sql
7384
- pgdata:/var/lib/postgresql/data/
85+
networks:
86+
- honcho
7487
healthcheck:
7588
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
7689
interval: 5s
@@ -79,46 +92,54 @@ services:
7992

8093
redis:
8194
image: redis:8.2
82-
restart: unless-stopped
95+
restart: always
8396
ports:
84-
- "127.0.0.1:6379:6379"
97+
- 6379:6379
8598
volumes:
86-
- redis-data:/data
99+
- ./redis-data:/data
100+
networks:
101+
- honcho
87102
healthcheck:
88103
test: ["CMD-SHELL", "redis-cli ping"]
89104
interval: 5s
90105
timeout: 5s
91106
retries: 5
92107

93-
# -- Development: monitoring stack (uncomment to enable) --
94-
# prometheus:
95-
# image: prom/prometheus:v3.2.1
96-
# ports:
97-
# - "127.0.0.1:9090:9090"
98-
# volumes:
99-
# - ./docker/prometheus.yml:/etc/prometheus/prometheus.yml:ro
100-
# - prometheus-data:/prometheus
101-
# depends_on:
102-
# api:
103-
# condition: service_started
104-
# grafana:
105-
# image: grafana/grafana:11.4.0
106-
# ports:
107-
# - "127.0.0.1:3000:3000"
108-
# environment:
109-
# - GF_SECURITY_ADMIN_USER=admin
110-
# - GF_SECURITY_ADMIN_PASSWORD=admin
111-
# - GF_AUTH_ANONYMOUS_ENABLED=true
112-
# - GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
113-
# volumes:
114-
# - ./docker/grafana-datasource.yml:/etc/grafana/provisioning/datasources/datasource.yml:ro
115-
# depends_on:
116-
# prometheus:
117-
# condition: service_started
108+
prometheus:
109+
image: prom/prometheus:v3.2.1
110+
ports:
111+
- 9090:9090
112+
volumes:
113+
- ./docker/prometheus.yml:/etc/prometheus/prometheus.yml:ro
114+
- prometheus-data:/prometheus
115+
depends_on:
116+
api:
117+
condition: service_started
118+
networks:
119+
- honcho
120+
121+
grafana:
122+
image: grafana/grafana:11.4.0
123+
ports:
124+
- 3000:3000
125+
environment:
126+
- GF_SECURITY_ADMIN_USER=admin
127+
- GF_SECURITY_ADMIN_PASSWORD=admin
128+
- GF_AUTH_ANONYMOUS_ENABLED=true
129+
- GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
130+
volumes:
131+
- ./grafana-data:/var/lib/grafana
132+
- ./docker/grafana-datasource.yml:/etc/grafana/provisioning/datasources/datasource.yml:ro
133+
depends_on:
134+
prometheus:
135+
condition: service_started
136+
networks:
137+
- honcho
138+
139+
networks:
140+
honcho:
141+
name: honcho
118142

119143
volumes:
120144
pgdata:
121-
redis-data:
122-
# -- Development: uncomment if using source mounts --
123-
# venv:
124-
# prometheus-data:
145+
prometheus-data:

docker/entrypoint.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
set -e
33

44
echo "Running database migrations..."
5-
/app/.venv/bin/python scripts/provision_db.py
5+
python scripts/provision_db.py
66

77
echo "Starting API server..."
8-
exec /app/.venv/bin/fastapi run --host 0.0.0.0 src/main.py
8+
exec fastapi run --host 0.0.0.0 src/main.py

docker/grafana-dashboards.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: 1
2+
3+
providers:
4+
- name: Honcho
5+
orgId: 1
6+
folder: Honcho
7+
type: file
8+
disableDeletion: false
9+
editable: true
10+
options:
11+
path: /etc/grafana/provisioning/dashboards/files

0 commit comments

Comments
 (0)