Make public options opaque
This commit is contained in:
44
engine.go
44
engine.go
@@ -52,7 +52,15 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Option customizes engine construction.
|
// Option customizes engine construction.
|
||||||
type Option func(*engineOptions) error
|
type Option interface {
|
||||||
|
apply(*engineOptions) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionFunc func(*engineOptions) error
|
||||||
|
|
||||||
|
func (f optionFunc) apply(options *engineOptions) error {
|
||||||
|
return f(options)
|
||||||
|
}
|
||||||
|
|
||||||
type engineOptions struct {
|
type engineOptions struct {
|
||||||
llmClient llm.Client
|
llmClient llm.Client
|
||||||
@@ -68,17 +76,17 @@ type engineOptions struct {
|
|||||||
|
|
||||||
// WithLLMClient injects a custom LLM client for execution.
|
// WithLLMClient injects a custom LLM client for execution.
|
||||||
func WithLLMClient(client LLMClient) Option {
|
func WithLLMClient(client LLMClient) Option {
|
||||||
return func(options *engineOptions) error {
|
return optionFunc(func(options *engineOptions) error {
|
||||||
if client == nil {
|
if client == nil {
|
||||||
return ErrInvalidConfig
|
return ErrInvalidConfig
|
||||||
}
|
}
|
||||||
options.llmClient = publicLLMClientAdapter{client: client}
|
options.llmClient = publicLLMClientAdapter{client: client}
|
||||||
return nil
|
return nil
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithPromptFS(fsys fs.FS, root string) Option {
|
func WithPromptFS(fsys fs.FS, root string) Option {
|
||||||
return func(options *engineOptions) error {
|
return optionFunc(func(options *engineOptions) error {
|
||||||
if fsys == nil {
|
if fsys == nil {
|
||||||
return ErrInvalidConfig
|
return ErrInvalidConfig
|
||||||
}
|
}
|
||||||
@@ -88,11 +96,11 @@ func WithPromptFS(fsys fs.FS, root string) Option {
|
|||||||
options.promptDefs = promptdef.NewFSRepository(fsys, root)
|
options.promptDefs = promptdef.NewFSRepository(fsys, root)
|
||||||
options.promptSource = true
|
options.promptSource = true
|
||||||
return nil
|
return nil
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithPromptFile(path string) Option {
|
func WithPromptFile(path string) Option {
|
||||||
return func(options *engineOptions) error {
|
return optionFunc(func(options *engineOptions) error {
|
||||||
fsys, root, err := fileSource(path)
|
fsys, root, err := fileSource(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -100,11 +108,11 @@ func WithPromptFile(path string) Option {
|
|||||||
options.promptDefs = promptdef.NewFSRepository(fsys, root)
|
options.promptDefs = promptdef.NewFSRepository(fsys, root)
|
||||||
options.promptSource = true
|
options.promptSource = true
|
||||||
return nil
|
return nil
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithProfileFS(fsys fs.FS, root string) Option {
|
func WithProfileFS(fsys fs.FS, root string) Option {
|
||||||
return func(options *engineOptions) error {
|
return optionFunc(func(options *engineOptions) error {
|
||||||
if fsys == nil {
|
if fsys == nil {
|
||||||
return ErrInvalidConfig
|
return ErrInvalidConfig
|
||||||
}
|
}
|
||||||
@@ -114,11 +122,11 @@ func WithProfileFS(fsys fs.FS, root string) Option {
|
|||||||
options.profiles = profile.NewFSRepository(fsys, root)
|
options.profiles = profile.NewFSRepository(fsys, root)
|
||||||
options.profileSource = true
|
options.profileSource = true
|
||||||
return nil
|
return nil
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithProfileFile(path string) Option {
|
func WithProfileFile(path string) Option {
|
||||||
return func(options *engineOptions) error {
|
return optionFunc(func(options *engineOptions) error {
|
||||||
fsys, root, err := fileSource(path)
|
fsys, root, err := fileSource(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -126,13 +134,13 @@ func WithProfileFile(path string) Option {
|
|||||||
options.profiles = profile.NewFSRepository(fsys, root)
|
options.profiles = profile.NewFSRepository(fsys, root)
|
||||||
options.profileSource = true
|
options.profileSource = true
|
||||||
return nil
|
return nil
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithProfiles configures in-memory profiles that take precedence over
|
// WithProfiles configures in-memory profiles that take precedence over
|
||||||
// configured profile files and built-in profiles.
|
// configured profile files and built-in profiles.
|
||||||
func WithProfiles(profiles ...Profile) Option {
|
func WithProfiles(profiles ...Profile) Option {
|
||||||
return func(options *engineOptions) error {
|
return optionFunc(func(options *engineOptions) error {
|
||||||
repo, err := newMemoryProfileRepository(profiles)
|
repo, err := newMemoryProfileRepository(profiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -140,11 +148,11 @@ func WithProfiles(profiles ...Profile) Option {
|
|||||||
options.memoryProfiles = repo
|
options.memoryProfiles = repo
|
||||||
options.memorySource = true
|
options.memorySource = true
|
||||||
return nil
|
return nil
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithSchemaFS(fsys fs.FS, root string) Option {
|
func WithSchemaFS(fsys fs.FS, root string) Option {
|
||||||
return func(options *engineOptions) error {
|
return optionFunc(func(options *engineOptions) error {
|
||||||
if fsys == nil {
|
if fsys == nil {
|
||||||
return ErrInvalidConfig
|
return ErrInvalidConfig
|
||||||
}
|
}
|
||||||
@@ -154,11 +162,11 @@ func WithSchemaFS(fsys fs.FS, root string) Option {
|
|||||||
options.validator = validate.NewFSValidator(fsys, root)
|
options.validator = validate.NewFSValidator(fsys, root)
|
||||||
options.validatorSource = true
|
options.validatorSource = true
|
||||||
return nil
|
return nil
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithSchemaFile(path string) Option {
|
func WithSchemaFile(path string) Option {
|
||||||
return func(options *engineOptions) error {
|
return optionFunc(func(options *engineOptions) error {
|
||||||
fsys, root, err := fileSource(path)
|
fsys, root, err := fileSource(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -166,7 +174,7 @@ func WithSchemaFile(path string) Option {
|
|||||||
options.validator = validate.NewFSValidator(fsys, root)
|
options.validator = validate.NewFSValidator(fsys, root)
|
||||||
options.validatorSource = true
|
options.validatorSource = true
|
||||||
return nil
|
return nil
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEngine constructs an Engine using the same default internal components as
|
// NewEngine constructs an Engine using the same default internal components as
|
||||||
@@ -177,7 +185,7 @@ func NewEngine(cfg Config, opts ...Option) (*Engine, error) {
|
|||||||
if opt == nil {
|
if opt == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := opt(&options); err != nil {
|
if err := opt.apply(&options); err != nil {
|
||||||
return nil, fmt.Errorf("%w: %v", ErrInvalidConfig, err)
|
return nil, fmt.Errorf("%w: %v", ErrInvalidConfig, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1378,6 +1378,45 @@ func TestSourceOptionsRejectInvalidInputs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPackageOptionsComposeFromSlice(t *testing.T) {
|
||||||
|
fake := &fakeLLMClient{response: &scriptorium.GenerateResponse{Content: "ok"}}
|
||||||
|
options := []scriptorium.Option{
|
||||||
|
nil,
|
||||||
|
scriptorium.WithProfiles(scriptorium.Profile{
|
||||||
|
ID: "slice-profile",
|
||||||
|
Endpoint: "http://slice/v1",
|
||||||
|
Model: "slice-model",
|
||||||
|
}),
|
||||||
|
scriptorium.WithLLMClient(fake),
|
||||||
|
}
|
||||||
|
|
||||||
|
engine, err := scriptorium.NewEngine(scriptorium.Config{
|
||||||
|
PromptDir: "./examples/prompts",
|
||||||
|
SchemaDir: "./examples/schemas",
|
||||||
|
}, options...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected package-provided options to compose, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = engine.Run(context.Background(), scriptorium.RunRequest{
|
||||||
|
PromptID: "generic.markdown_summary",
|
||||||
|
ProfileID: "slice-profile",
|
||||||
|
Inputs: map[string]scriptorium.ArtifactRef{
|
||||||
|
"transcript": scriptorium.Inline("Rin opens the gate."),
|
||||||
|
"glossary": scriptorium.Inline("gate: A guarded passage."),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected run with composed options to succeed, got %v", err)
|
||||||
|
}
|
||||||
|
if len(fake.requests) != 1 {
|
||||||
|
t.Fatalf("expected one generate request, got %d", len(fake.requests))
|
||||||
|
}
|
||||||
|
if fake.requests[0].Target.Model != "slice-model" {
|
||||||
|
t.Fatalf("expected profile from composed options, got %q", fake.requests[0].Target.Model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestExtraParamsTypedNestedValuesAreCopiedAcrossPublicBoundary(t *testing.T) {
|
func TestExtraParamsTypedNestedValuesAreCopiedAcrossPublicBoundary(t *testing.T) {
|
||||||
fake := &fakeLLMClient{response: &scriptorium.GenerateResponse{Content: "ok"}}
|
fake := &fakeLLMClient{response: &scriptorium.GenerateResponse{Content: "ok"}}
|
||||||
engine := newExampleEngineWithOptions(t, "./examples/schemas", scriptorium.WithLLMClient(fake))
|
engine := newExampleEngineWithOptions(t, "./examples/schemas", scriptorium.WithLLMClient(fake))
|
||||||
|
|||||||
Reference in New Issue
Block a user