Nik Afiq 520f5d1ffb
Some checks failed
CI / build-ai-gateway (push) Has been cancelled
CI / build-ha-gateway (push) Has been cancelled
CI / build-discord-bot (push) Has been cancelled
CI / test (push) Has been cancelled
feat: add ai-gateway microservice with gRPC API for AI logic
- Implemented new gRPC service `AIService` in `proto/ai/v1/ai.proto` for handling natural language queries.
- Generated Go code for the gRPC service and messages in `gen/ai/v1/`.
- Created `services/ai-gateway/` directory structure with necessary files for the service.
- Added configuration loading and structured logging.
- Implemented domain logic for intent parsing and interaction with Home Assistant.
- Established outbound adapters for Ollama and Home Assistant with mTLS support.
- Updated `go.work` to include the new service and maintain existing dependencies.
- Modified `discord-bot` to use the new `ai-gateway` for AI interactions.
- Added deployment manifest for Kubernetes and CI/CD configuration for building and deploying the service.
2026-04-21 21:52:28 +09:00

50 lines
1.3 KiB
Go

package domain
import (
"context"
"sync"
"time"
"gitea.nik4nao.com/nik/home-services/ai-gateway/internal/core/ports/driven"
)
// LightCache keeps recently-fetched light discovery data in memory.
type LightCache struct {
ttl time.Duration
loadFunc func(context.Context) ([]driven.Light, error)
mu sync.RWMutex
lights []driven.Light
loadedAt time.Time
}
// NewLightCache constructs a TTL-based in-memory light cache.
func NewLightCache(ttl time.Duration, loadFunc func(context.Context) ([]driven.Light, error)) *LightCache {
return &LightCache{ttl: ttl, loadFunc: loadFunc}
}
// Get returns the cached lights, refreshing lazily when empty or stale.
func (c *LightCache) Get(ctx context.Context) ([]driven.Light, error) {
c.mu.RLock()
if len(c.lights) > 0 && time.Since(c.loadedAt) < c.ttl {
lights := append([]driven.Light(nil), c.lights...)
c.mu.RUnlock()
return lights, nil
}
c.mu.RUnlock()
c.mu.Lock()
defer c.mu.Unlock()
if len(c.lights) > 0 && time.Since(c.loadedAt) < c.ttl {
return append([]driven.Light(nil), c.lights...), nil
}
lights, err := c.loadFunc(ctx)
if err != nil {
return nil, err
}
c.lights = append([]driven.Light(nil), lights...)
c.loadedAt = time.Now()
return append([]driven.Light(nil), c.lights...), nil
}