Files
Cyrene_v2/backend/internal/plugin/registry.go
T
Aska a58ec7325d
Cyrene CI / backend (push) Has been cancelled
Cyrene CI / frontend (push) Has been cancelled
refactor(logging): replace all print/log with structured slog logger
- main.go: 30+ log.Printf → logger.Infof
- plugin/registry: 12 log.Printf → logger.Infof
- db/db.go, ws/hub.go: log → logger
- platform-bridge: fmt.Printf/println → logger.Info
- Logs now: time=... level=INFO msg=...
- 16 packages pass, vet clean
2026-08-12 21:31:06 +08:00

193 lines
5.0 KiB
Go

package plugin
import (
"context"
"fmt"
"git.yeij.top/AskaEth/Cyrene_v2/internal/logger"
"sync"
)
// Registry manages all loaded plugins and provides type-safe access to providers.
type Registry struct {
mu sync.RWMutex
plugins map[string]Plugin
iotProviders []IoTProvider
toolProviders []ToolProvider
ragProviders []RAGProvider
}
// NewRegistry creates a new empty plugin registry.
func NewRegistry() *Registry {
return &Registry{
plugins: make(map[string]Plugin),
iotProviders: make([]IoTProvider, 0),
toolProviders: make([]ToolProvider, 0),
ragProviders: make([]RAGProvider, 0),
}
}
// Register adds a plugin to the registry after calling Init.
// If Init fails or the plugin is disabled, it is not added.
func (r *Registry) Register(p Plugin, cfg PluginConfig) error {
ctx := context.Background()
if err := p.Init(ctx, cfg); err != nil {
return fmt.Errorf("plugin %s init failed: %w", p.Name(), err)
}
if !p.Enabled() {
logger.Infof("[plugin] %s is disabled, skipping", p.Name())
return nil
}
r.mu.Lock()
defer r.mu.Unlock()
// Check for duplicate
if _, exists := r.plugins[p.Name()]; exists {
return fmt.Errorf("plugin %s already registered", p.Name())
}
r.plugins[p.Name()] = p
// Auto-detect provider interfaces
if iot, ok := p.(IoTProvider); ok {
r.iotProviders = append(r.iotProviders, iot)
logger.Infof("[plugin] registered IoT provider: %s v%s", p.Name(), p.Version())
}
if tool, ok := p.(ToolProvider); ok {
r.toolProviders = append(r.toolProviders, tool)
logger.Infof("[plugin] registered tool provider: %s v%s (%d tools)", p.Name(), p.Version(), len(tool.Tools()))
}
if rag, ok := p.(RAGProvider); ok {
r.ragProviders = append(r.ragProviders, rag)
logger.Infof("[plugin] registered RAG provider: %s v%s", p.Name(), p.Version())
}
logger.Infof("[plugin] %s v%s loaded successfully", p.Name(), p.Version())
return nil
}
// Get returns a plugin by name, or nil if not found.
func (r *Registry) Get(name string) Plugin {
r.mu.RLock()
defer r.mu.RUnlock()
return r.plugins[name]
}
// ListNames returns the names of all loaded plugins.
func (r *Registry) ListNames() []string {
r.mu.RLock()
defer r.mu.RUnlock()
names := make([]string, 0, len(r.plugins))
for name := range r.plugins {
names = append(names, name)
}
return names
}
// GetIoTProvider returns the first registered IoT provider, or nil.
// When multiple IoT providers are registered, the first one wins.
func (r *Registry) GetIoTProvider() IoTProvider {
r.mu.RLock()
defer r.mu.RUnlock()
if len(r.iotProviders) > 0 {
return r.iotProviders[0]
}
return nil
}
// GetAllToolProviders returns all registered tool providers.
func (r *Registry) GetAllToolProviders() []ToolProvider {
r.mu.RLock()
defer r.mu.RUnlock()
result := make([]ToolProvider, len(r.toolProviders))
copy(result, r.toolProviders)
return result
}
// GetAllToolDefinitions returns a flat list of all tool definitions from all tool providers.
func (r *Registry) GetAllToolDefinitions() []ToolDefinition {
r.mu.RLock()
defer r.mu.RUnlock()
defs := make([]ToolDefinition, 0)
for _, tp := range r.toolProviders {
defs = append(defs, tp.Tools()...)
}
return defs
}
// ExecuteTool finds a tool by name across all tool providers and executes it.
func (r *Registry) ExecuteTool(ctx context.Context, toolName string, params map[string]any) (string, error) {
r.mu.RLock()
defer r.mu.RUnlock()
for _, tp := range r.toolProviders {
for _, def := range tp.Tools() {
if def.Name == toolName {
return tp.Execute(ctx, toolName, params)
}
}
}
return "", fmt.Errorf("tool %s not found in any provider", toolName)
}
// GetRAGProviders returns all registered RAG providers.
func (r *Registry) GetRAGProviders() []RAGProvider {
r.mu.RLock()
defer r.mu.RUnlock()
result := make([]RAGProvider, len(r.ragProviders))
copy(result, r.ragProviders)
return result
}
// Count returns the total number of loaded plugins.
func (r *Registry) Count() int {
r.mu.RLock()
defer r.mu.RUnlock()
return len(r.plugins)
}
// Shutdown calls Close on all plugins in reverse registration order.
func (r *Registry) Shutdown() {
r.mu.Lock()
defer r.mu.Unlock()
// Close base plugins that are not specialized providers
for _, p := range r.plugins {
// Check if it was already closed via a provider list
if _, ok := p.(IoTProvider); ok {
continue
}
if _, ok := p.(ToolProvider); ok {
continue
}
if _, ok := p.(RAGProvider); ok {
continue
}
if err := p.Close(); err != nil {
logger.Infof("[plugin] error closing %s: %v", p.Name(), err)
}
}
// Close IoT providers
for _, p := range r.iotProviders {
if err := p.Close(); err != nil {
logger.Infof("[plugin] error closing %s: %v", p.Name(), err)
}
}
// Close tool providers
for _, p := range r.toolProviders {
if err := p.Close(); err != nil {
logger.Infof("[plugin] error closing %s: %v", p.Name(), err)
}
}
// Close RAG providers
for _, p := range r.ragProviders {
if err := p.Close(); err != nil {
logger.Infof("[plugin] error closing %s: %v", p.Name(), err)
}
}
logger.Infof("[plugin] all plugins shut down")
}