package cli import ( "context" "fmt" "gitea.maximumdirect.net/eric/notarius/internal/core/artifacts" "gitea.maximumdirect.net/eric/notarius/internal/core/config" "gitea.maximumdirect.net/eric/notarius/internal/framework/contracts" "gitea.maximumdirect.net/eric/notarius/internal/framework/llm" "gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline" dndregister "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/register" genericregister "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/register" seriatimregister "gitea.maximumdirect.net/eric/notarius/internal/modules/seriatim/register" ) type productionComponents struct { registries pipeline.Registries assets *llm.AssetRegistry } func newProductionComponents() (productionComponents, error) { registries := pipeline.Registries{ Inputs: pipeline.NewInputAdapterRegistry(), Chunkers: pipeline.NewChunkerRegistry(), ArtifactCodecs: pipeline.NewArtifactCodecRegistry(), ArtifactEvidence: pipeline.NewArtifactEvidenceRegistry(), Extractors: pipeline.NewExtractorRegistry(), Mergers: pipeline.NewMergerRegistry(), Normalizers: pipeline.NewNormalizerRegistry(), Validators: pipeline.NewValidatorRegistry(), ValidatorChains: pipeline.NewValidatorChainRegistry(), Outputs: pipeline.NewOutputEncoderRegistry(), } assets := llm.NewAssetRegistry() registrars := []struct { name string register func(pipeline.Registries, *llm.AssetRegistry) error }{ {name: "generic", register: genericregister.Register}, {name: "seriatim", register: seriatimregister.Register}, {name: "dnd", register: dndregister.Register}, } for _, registrar := range registrars { if err := registrar.register(registries, assets); err != nil { return productionComponents{}, fmt.Errorf("register %s module family: %w", registrar.name, err) } } return productionComponents{registries: registries, assets: assets}, nil } func productionRegistries() (pipeline.Registries, error) { components, err := newProductionComponents() return components.registries, err } func productionCatalog() (pipeline.ModuleCatalog, error) { registries, err := productionRegistries() if err != nil { return pipeline.ModuleCatalog{}, err } return catalogFromRegistries(registries), nil } func productionPromptAssets() (*llm.AssetRegistry, error) { components, err := newProductionComponents() return components.assets, err } func effectiveCatalog(opts Options) (pipeline.ModuleCatalog, error) { if !isEmptyCatalog(opts.Catalog) { return opts.Catalog, nil } if !isEmptyRegistries(opts.Registries) { return catalogFromRegistries(opts.Registries), nil } return productionCatalog() } func effectiveRegistries(opts Options) (pipeline.Registries, error) { if !isEmptyRegistries(opts.Registries) { return opts.Registries, nil } if !isEmptyCatalog(opts.Catalog) { return registriesFromCatalog(opts.Catalog), nil } return productionRegistries() } func catalogFromRegistries(registries pipeline.Registries) pipeline.ModuleCatalog { return pipeline.ModuleCatalog{ Inputs: registries.Inputs, Chunkers: registries.Chunkers, ArtifactCodecs: registries.ArtifactCodecs, ArtifactEvidence: registries.ArtifactEvidence, Extractors: registries.Extractors, Mergers: registries.Mergers, Normalizers: registries.Normalizers, Validators: registries.Validators, ValidatorChains: registries.ValidatorChains, Outputs: registries.Outputs, } } func registriesFromCatalog(catalog pipeline.ModuleCatalog) pipeline.Registries { return pipeline.Registries{ Inputs: catalog.Inputs, Chunkers: catalog.Chunkers, ArtifactCodecs: catalog.ArtifactCodecs, ArtifactEvidence: catalog.ArtifactEvidence, Extractors: catalog.Extractors, Mergers: catalog.Mergers, Normalizers: catalog.Normalizers, Validators: catalog.Validators, ValidatorChains: catalog.ValidatorChains, Outputs: catalog.Outputs, } } func isEmptyCatalog(catalog pipeline.ModuleCatalog) bool { return catalog.Inputs == nil && catalog.Chunkers == nil && catalog.ArtifactCodecs == nil && catalog.ArtifactEvidence == nil && catalog.Extractors == nil && catalog.Mergers == nil && catalog.Normalizers == nil && catalog.Validators == nil && catalog.ValidatorChains == nil && catalog.Outputs == nil } func isEmptyRegistries(registries pipeline.Registries) bool { return registries.Inputs == nil && registries.Chunkers == nil && registries.ArtifactCodecs == nil && registries.ArtifactEvidence == nil && registries.Extractors == nil && registries.Mergers == nil && registries.Normalizers == nil && registries.Validators == nil && registries.ValidatorChains == nil && registries.Outputs == nil } func productionLLMClientFactory(ctx context.Context, cfg config.Config, profileID string) (contracts.StructuredLLMClient, []artifacts.LLMProfileManifest, error) { if err := ctx.Err(); err != nil { return nil, nil, err } assets, err := productionPromptAssets() if err != nil { return nil, nil, err } return buildProductionLLMClient(ctx, cfg, profileID, assets) } func productionLLMClientFactoryWithAssets(assets *llm.AssetRegistry) LLMClientFactory { return func(ctx context.Context, cfg config.Config, profileID string) (contracts.StructuredLLMClient, []artifacts.LLMProfileManifest, error) { return buildProductionLLMClient(ctx, cfg, profileID, assets) } } func buildProductionLLMClient(ctx context.Context, cfg config.Config, profileID string, assets *llm.AssetRegistry) (contracts.StructuredLLMClient, []artifacts.LLMProfileManifest, error) { if err := ctx.Err(); err != nil { return nil, nil, err } if assets == nil { return nil, nil, fmt.Errorf("production asset registry must not be nil") } recorder := llm.NewLLMProfileRecorder() client, err := llm.NewPromptKitClient(llm.PromptKitClientConfig{ ProfileDir: cfg.PromptKit.ProfileDir, ProfileFile: cfg.PromptKit.ProfileFile, Assets: assets, Recorder: recorder, }) if err != nil { return nil, nil, fmt.Errorf("create PromptKit-backed LLM client: %w", err) } scheduler, err := llm.NewScheduler(cfg.Concurrency.TotalLLM) if err != nil { return nil, nil, fmt.Errorf("create LLM scheduler: %w", err) } return llm.NewScheduledClient(client, scheduler), nil, nil }