home-services/CLAUDE.md
Nik Afiq 5238298b55
Some checks failed
CI / test (push) Successful in 6s
CI / build-ai-gateway (push) Failing after 28s
CI / build-ha-gateway (push) Failing after 24s
CI / build-discord-bot (push) Failing after 29s
Add TTS model components and inference server
- Implemented core model components in `modules.py` including various convolutional layers and normalization techniques.
- Added transformation functions in `transforms.py` for piecewise rational quadratic transformations.
- Created utility functions in `utils.py` for checkpoint management, logging, and hyperparameter handling.
- Introduced monotonic alignment functionality with Cython optimization in `monotonic_align`.
- Developed a minimal inference server in `server.py` to handle synthesis requests.
- Updated requirements to include necessary dependencies for Cython and scipy.
2026-07-24 22:38:36 +09:00

143 lines
7.8 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Repo Overview
`home-services` is a Go workspace of three internal services for home control, connected by gRPC and sharing committed protobuf-generated code:
```text
Discord users
|
v
discord-bot -----> ha-gateway -----> Home Assistant REST API
|
v
ai-gateway ------> Ollama
|
v
ha-gateway
```
- **ha-gateway** (port `50051`) — gRPC boundary for Home Assistant. Talks to HA's REST API; implements entity state, light control/discovery, switch control/discovery, and climate (HVAC) control/discovery; also relays SwitchBot Cloud remote commands (`RemoteApp`) when `SWITCHBOT_TOKEN`/`SWITCHBOT_SECRET` are set. Event streaming is stubbed.
- **ai-gateway** (port `50052`) — gRPC service that turns free-form text into home actions. Calls Ollama for intent extraction, resolves intents against a cached light list from `ha-gateway`, and calls `ha-gateway` to execute approved actions.
- **discord-bot** — registers `/light`, `/switch`, `/ac`, `/ai` slash commands and calls `ha-gateway`/`ai-gateway` via gRPC clients.
Each service is a separate Go module (own `go.mod`) joined by `go.work` at the root, plus a `gen` module for shared generated code. Module paths are `gitea.nik4nao.com/nik/home-services/{ha-gateway,ai-gateway,discord-bot,gen}`.
## Architecture (hexagonal, per service)
Every service follows the same internal layout, with dependencies pointing inward:
```text
cmd/<entrypoint>/ # process entrypoint and wiring (loads .env, builds adapters, starts gRPC)
internal/adapters/primary/ # inbound edges: gRPC servers, Discord handlers
internal/adapters/secondary/ # outbound edges: HA REST client, Ollama client, ha-gateway/ai-gateway gRPC clients
internal/app/ # use-case orchestration
internal/core/domain/ # domain types
internal/core/ports/ # driving (inbound) and driven (outbound) interfaces
internal/config/ # environment loading
internal/logger/ # slog setup
internal/telemetry/ # OpenTelemetry setup
```
`internal/core` has no dependency on adapters — ports are interfaces that adapters implement (driven) or call into (driving). When adding a capability, the usual path is: define/extend a port in `core/ports`, implement orchestration in `app`, then wire an adapter in `adapters/primary` or `adapters/secondary`.
Protobuf contracts live in `proto/` (buf module, `ai/v1` and `ha/v1` packages). Generated Go code is committed under `gen/` and consumed by all three services through the Go workspace — do not hand-edit files in `gen/`.
## `tmp/` is reference-only, never a dependency
`tmp/` is gitignored — nothing under it is pushed to git, and it should be treated as temporary scratch space for reference material (e.g. `tmp/reference/switchbot-control-reference/`, a standalone CLI copied in for local discovery/testing against an external API). Rules:
- It's fine to read code under `tmp/` for patterns, to run its scripts/tools locally (a discovery script, a CLI, etc.), or to use it as a manual testing aid.
- Never make any of the three services (`ha-gateway`, `ai-gateway`, `discord-bot`) import, `go.work use`, or otherwise depend on anything under `tmp/` at build or runtime. Since `tmp/` isn't committed, that dependency would silently break for every other clone of the repo (including CI).
- If a reference tool under `tmp/` lives in its own Go module nested inside this repo's `go.work` workspace, invoke it with `GOWORK=off` rather than adding it to the root `go.work` — e.g. `GOWORK=off ./scripts/some-tool ...` from that tool's own directory.
- If something under `tmp/` turns out to be genuinely needed at runtime, port the actual logic into the relevant service's `internal/` tree (following the hexagonal layout above) instead of reaching into `tmp/` from committed code.
## Common Commands
Regenerate protobuf code after changing anything under `proto/` (requires `buf`):
```bash
buf generate
```
Run tests / vet (from repo root, or `cd` into a service and drop the prefix):
```bash
go test ./ha-gateway/... ./ai-gateway/... ./discord-bot/...
go vet ./ha-gateway/... ./ai-gateway/... ./discord-bot/...
```
Run a single test:
```bash
cd ha-gateway && go test ./internal/app/... -run TestEntityAppGetState
```
Build binaries:
```bash
go build ./ha-gateway/... ./ai-gateway/... ./discord-bot/...
```
Run a service locally (each loads `.env` from its own working directory via `godotenv`, so `cd` into the service dir first):
```bash
cd ha-gateway && cp .env.example .env && go run ./cmd/gateway
cd ai-gateway && cp .env.example .env && go run ./cmd/gateway
cd discord-bot && cp .env.example .env && go run ./cmd/bot
```
For local plaintext dev, point gateways at each other with `TLS_DIR` empty, e.g. `HA_GATEWAY_ADDR=localhost:50051`, `AI_GATEWAY_ADDR=localhost:50052`.
Build container images (from repo root, since Dockerfiles reference the whole workspace):
```bash
docker build -f ha-gateway/Dockerfile -t ha-gateway:dev .
docker build -f ai-gateway/Dockerfile -t ai-gateway:dev .
docker build -f discord-bot/Dockerfile -t discord-bot:dev .
```
gRPC smoke checks (with the relevant service running):
```bash
grpcurl -plaintext -d '{"domain":"light"}' localhost:50051 ha.v1.EntityService/ListStates
grpcurl -plaintext -d '{"entity_id":"light.living_room","brightness_pct":80}' localhost:50051 ha.v1.LightService/TurnOn
grpcurl -plaintext -d '{"text":"turn on the desk lamp","source":"local"}' localhost:50052 ai.v1.AIService/Query
grpcurl -plaintext -d '{}' localhost:50052 ai.v1.AIService/ListModels
```
Note: `ha-gateway` always registers gRPC reflection; `ai-gateway` only registers reflection when `LOG_LEVEL=debug`.
## Testing Conventions
Tests use the standard library `testing` package only (no testify). Mocks are hand-written structs implementing the relevant `core/ports/driven` interface with function fields (see `ha-gateway/internal/app/entity_test.go` for the pattern).
## Configuration Notes
- Every service reads `TLS_DIR` to enable optional mTLS; when set, the directory must contain `tls.crt`, `tls.key`, and `ca.crt`.
- `OTEL_ENDPOINT` enables OTLP gRPC traces/metrics; leave empty for local no-op telemetry.
- `LOG_FORMAT=json` is the production default; `text` is easier to read locally.
- `ha-gateway` reads `SWITCHBOT_TOKEN`/`SWITCHBOT_SECRET` (optional) to enable SwitchBot Cloud remote commands via `RemoteApp`; leave empty to disable that path.
- None of the services implement app-layer authorization — they rely on being kept on a trusted internal network or on mTLS. Keep this in mind before adding any endpoint that wasn't previously reachable.
## nik-gpu deployment target
`tts-gateway` (planned in `TTS_GATEWAY_PLAN.md`) is designed to run on `nik-gpu`, a remote Nvidia
GPU host reachable via `ssh nik-gpu`, managed through the Claude Code skills `nik-gpu-status`
(read-only check), `nik-gpu-sync` (rsync this repo to `~/repo/home-service/` there), and
`nik-gpu-docker-build` (build/smoke-test via a `nik-gpu` Docker context).
**Never automatically run installation or other host-system-altering commands on nik-gpu**
`apt`/`apt-get`, `pip install` outside a container, Docker daemon config changes, driver/toolkit
updates, or anything requiring `sudo`. Always print the exact command and ask the user to run it
themselves (their own terminal, or `! <command>` in a Claude Code session). This applies to the
bare nik-gpu host specifically; installing packages *inside* a Dockerfile build (e.g.
`apt-get install open-jtalk` as a build step) is a normal container build action, not a host
mutation, and is fine to run.
## CI
`.gitea/workflows/ci.yaml` runs `go vet` and `go test` for all four modules (`gen`, `ai-gateway`, `ha-gateway`, `discord-bot`), then builds and pushes Docker images for the three services on pushes to `main`.