Nik Afiq 685b6e2054
All checks were successful
CI / test (push) Successful in 1m12s
CI / build-ai-gateway (push) Successful in 3m0s
CI / build-ha-gateway (push) Successful in 44s
CI / build-discord-bot (push) Successful in 50s
feat: update README files for ha-gateway and ai-gateway, and enhance discord-bot documentation
2026-05-06 19:33:06 +09:00

4.2 KiB

ai-gateway

ai-gateway is an internal gRPC service that turns free-form text into home assistant actions. It asks Ollama to produce a structured intent, resolves that intent against known Home Assistant lights, and calls ha-gateway for any approved action.

Runtime Flow

  1. The service loads .env, configures logging and telemetry, and starts gRPC on GRPC_PORT.
  2. AIService.Query receives prompt text and an optional model name.
  3. The app refreshes or reads the light cache from ha-gateway.
  4. Ollama receives a prompt containing the user text and known lights.
  5. The app parses the model output as JSON intent data.
  6. Supported intents are executed through ha-gateway; unsupported or invalid responses return a plain explanation without taking action.

gRPC API

Defined in proto/ai/v1/ai.proto.

  • AIService.Query: sends text to the AI command flow
  • AIService.ListModels: returns model names reported by Ollama

Supported action intents currently focus on lights:

  • turn on a light
  • turn off a light
  • list known lights
  • no-op / unknown intent

Configuration

Environment variables:

Variable Default Description
GRPC_PORT 50052 gRPC listen port
OLLAMA_URL http://192.168.7.96:11434 Ollama base URL
OLLAMA_MODEL llama3 Default model for queries without an explicit model
OLLAMA_TIMEOUT 120s HTTP timeout for Ollama calls
HA_GATEWAY_ADDR ha-gateway.home-services.svc.cluster.local:50051 gRPC address for ha-gateway
HA_GATEWAY_SERVER_NAME ha-gateway.home-services.svc.cluster.local Expected server name for TLS
TLS_DIR empty Enables mTLS for the server and gateway client when set
OTEL_ENDPOINT empty OTLP gRPC collector endpoint; empty disables telemetry
LOG_LEVEL info debug, info, warn, or error
LOG_FORMAT json json or text
LIGHT_CACHE_TTL 60s Light discovery cache lifetime

Example env file: .env.example

When TLS_DIR is set, the directory must contain tls.crt, tls.key, and ca.crt.

Local Run

Start ha-gateway first, then run:

cp .env.example .env
go run ./cmd/gateway

Run from ai-gateway/ so godotenv loads ai-gateway/.env.

For local plaintext development:

HA_GATEWAY_ADDR=localhost:50051
TLS_DIR=

Smoke Checks

grpcurl -plaintext -d '{"text":"list the lights","source":"local"}' \
  localhost:50052 ai.v1.AIService/Query

grpcurl -plaintext -d '{}' localhost:50052 ai.v1.AIService/ListModels

Reflection is only registered when LOG_LEVEL=debug; otherwise call by using the compiled proto descriptors or generated clients.

Test And Build

go test ./...
go build ./...

Build the container image from the workspace root:

docker build -f ai-gateway/Dockerfile -t ai-gateway:dev .

Optionally pass a build version:

docker build -f ai-gateway/Dockerfile --build-arg VERSION=$(git rev-parse --short HEAD) -t ai-gateway:dev .

Package Map

cmd/gateway/                              # process entrypoint and wiring
internal/adapters/primary/grpc/           # AIService gRPC server
internal/adapters/secondary/ollama/       # Ollama HTTP client
internal/adapters/secondary/hagateway/    # ha-gateway gRPC client
internal/app/                             # query orchestration and intent dispatch
internal/config/                          # environment loading
internal/core/domain/                     # prompt, intent, and cache types
internal/core/ports/driven/               # app-facing LLM and HA interfaces
internal/logger/                          # slog setup
internal/telemetry/                       # OpenTelemetry setup

Limitations

  • Intent parsing depends on the selected model returning valid JSON.
  • Light actions are supported; broader Home Assistant domains are not wired yet.
  • The light cache is per-process memory and refreshes by TTL.
  • Keep this service internal or protect it with mTLS; it does not implement separate app-layer authorization.