iop/apps/edge/internal/service/service.go
toki 445716cc3e feat(edge): runtime config refresh mechanism implementation
- Add configrefresh package for config classification, request handling
- Add bootstrap refresh_admin.go for admin-level config refresh
- Add edgevalidate package for config validation
- Update edge bootstrap runtime with refresh capabilities
- Update edge service layer with config refresh integration
- Update openai layer routes/chat_handler/server for config refresh
- Update edgecmd config handling with refresh support
- Update input manager, transport server, status provider
- Add edge.yaml config updates and go/config package changes
- Add hostsetup templates and test updates
- Update roadmap and phase documentation for runtime-reconnect-config-refresh
2026-06-21 21:35:50 +09:00

116 lines
3 KiB
Go

package service
import (
"sync"
edgeevents "iop/apps/edge/internal/events"
edgenode "iop/apps/edge/internal/node"
"iop/packages/go/config"
)
const (
DefaultSessionID = "default"
DefaultTimeoutSec = 30
)
// Service is the surface-neutral application service shared by the console,
// HTTP/A2A input surfaces, and the Control Plane connector. Its responsibilities
// are split across this package by concern: status_provider.go (node snapshots,
// capabilities, health status), run_dispatch.go (run lifecycle), node_command.go
// (node command transport), and control_command.go (Control Plane command
// execution).
type Service struct {
mu sync.RWMutex
registry *edgenode.Registry
events *edgeevents.Bus
nodeStore *edgenode.NodeStore
queue *modelQueueManager
modelCatalog []config.ModelCatalogEntry
}
func New(registry *edgenode.Registry, events *edgeevents.Bus) *Service {
s := &Service{registry: registry, events: events}
if events != nil {
q := newModelQueueManager(nil)
q.startEventWatcher(events)
s.queue = q
}
return s
}
func (s *Service) SetNodeStore(store *edgenode.NodeStore) {
s.mu.Lock()
s.nodeStore = store
s.mu.Unlock()
if s.queue != nil {
s.queue.setStore(store)
}
}
// SetRuntimeConfig atomically replaces the runtime config snapshot used by
// dispatch and status readers. A reader observes either the old node
// store/catalog pair or the new pair, never a mix.
func (s *Service) SetRuntimeConfig(store *edgenode.NodeStore, catalog []config.ModelCatalogEntry) {
catalog = cloneModelCatalog(catalog)
s.mu.Lock()
s.nodeStore = store
s.modelCatalog = catalog
s.mu.Unlock()
if s.queue != nil {
s.queue.setStore(store)
}
}
// SetModelCatalog provides the top-level provider-pool model catalog to the
// service. Must be called before the first provider-pool SubmitRun.
func (s *Service) SetModelCatalog(catalog []config.ModelCatalogEntry) {
s.mu.Lock()
s.modelCatalog = cloneModelCatalog(catalog)
s.mu.Unlock()
}
func (s *Service) runtimeConfigSnapshot() (*edgenode.NodeStore, []config.ModelCatalogEntry) {
s.mu.RLock()
defer s.mu.RUnlock()
return s.nodeStore, cloneModelCatalog(s.modelCatalog)
}
func cloneModelCatalog(catalog []config.ModelCatalogEntry) []config.ModelCatalogEntry {
if len(catalog) == 0 {
return nil
}
out := make([]config.ModelCatalogEntry, len(catalog))
for i, entry := range catalog {
out[i] = entry
if entry.Providers != nil {
out[i].Providers = make(map[string]string, len(entry.Providers))
for k, v := range entry.Providers {
out[i].Providers[k] = v
}
}
}
return out
}
func (s *Service) ListNodes() []*edgenode.NodeEntry {
return s.registry.All()
}
// NormalizeSessionID maps an empty session id to the default session.
func NormalizeSessionID(id string) string {
if id == "" {
return DefaultSessionID
}
return id
}
func normalizeTimeoutSec(timeoutSec int) int {
if timeoutSec <= 0 {
return DefaultTimeoutSec
}
return timeoutSec
}
func nodeLabel(entry *edgenode.NodeEntry) string {
return entry.DisplayLabel()
}