108 lines
5.2 KiB
Go
108 lines
5.2 KiB
Go
package ckprompt
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"go.maximumdirect.net/ckconfig"
|
|
commandkit "go.maximumdirect.net/commandkit"
|
|
promptkit "go.maximumdirect.net/promptkit"
|
|
)
|
|
|
|
func TestPreparationCommandForwardsOrderedInputsAndNativeReport(t *testing.T) {
|
|
type key struct{}
|
|
ctx := context.WithValue(context.Background(), key{}, "context")
|
|
var received PreparationRequest
|
|
calls := 0
|
|
native := &promptkit.PreparedRun{PromptID: "native", Messages: []promptkit.RenderedMessage{{Content: "rendered"}}}
|
|
app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{PrepareRequest: func(got context.Context, req PreparationRequest, _, _ struct{}) (commandkit.Acquired[*promptkit.PreparedRun], error) {
|
|
if got.Value(key{}) != "context" {
|
|
t.Fatal("invocation context lost")
|
|
}
|
|
calls++
|
|
received = req
|
|
return commandkit.Acquired[*promptkit.PreparedRun]{Value: native}, nil
|
|
}}))
|
|
var output bytes.Buffer
|
|
result := app.Execute(ctx, []string{"promptkit", "show", "prepared", "briefing", "--config=chosen", "--llm-profile=editorial", "--llm-input-file=x=first.txt", "--llm-input-text=x=a=b", "--llm-input-text=y=", "--llm-input-file=z=path", "--llm-input-text=z=last", "--llm-input-file=z=final", "--format=json"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
|
if result.Kind != commandkit.OutcomeSuccess || calls != 1 || received.Request.PromptID != "briefing" || received.Request.ProfileID != "editorial" || received.Selection.Config.Value != "chosen" || !received.Selection.Config.Set {
|
|
t.Fatalf("outcome/request = %#v %#v", result, received)
|
|
}
|
|
want := map[string]promptkit.ArtifactRef{"x": promptkit.Inline("a=b"), "y": promptkit.Inline(""), "z": promptkit.File("final")}
|
|
if !reflect.DeepEqual(received.Request.Inputs, want) {
|
|
t.Fatalf("inputs = %#v", received.Request.Inputs)
|
|
}
|
|
var report, expected any
|
|
encoded, err := json.Marshal(native)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := json.Unmarshal(encoded, &expected); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := json.Unmarshal(output.Bytes(), &report); err != nil || !reflect.DeepEqual(report, expected) {
|
|
t.Fatalf("report = %s (%v)", output.String(), err)
|
|
}
|
|
output.Reset()
|
|
result = app.Execute(ctx, []string{"promptkit", "show", "prepared", "fresh", "--format=json"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
|
if result.Kind != commandkit.OutcomeSuccess || received.Request.Inputs != nil {
|
|
t.Fatalf("fresh request = %#v %#v", result, received)
|
|
}
|
|
}
|
|
|
|
func TestPreparationErrorsAndHelpDoNotInvokeUnrelatedCallbacks(t *testing.T) {
|
|
cause := errors.New("preparation failure")
|
|
calls := 0
|
|
suite := Suite[struct{}, struct{}]{PrepareRequest: func(context.Context, PreparationRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.PreparedRun], error) {
|
|
calls++
|
|
return commandkit.Acquired[*promptkit.PreparedRun]{}, cause
|
|
}}
|
|
app := newPromptTestApp(t, Command(suite))
|
|
var help bytes.Buffer
|
|
result := app.Execute(context.Background(), []string{"promptkit", "show", "prepared", "--help"}, commandkit.Streams{Out: &help, Err: io.Discard})
|
|
if result.Kind != commandkit.OutcomeSuccess || calls != 0 || !bytes.Contains(help.Bytes(), []byte("resource I/O")) || !bytes.Contains(help.Bytes(), []byte("rendered application content")) {
|
|
t.Fatalf("help = %#v %q", result, help.String())
|
|
}
|
|
result = app.Execute(context.Background(), []string{"promptkit", "show", "prepared", "missing"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
|
if result.Kind != commandkit.OutcomeOperational || !errors.Is(result.Cause, cause) || calls != 1 {
|
|
t.Fatalf("failure = %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestNativePreparationBuilderAndErrorIdentity(t *testing.T) {
|
|
client := &validationFailOnGenerate{}
|
|
engine := validationTestEngine(t, adapterPrompt, promptkit.WithLLMClient(client))
|
|
built := promptkit.RunRequest{PromptID: "briefing", ProfileID: "default", Inputs: map[string]promptkit.ArtifactRef{"topic": promptkit.Inline("builder value")}}
|
|
ctx := context.Background()
|
|
want, err := engine.Prepare(ctx, built)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
calls := 0
|
|
suite := WithRequestPreparation(Suite[struct{}, struct{}]{}, func(context.Context, ckconfig.Selection, struct{}, struct{}) (*promptkit.Engine, error) {
|
|
return engine, nil
|
|
}, func(_ context.Context, seed PreparationRequest, _, _ struct{}) (promptkit.RunRequest, error) {
|
|
calls++
|
|
if seed.Request.PromptID != "seed" {
|
|
t.Fatalf("seed = %#v", seed)
|
|
}
|
|
return built, nil
|
|
})
|
|
got, err := suite.PrepareRequest(ctx, PreparationRequest{Request: promptkit.RunRequest{PromptID: "seed"}}, struct{}{}, struct{}{})
|
|
if err != nil || calls != 1 || got.Value == nil || got.Value.PromptID != want.PromptID || !reflect.DeepEqual(got.Value.Messages, want.Messages) || client.calls != 0 {
|
|
t.Fatalf("prepared = %#v, err = %v, calls = %d, model calls = %d", got, err, calls, client.calls)
|
|
}
|
|
withoutBuilder := WithRequestPreparation(Suite[struct{}, struct{}]{}, func(context.Context, ckconfig.Selection, struct{}, struct{}) (*promptkit.Engine, error) {
|
|
return engine, nil
|
|
}, nil)
|
|
_, err = withoutBuilder.PrepareRequest(ctx, PreparationRequest{Request: promptkit.RunRequest{PromptID: "missing"}}, struct{}{}, struct{}{})
|
|
if !errors.Is(err, promptkit.ErrPromptNotFound) {
|
|
t.Fatalf("native error = %v", err)
|
|
}
|
|
}
|