111 lines
5.0 KiB
Go
111 lines
5.0 KiB
Go
package ckprompt_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"io"
|
|
"testing"
|
|
"testing/fstest"
|
|
|
|
commandkit "gitea.maximumdirect.net/eric/CommandKit"
|
|
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig"
|
|
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckprompt"
|
|
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
|
promptkit "gitea.maximumdirect.net/eric/promptkit"
|
|
)
|
|
|
|
type compositionInput struct{ Campaign commandkit.Supplied[string] }
|
|
type compositionState struct{ token int }
|
|
|
|
func TestPublicConfigurationAndDefinitionFamiliesRemainIndependent(t *testing.T) {
|
|
inputs, err := promptkit.FilesystemInputs("composition fixture", fstest.MapFS{
|
|
"config/prompts.yml": &fstest.MapFile{Data: []byte(`prompts:
|
|
briefing:
|
|
"1":
|
|
messages:
|
|
- role: user
|
|
content: "Hello"
|
|
output:
|
|
format: text
|
|
validation_mode: none
|
|
backends:
|
|
local:
|
|
endpoint: "https://example.invalid/v1"
|
|
profiles:
|
|
default:
|
|
backend: local
|
|
model: example
|
|
`)}}, "config")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Underlays: []configkit.Underlay{{Inputs: inputs}}}})
|
|
if err != nil {
|
|
t.Fatalf("construct engine: %v", err)
|
|
}
|
|
|
|
flag := commandkit.ValueFlag(commandkit.FlagMeta{Name: "campaign"}, (*string)(nil), func(value string) (string, error) { return value, nil },
|
|
func(input **compositionInput, value commandkit.Supplied[string]) { (*input).Campaign = value })
|
|
newInputCalls, newStateCalls := 0, 0
|
|
newInput := func() *compositionInput { newInputCalls++; return &compositionInput{} }
|
|
newState := func() *compositionState { newStateCalls++; return &compositionState{token: newStateCalls} }
|
|
var appInput, engineInput *compositionInput
|
|
var appState, configEngineState *compositionState
|
|
appAcquisitions, engineScopeAcquisitions, loaderCalls := 0, 0, 0
|
|
loadEngine := func(_ context.Context, _ ckconfig.Selection, input *compositionInput, state *compositionState) (*promptkit.Engine, error) {
|
|
loaderCalls++
|
|
if !input.Campaign.Set || input.Campaign.Value != "spring" {
|
|
t.Errorf("engine loader input = %#v", input)
|
|
}
|
|
if loaderCalls == 1 {
|
|
engineInput, configEngineState = input, state
|
|
}
|
|
return engine, nil
|
|
}
|
|
appScope := ckconfig.Scope[*compositionInput, *compositionState]{
|
|
Name: "app", Operations: []ckconfig.Operation{ckconfig.Validate},
|
|
Acquire: func(_ context.Context, _ ckconfig.Request, input *compositionInput, state *compositionState) (commandkit.Acquired[ckconfig.View], error) {
|
|
appAcquisitions++
|
|
appInput, appState = input, state
|
|
return commandkit.Acquired[ckconfig.View]{}, nil
|
|
},
|
|
}
|
|
engineScope := ckprompt.ConfigScope(loadEngine)
|
|
acquireEngineScope := engineScope.Acquire
|
|
engineScope.Acquire = func(ctx context.Context, request ckconfig.Request, input *compositionInput, state *compositionState) (commandkit.Acquired[ckconfig.View], error) {
|
|
engineScopeAcquisitions++
|
|
return acquireEngineScope(ctx, request, input, state)
|
|
}
|
|
configCommand := ckconfig.Command(ckconfig.Suite[*compositionInput, *compositionState]{
|
|
NewInput: newInput, NewState: newState,
|
|
Selectors: []ckconfig.Selector[*compositionInput]{{Binding: flag, Scopes: []string{"app", "promptkit"}}},
|
|
Scopes: []ckconfig.Scope[*compositionInput, *compositionState]{appScope, engineScope},
|
|
})
|
|
definitions := ckprompt.FromEngine(loadEngine)
|
|
definitions.NewInput = newInput
|
|
definitions.NewState = newState
|
|
definitions.Selectors = []commandkit.Flag[*compositionInput]{flag}
|
|
command, err := commandkit.New(commandkit.Identity{Name: "composition"}, configCommand, ckprompt.Command(definitions))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var output bytes.Buffer
|
|
outcome := command.Execute(context.Background(), []string{"config", "validate", "--scope", "all", "--campaign", "spring"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
|
if outcome.Kind != commandkit.OutcomeSuccess || appAcquisitions != 1 || engineScopeAcquisitions != 1 || loaderCalls != 1 {
|
|
t.Fatalf("configuration run = (%#v, output=%q, counts=%d/%d/%d)", outcome, output.String(), appAcquisitions, engineScopeAcquisitions, loaderCalls)
|
|
}
|
|
if appInput == nil || engineInput == nil || appInput == engineInput || appState == nil || appState != configEngineState || appState.token != 1 {
|
|
t.Fatalf("scope values were not isolated/shared as selected: inputs=%p/%p states=%p/%p", appInput, engineInput, appState, configEngineState)
|
|
}
|
|
|
|
output.Reset()
|
|
outcome = command.Execute(context.Background(), []string{"promptkit", "show", "profile", "default", "--campaign", "spring", "--format", "yaml"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
|
if outcome.Kind != commandkit.OutcomeSuccess || appAcquisitions != 1 || engineScopeAcquisitions != 1 || loaderCalls != 2 || !bytes.Contains(output.Bytes(), []byte("profile_id: default")) {
|
|
t.Fatalf("independent profile run = (%#v, output=%q, counts=%d/%d/%d)", outcome, output.String(), appAcquisitions, engineScopeAcquisitions, loaderCalls)
|
|
}
|
|
if newInputCalls != 3 || newStateCalls != 2 {
|
|
t.Fatalf("invocation factory calls input/state=%d/%d", newInputCalls, newStateCalls)
|
|
}
|
|
}
|