Move option source construction into the internal engine

This commit is contained in:
2026-09-22 04:44:41 +00:00
parent bd2b7b1862
commit 47bc9a6655
9 changed files with 190 additions and 157 deletions

129
engine.go
View File

@@ -6,19 +6,11 @@ import (
"fmt"
"io/fs"
"net/http"
"os"
"path/filepath"
"strings"
"time"
artifactadapter "gitea.maximumdirect.net/eric/promptkit/internal/artifact"
"gitea.maximumdirect.net/eric/promptkit/internal/domain"
internalengine "gitea.maximumdirect.net/eric/promptkit/internal/engine"
"gitea.maximumdirect.net/eric/promptkit/internal/llm"
"gitea.maximumdirect.net/eric/promptkit/internal/profile"
"gitea.maximumdirect.net/eric/promptkit/internal/promptdef"
"gitea.maximumdirect.net/eric/promptkit/internal/usecase"
"gitea.maximumdirect.net/eric/promptkit/internal/validate"
)
// ErrInvalidConfig identifies invalid engine construction, including missing
@@ -134,20 +126,7 @@ func (f optionFunc) apply(options *engineOptions) error {
}
type engineOptions struct {
llmClient llm.Client
artifactReader artifactadapter.Reader
promptDefs promptdef.Repository
profiles profile.Repository
fallbackProfiles profile.Repository
memoryProfiles profile.Repository
backends []domain.Backend
validator validate.Validator
promptSource bool
profileSource bool
fallbackProfileSource bool
memorySource bool
validatorSource bool
artifactSource bool
internal internalengine.Options
}
// WithLLMClient replaces the built-in model client used by [Engine.Run] and
@@ -163,7 +142,7 @@ func WithLLMClient(client LLMClient) Option {
if client == nil {
return ErrInvalidConfig
}
options.llmClient = publicLLMClientAdapter{client: client}
options.internal.SetLLMClient(publicLLMClientAdapter{client: client})
return nil
})
}
@@ -178,8 +157,7 @@ func WithArtifactReader(reader ArtifactReader) Option {
if reader == nil {
return ErrInvalidConfig
}
options.artifactReader = publicArtifactReaderAdapter{reader: reader}
options.artifactSource = true
options.internal.SetArtifactReader(publicArtifactReaderAdapter{reader: reader})
return nil
})
}
@@ -193,15 +171,7 @@ func WithArtifactReader(reader ArtifactReader) Option {
// prompt-source options.
func WithPromptFS(fsys fs.FS, root string) Option {
return optionFunc(func(options *engineOptions) error {
if fsys == nil {
return ErrInvalidConfig
}
if strings.TrimSpace(root) == "" {
return ErrInvalidConfig
}
options.promptDefs = promptdef.NewFSRepository(fsys, root)
options.promptSource = true
return nil
return options.internal.SetPromptFS(fsys, root)
})
}
@@ -212,13 +182,7 @@ func WithPromptFS(fsys fs.FS, root string) Option {
// This option replaces Config.PromptDir and earlier prompt-source options.
func WithPromptFile(path string) Option {
return optionFunc(func(options *engineOptions) error {
fsys, root, err := fileSource(path)
if err != nil {
return err
}
options.promptDefs = promptdef.NewFileRepository(fsys, root, filepath.Dir(path))
options.promptSource = true
return nil
return options.internal.SetPromptFile(path)
})
}
@@ -232,15 +196,7 @@ func WithPromptFile(path string) Option {
// FS profile-source options, but remains below WithProfiles in precedence.
func WithProfileFS(fsys fs.FS, root string) Option {
return optionFunc(func(options *engineOptions) error {
if fsys == nil {
return ErrInvalidConfig
}
if strings.TrimSpace(root) == "" {
return ErrInvalidConfig
}
options.profiles = profile.NewFSRepository(fsys, root)
options.profileSource = true
return nil
return options.internal.SetProfileFS(fsys, root)
})
}
@@ -254,13 +210,7 @@ func WithProfileFS(fsys fs.FS, root string) Option {
// precedence.
func WithProfileFile(path string) Option {
return optionFunc(func(options *engineOptions) error {
fsys, root, err := fileSource(path)
if err != nil {
return err
}
options.profiles = profile.NewFSRepository(fsys, root)
options.profileSource = true
return nil
return options.internal.SetProfileFile(path)
})
}
@@ -285,15 +235,7 @@ func WithProfileFile(path string) Option {
// failover.
func WithFallbackProfileFS(fsys fs.FS, root string) Option {
return optionFunc(func(options *engineOptions) error {
if fsys == nil {
return ErrInvalidConfig
}
if strings.TrimSpace(root) == "" {
return ErrInvalidConfig
}
options.fallbackProfiles = profile.NewFSRepository(fsys, root)
options.fallbackProfileSource = true
return nil
return options.internal.SetFallbackProfileFS(fsys, root)
})
}
@@ -312,13 +254,7 @@ func WithProfiles(profiles ...Profile) Option {
for index, publicProfile := range profiles {
definitions[index] = toDomainProfile(publicProfile)
}
repo, err := profile.NewMemoryRepository(definitions)
if err != nil {
return err
}
options.memoryProfiles = repo
options.memorySource = true
return nil
return options.internal.SetMemoryProfiles(definitions)
})
}
@@ -330,15 +266,7 @@ func WithProfiles(profiles ...Profile) Option {
// replaces Config.SchemaDir and earlier schema-source options.
func WithSchemaFS(fsys fs.FS, root string) Option {
return optionFunc(func(options *engineOptions) error {
if fsys == nil {
return ErrInvalidConfig
}
if strings.TrimSpace(root) == "" {
return ErrInvalidConfig
}
options.validator = validate.NewFSValidator(fsys, root)
options.validatorSource = true
return nil
return options.internal.SetSchemaFS(fsys, root)
})
}
@@ -349,13 +277,7 @@ func WithSchemaFS(fsys fs.FS, root string) Option {
// replaces Config.SchemaDir and earlier schema-source options.
func WithSchemaFile(path string) Option {
return optionFunc(func(options *engineOptions) error {
fsys, root, err := fileSource(path)
if err != nil {
return err
}
options.validator = validate.NewFSValidator(fsys, root)
options.validatorSource = true
return nil
return options.internal.SetSchemaFile(path)
})
}
@@ -384,15 +306,7 @@ func NewEngine(cfg Config, opts ...Option) (*Engine, error) {
assembled, err := internalengine.New(
internalengine.Config{PromptDir: cfg.PromptDir, ProfileDir: cfg.ProfileDir, SchemaDir: cfg.SchemaDir, Timeout: cfg.Timeout, HTTPClient: cfg.HTTPClient},
internalengine.Options{
LLMClient: options.llmClient, ArtifactReader: options.artifactReader,
PromptDefinitions: options.promptDefs, Profiles: options.profiles,
FallbackProfiles: options.fallbackProfiles, MemoryProfiles: options.memoryProfiles,
Backends: options.backends, Validator: options.validator,
PromptSource: options.promptSource, ProfileSource: options.profileSource,
FallbackProfileSource: options.fallbackProfileSource, MemorySource: options.memorySource,
ValidatorSource: options.validatorSource, ArtifactSource: options.artifactSource,
},
options.internal,
)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrInvalidConfig, err)
@@ -400,25 +314,6 @@ func NewEngine(cfg Config, opts ...Option) (*Engine, error) {
return &Engine{internal: assembled}, nil
}
func fileSource(name string) (fs.FS, string, error) {
if strings.TrimSpace(name) == "" {
return nil, "", ErrInvalidConfig
}
dir := filepath.Dir(name)
base := filepath.Base(name)
if base == "." || base == string(filepath.Separator) {
return nil, "", ErrInvalidConfig
}
info, err := os.Stat(name)
if err != nil {
return nil, "", fmt.Errorf("%w: failed to access source file %q: %v", ErrInvalidConfig, name, err)
}
if info.IsDir() {
return nil, "", fmt.Errorf("%w: source path %q must be a file", ErrInvalidConfig, name)
}
return os.DirFS(dir), filepath.ToSlash(base), nil
}
// InspectPrompt resolves one explicit prompt definition without selecting a
// profile or starting execution work.
//