Files
audita/internal/framework/modules/registry.go

164 lines
5.2 KiB
Go

package modules
import (
"context"
"fmt"
"strings"
"gitea.maximumdirect.net/eric/audita/internal/core/config"
"gitea.maximumdirect.net/eric/audita/internal/core/modulecatalog"
"gitea.maximumdirect.net/eric/audita/internal/core/schema"
"gitea.maximumdirect.net/eric/audita/internal/framework/contracts"
glossarymodule "gitea.maximumdirect.net/eric/audita/internal/modules/glossary"
grammarmodule "gitea.maximumdirect.net/eric/audita/internal/modules/grammar"
homophonesmodule "gitea.maximumdirect.net/eric/audita/internal/modules/homophones"
spokenwordmodule "gitea.maximumdirect.net/eric/audita/internal/modules/spoken_word"
)
const (
ModuleKeyGlossary = modulecatalog.KeyGlossary
ModuleKeyHomophones = modulecatalog.KeyHomophones
ModuleKeySpokenWord = modulecatalog.KeySpokenWord
ModuleKeyGrammar = modulecatalog.KeyGrammar
)
const (
ReasonUnsupportedModule = "unsupported_module"
)
// IsKnownModuleKey reports whether a module key is recognized by the production
// registry scaffold.
func IsKnownModuleKey(key string) bool {
return modulecatalog.IsSupported(key)
}
// Dependencies holds explicit constructor dependencies for module creation.
type Dependencies struct {
Config *config.Config
Glossary *schema.Glossary
ProposalLLMClient contracts.StructuredLLMClient
ProposalLLMScheduler contracts.LLMScheduler
ValidationLLMClient contracts.StructuredLLMClient
ValidationLLMScheduler contracts.LLMScheduler
DiagnosticsDir string
}
// ConstructRequest is one module-construction request.
type ConstructRequest struct {
RunSpec contracts.ModuleRunSpec
Dependencies
}
// Constructor builds one module instance from a run spec and explicit deps.
type Constructor func(ctx context.Context, req ConstructRequest) (contracts.TranscriptModule, error)
// Factory resolves configured module specs into module instances.
type Factory struct {
deps Dependencies
constructors map[string]Constructor
}
// NewFactory creates a production module registry.
func NewFactory(deps Dependencies) *Factory {
factory := &Factory{
deps: deps,
constructors: make(map[string]Constructor, len(modulecatalog.SupportedKeys())),
}
_ = factory.RegisterConstructor(ModuleKeyGlossary, constructGlossaryModule)
_ = factory.RegisterConstructor(ModuleKeyHomophones, constructHomophonesModule)
_ = factory.RegisterConstructor(ModuleKeySpokenWord, constructSpokenWordModule)
_ = factory.RegisterConstructor(ModuleKeyGrammar, constructGrammarModule)
return factory
}
// RegisterConstructor registers a constructor for a known module key.
func (f *Factory) RegisterConstructor(moduleKey string, constructor Constructor) error {
if f == nil {
return fmt.Errorf("module factory is nil")
}
key := strings.TrimSpace(moduleKey)
if !IsKnownModuleKey(key) {
return &UnsupportedModuleError{ModuleKey: key}
}
if constructor == nil {
return fmt.Errorf("constructor for module %q must not be nil", key)
}
f.constructors[key] = constructor
return nil
}
func constructGrammarModule(ctx context.Context, req ConstructRequest) (contracts.TranscriptModule, error) {
_ = ctx
_ = req
return grammarmodule.New()
}
func constructGlossaryModule(ctx context.Context, req ConstructRequest) (contracts.TranscriptModule, error) {
_ = ctx
_ = req
return glossarymodule.New()
}
func constructHomophonesModule(ctx context.Context, req ConstructRequest) (contracts.TranscriptModule, error) {
_ = ctx
_ = req
return homophonesmodule.New()
}
func constructSpokenWordModule(ctx context.Context, req ConstructRequest) (contracts.TranscriptModule, error) {
_ = ctx
_ = req
return spokenwordmodule.New()
}
// ModuleForSpec resolves one configured run spec into a module instance.
func (f *Factory) ModuleForSpec(spec contracts.ModuleRunSpec) (contracts.TranscriptModule, error) {
if f == nil {
return nil, fmt.Errorf("module factory is nil")
}
key := strings.TrimSpace(spec.ModuleKey)
if !IsKnownModuleKey(key) {
return nil, &UnsupportedModuleError{ModuleKey: key}
}
constructor, ok := f.constructors[key]
if !ok || constructor == nil {
return nil, fmt.Errorf("internal module registry error: constructor for module %q is not configured", key)
}
module, err := constructor(context.Background(), ConstructRequest{
RunSpec: spec,
Dependencies: Dependencies{
Config: f.deps.Config,
Glossary: f.deps.Glossary,
ProposalLLMClient: f.deps.ProposalLLMClient,
ProposalLLMScheduler: f.deps.ProposalLLMScheduler,
ValidationLLMClient: f.deps.ValidationLLMClient,
ValidationLLMScheduler: f.deps.ValidationLLMScheduler,
DiagnosticsDir: f.deps.DiagnosticsDir,
},
})
if err != nil {
return nil, fmt.Errorf("construct module %q: %w", spec.InstanceName, err)
}
if module == nil {
return nil, fmt.Errorf("constructor for module %q returned nil module", key)
}
return module, nil
}
// UnsupportedModuleError indicates a configured module key is unknown.
type UnsupportedModuleError struct {
ModuleKey string
}
func (e *UnsupportedModuleError) Error() string {
return fmt.Sprintf("unsupported module key %q", strings.TrimSpace(e.ModuleKey))
}
// ReasonCode returns a stable reason code suitable for reporting.
func (e *UnsupportedModuleError) ReasonCode() string {
return ReasonUnsupportedModule
}