feat(server): hot-reload config store and reloadable infra clients
- Replace single Load() with a watching Store that re-reads config files
(and config.local.yaml) and fans out a ReloadEvent with a per-field diff
so consumers can decide whether the change is hot-applicable or requires
a process restart.
- Wrap llm, retrieval, vector store, and object storage clients in
Reloadable* shells so the bootstrap can swap their underlying impls when
config changes without re-instantiating handlers.
- Make jwt.Manager and ops TokenIssuer mutable under a lock so secrets and
TTLs can be rotated live; thread default plan code through a setter on
the ops AdminUserService.
- Wire ConfigStore through bootstrap and every cmd/main.go, scheduler /
worker / tenant-api / ops-api start the watcher; services and handlers
take a config.Provider so they always read current values for things
like generation.stream_enabled, scheduler dispatch, retrieval, etc.
- Switch shared/config decoding off viper to a Kratos-derived runtime
package so env placeholders (\${VAR:default}) resolve consistently and
the same source machinery powers both the loader and the watcher.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/geo-platform/tenant-api/internal/shared/config"
|
||||
)
|
||||
|
||||
const defaultArticleGenerationTimeout = 8 * time.Minute
|
||||
|
||||
type runtimeConfigProvider = config.Provider
|
||||
|
||||
type generationRuntimeConfig struct {
|
||||
StreamEnabled bool
|
||||
ArticleTimeout time.Duration
|
||||
MaxOutputTokens int64
|
||||
}
|
||||
|
||||
type knowledgeRuntimeConfig struct {
|
||||
ChunkSize int
|
||||
ChunkOverlap int
|
||||
EmbeddingBatchSize int
|
||||
RecallLimit int
|
||||
RerankTopN int
|
||||
ArkBaseURL string
|
||||
ArkAPIKey string
|
||||
URLMarkdownModel string
|
||||
}
|
||||
|
||||
type generationStreamDisabledConfigProvider struct {
|
||||
base config.Provider
|
||||
}
|
||||
|
||||
func NewGenerationStreamDisabledConfigProvider(base config.Provider) config.Provider {
|
||||
return generationStreamDisabledConfigProvider{base: base}
|
||||
}
|
||||
|
||||
func (p generationStreamDisabledConfigProvider) Current() *config.Config {
|
||||
if p.base == nil {
|
||||
return nil
|
||||
}
|
||||
cfg := p.base.Current()
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
next := *cfg
|
||||
next.Generation.StreamEnabled = false
|
||||
return &next
|
||||
}
|
||||
|
||||
func generationRuntimeFromConfig(generation config.GenerationConfig, llmCfg config.LLMConfig) generationRuntimeConfig {
|
||||
articleTimeout := generation.ArticleTimeout
|
||||
if articleTimeout <= 0 {
|
||||
articleTimeout = defaultArticleGenerationTimeout
|
||||
}
|
||||
return generationRuntimeConfig{
|
||||
StreamEnabled: generation.StreamEnabled,
|
||||
ArticleTimeout: articleTimeout,
|
||||
MaxOutputTokens: llmCfg.MaxOutputTokens,
|
||||
}
|
||||
}
|
||||
|
||||
func knowledgeRuntimeFromConfig(retrievalCfg config.RetrievalConfig, llmCfg config.LLMConfig) knowledgeRuntimeConfig {
|
||||
chunkSize := retrievalCfg.ChunkSize
|
||||
if chunkSize <= 0 {
|
||||
chunkSize = 900
|
||||
}
|
||||
chunkOverlap := retrievalCfg.ChunkOverlap
|
||||
if chunkOverlap < 0 {
|
||||
chunkOverlap = 120
|
||||
}
|
||||
batchSize := retrievalCfg.EmbeddingBatchSize
|
||||
if batchSize <= 0 {
|
||||
batchSize = 16
|
||||
}
|
||||
recallLimit := retrievalCfg.RecallLimit
|
||||
if recallLimit <= 0 {
|
||||
recallLimit = 12
|
||||
}
|
||||
rerankTopN := retrievalCfg.RerankTopN
|
||||
if rerankTopN <= 0 {
|
||||
rerankTopN = 6
|
||||
}
|
||||
|
||||
model := strings.TrimSpace(llmCfg.KnowledgeURLModel)
|
||||
if model == "" {
|
||||
model = strings.TrimSpace(llmCfg.Model)
|
||||
}
|
||||
baseURL := strings.TrimSpace(llmCfg.BaseURL)
|
||||
if baseURL == "" {
|
||||
baseURL = defaultKnowledgeArkBaseURL
|
||||
}
|
||||
|
||||
return knowledgeRuntimeConfig{
|
||||
ChunkSize: chunkSize,
|
||||
ChunkOverlap: chunkOverlap,
|
||||
EmbeddingBatchSize: batchSize,
|
||||
RecallLimit: recallLimit,
|
||||
RerankTopN: rerankTopN,
|
||||
ArkBaseURL: baseURL,
|
||||
ArkAPIKey: strings.TrimSpace(llmCfg.APIKey),
|
||||
URLMarkdownModel: model,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user