Adopt Promptkit at application boundaries

This commit is contained in:
2026-07-28 14:04:29 +00:00
parent 309fe9b7ea
commit e13610481d
10 changed files with 169 additions and 166 deletions

1
go.mod
View File

@@ -3,6 +3,7 @@ module gitea.maximumdirect.net/eric/scriptorium
go 1.25.5
require (
gitea.maximumdirect.net/eric/promptkit v0.1.0
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
gopkg.in/yaml.v3 v3.0.1
)

2
go.sum
View File

@@ -1,3 +1,5 @@
gitea.maximumdirect.net/eric/promptkit v0.1.0 h1:vuKeBxkiY8E54LRFbLQFjlJJCiOfMvB1++DYBCrD/ug=
gitea.maximumdirect.net/eric/promptkit v0.1.0/go.mod h1:R95NM6fbMDGDC0/UomgnSBP6ui2ns+8SZb8bESNvrDQ=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=

View File

@@ -12,7 +12,7 @@ import (
"strings"
"time"
"gitea.maximumdirect.net/eric/scriptorium"
"gitea.maximumdirect.net/eric/promptkit"
httpadapter "gitea.maximumdirect.net/eric/scriptorium/internal/adapter/http"
appconfig "gitea.maximumdirect.net/eric/scriptorium/internal/config"
"gitea.maximumdirect.net/eric/scriptorium/internal/defaults"
@@ -209,7 +209,7 @@ func serveCommand(args []string, stderr io.Writer) int {
promptDir: cfg.promptDir,
profileDir: cfg.profileDir,
schemaDir: cfg.schemaDir,
}, scriptorium.WithArtifactReader(artifactReader))
}, promptkit.WithArtifactReader(artifactReader))
if err != nil {
fmt.Fprintf(stderr, "engine error: %v\n", err)
return ExitRuntimeError
@@ -538,36 +538,36 @@ func validateRequiredLibraryDirs(promptDir string) error {
return nil
}
func newEngine(cfg *runConfig, options ...scriptorium.Option) (*scriptorium.Engine, error) {
return scriptorium.NewEngine(scriptorium.Config{
func newEngine(cfg *runConfig, options ...promptkit.Option) (*promptkit.Engine, error) {
return promptkit.NewEngine(promptkit.Config{
PromptDir: cfg.promptDir,
ProfileDir: cfg.profileDir,
SchemaDir: cfg.schemaDir,
}, options...)
}
func buildRunRequestFromConfig(cfg *runConfig) (scriptorium.RunRequest, error) {
func buildRunRequestFromConfig(cfg *runConfig) (promptkit.RunRequest, error) {
inputMappings, err := parseMappings(cfg.inputRaw, false)
if err != nil {
return scriptorium.RunRequest{}, fmt.Errorf("input parse error: %w", err)
return promptkit.RunRequest{}, fmt.Errorf("input parse error: %w", err)
}
varMappings := map[string]string{}
if len(cfg.varRaw) > 0 {
varMappings, err = parseMappings(cfg.varRaw, false)
if err != nil {
return scriptorium.RunRequest{}, fmt.Errorf("var parse error: %w", err)
return promptkit.RunRequest{}, fmt.Errorf("var parse error: %w", err)
}
}
inputs := make(map[string]scriptorium.ArtifactRef, len(inputMappings))
inputs := make(map[string]promptkit.ArtifactRef, len(inputMappings))
for name, path := range inputMappings {
inputs[name] = scriptorium.File(path)
inputs[name] = promptkit.File(path)
}
var modelOverride *scriptorium.ExecutionTargetOverride
var modelOverride *promptkit.ExecutionTargetOverride
if cfg.llmBaseURLSet || cfg.modelSet || cfg.temperatureSet || cfg.maxTokensSet || cfg.topPSet || cfg.apiKeyEnvSet || cfg.timeoutSet {
modelOverride = &scriptorium.ExecutionTargetOverride{
modelOverride = &promptkit.ExecutionTargetOverride{
Endpoint: cfg.llmBaseURL,
Model: cfg.model,
APIKeyEnv: cfg.apiKeyEnv,
@@ -587,7 +587,7 @@ func buildRunRequestFromConfig(cfg *runConfig) (scriptorium.RunRequest, error) {
}
}
return scriptorium.RunRequest{
return promptkit.RunRequest{
PromptID: cfg.promptID,
ProfileID: cfg.profileID,
Inputs: inputs,
@@ -651,17 +651,17 @@ func writeOutput(stdout io.Writer, outputPath string, body []byte) error {
return os.WriteFile(outputPath, body, 0644)
}
func determineExitCode(runErr error, result *scriptorium.RunResult) int {
func determineExitCode(runErr error, result *promptkit.RunResult) int {
if runErr != nil {
return ExitRuntimeError
}
if result != nil && result.Validation.Status == scriptorium.ValidationFailed {
if result != nil && result.Validation.Status == promptkit.ValidationFailed {
return ExitValidationFailed
}
return ExitOK
}
func printSummary(stderr io.Writer, res *scriptorium.RunResult) {
func printSummary(stderr io.Writer, res *promptkit.RunResult) {
if res == nil {
return
}

View File

@@ -17,7 +17,7 @@ import (
"testing"
"time"
"gitea.maximumdirect.net/eric/scriptorium"
"gitea.maximumdirect.net/eric/promptkit"
appconfig "gitea.maximumdirect.net/eric/scriptorium/internal/config"
"gitea.maximumdirect.net/eric/scriptorium/internal/defaults"
renderformat "gitea.maximumdirect.net/eric/scriptorium/internal/format"
@@ -765,13 +765,13 @@ func TestDetermineExitCode(t *testing.T) {
if got := determineExitCode(errors.New("boom"), nil); got != ExitRuntimeError {
t.Fatalf("expected runtime exit code, got %d", got)
}
if got := determineExitCode(nil, &scriptorium.RunResult{Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationFailed}}); got != ExitValidationFailed {
if got := determineExitCode(nil, &promptkit.RunResult{Validation: promptkit.ValidationResult{Status: promptkit.ValidationFailed}}); got != ExitValidationFailed {
t.Fatalf("expected validation exit code, got %d", got)
}
if got := determineExitCode(nil, &scriptorium.RunResult{Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationPassed}}); got != ExitOK {
if got := determineExitCode(nil, &promptkit.RunResult{Validation: promptkit.ValidationResult{Status: promptkit.ValidationPassed}}); got != ExitOK {
t.Fatalf("expected success exit code for passed validation, got %d", got)
}
if got := determineExitCode(nil, &scriptorium.RunResult{Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationSkipped}}); got != ExitOK {
if got := determineExitCode(nil, &promptkit.RunResult{Validation: promptkit.ValidationResult{Status: promptkit.ValidationSkipped}}); got != ExitOK {
t.Fatalf("expected success exit code for skipped validation, got %d", got)
}
}
@@ -1242,12 +1242,12 @@ func TestWriteOutputAndSummaryUseSeparateWriters(t *testing.T) {
if err := writeOutput(&stdout, "", []byte("artifact-body")); err != nil {
t.Fatalf("unexpected writeOutput error: %v", err)
}
printSummary(&stderr, &scriptorium.RunResult{
printSummary(&stderr, &promptkit.RunResult{
PromptID: "p",
PromptVersion: "1",
SelectedProfileID: "exec",
ModelName: "m",
Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationPassed, Mode: scriptorium.ValidationBasic},
Validation: promptkit.ValidationResult{Status: promptkit.ValidationPassed, Mode: promptkit.ValidationBasic},
RenderedPromptHash: "h",
InputHashes: map[string]string{"in": "x"},
})
@@ -1266,15 +1266,15 @@ func TestWriteOutputAndSummaryUseSeparateWriters(t *testing.T) {
func TestPrintSummaryIncludesCacheUsageWhenPresent(t *testing.T) {
var stderr bytes.Buffer
printSummary(&stderr, &scriptorium.RunResult{
printSummary(&stderr, &promptkit.RunResult{
PromptID: "p",
PromptVersion: "1",
SelectedProfileID: "exec",
ModelName: "m",
Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationPassed, Mode: scriptorium.ValidationBasic},
Validation: promptkit.ValidationResult{Status: promptkit.ValidationPassed, Mode: promptkit.ValidationBasic},
RenderedPromptHash: "h",
InputHashes: map[string]string{"in": "x"},
Usage: scriptorium.TokenUsage{
Usage: promptkit.TokenUsage{
PromptTokens: 10,
CompletionTokens: 5,
TotalTokens: 15,

View File

@@ -11,7 +11,7 @@ import (
"path/filepath"
"strings"
"gitea.maximumdirect.net/eric/scriptorium"
"gitea.maximumdirect.net/eric/promptkit"
)
var (
@@ -25,7 +25,7 @@ const fallbackArtifactContentType = "text/plain"
// NewRestrictedArtifactReader creates the HTTP artifact reader for a rooted
// filesystem and optional byte limit. An empty root permits inline artifacts
// but denies file references; a zero limit permits artifacts of any size.
func NewRestrictedArtifactReader(root string, maxBytes int64) (scriptorium.ArtifactReader, error) {
func NewRestrictedArtifactReader(root string, maxBytes int64) (promptkit.ArtifactReader, error) {
if maxBytes < 0 {
return nil, fmt.Errorf("artifact size limit must be greater than or equal to 0")
}
@@ -46,9 +46,9 @@ type restrictedArtifactReader struct {
maxBytes int64
}
var _ scriptorium.ArtifactReader = (*restrictedArtifactReader)(nil)
var _ promptkit.ArtifactReader = (*restrictedArtifactReader)(nil)
func (r *restrictedArtifactReader) Read(ctx context.Context, ref scriptorium.ArtifactRef) (*scriptorium.Artifact, error) {
func (r *restrictedArtifactReader) Read(ctx context.Context, ref promptkit.ArtifactRef) (*promptkit.Artifact, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
@@ -56,22 +56,22 @@ func (r *restrictedArtifactReader) Read(ctx context.Context, ref scriptorium.Art
}
switch ref.Type {
case scriptorium.ArtifactRefInline:
case promptkit.ArtifactRefInline:
return readInlineArtifact(ref)
case scriptorium.ArtifactRefFile:
case promptkit.ArtifactRefFile:
return r.readFileArtifact(ref)
default:
return nil, fmt.Errorf("unsupported artifact reference type %q", ref.Type)
}
}
func readInlineArtifact(ref scriptorium.ArtifactRef) (*scriptorium.Artifact, error) {
func readInlineArtifact(ref promptkit.ArtifactRef) (*promptkit.Artifact, error) {
if ref.Body == "" {
return nil, errors.New("inline artifact body is required")
}
body := []byte(ref.Body)
return &scriptorium.Artifact{
return &promptkit.Artifact{
ContentType: fallbackArtifactContentType,
Body: body,
Size: int64(len(body)),
@@ -80,7 +80,7 @@ func readInlineArtifact(ref scriptorium.ArtifactRef) (*scriptorium.Artifact, err
}, nil
}
func (r *restrictedArtifactReader) readFileArtifact(ref scriptorium.ArtifactRef) (*scriptorium.Artifact, error) {
func (r *restrictedArtifactReader) readFileArtifact(ref promptkit.ArtifactRef) (*promptkit.Artifact, error) {
if ref.URI == "" {
return nil, errors.New("file artifact path is required")
}
@@ -119,7 +119,7 @@ func (r *restrictedArtifactReader) resolveLexicalPath(rawPath string) (string, e
return absCandidate, nil
}
func readArtifactFile(path string, maxBytes int64) (*scriptorium.Artifact, error) {
func readArtifactFile(path string, maxBytes int64) (*promptkit.Artifact, error) {
file, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("failed to read file %s: %w", path, err)
@@ -150,7 +150,7 @@ func readArtifactFile(path string, maxBytes int64) (*scriptorium.Artifact, error
if contentType == "" {
contentType = fallbackArtifactContentType
}
return &scriptorium.Artifact{
return &promptkit.Artifact{
Name: filepath.Base(path),
ContentType: contentType,
Body: body,

View File

@@ -8,7 +8,7 @@ import (
"path/filepath"
"testing"
"gitea.maximumdirect.net/eric/scriptorium"
"gitea.maximumdirect.net/eric/promptkit"
)
func TestRestrictedArtifactReaderReadsContainedFiles(t *testing.T) {
@@ -37,9 +37,9 @@ func TestRestrictedArtifactReaderReadsContainedFiles(t *testing.T) {
t.Fatalf("construct restricted reader: %v", err)
}
for _, ref := range []scriptorium.ArtifactRef{
{Type: scriptorium.ArtifactRefFile, URI: "nested/../input.html"},
{Type: scriptorium.ArtifactRefFile, URI: inputPath},
for _, ref := range []promptkit.ArtifactRef{
{Type: promptkit.ArtifactRefFile, URI: "nested/../input.html"},
{Type: promptkit.ArtifactRefFile, URI: inputPath},
} {
artifact, err := reader.Read(context.Background(), ref)
if err != nil {
@@ -56,7 +56,7 @@ func TestRestrictedArtifactReaderReadsContainedFiles(t *testing.T) {
}
}
artifact, err := reader.Read(context.Background(), scriptorium.File("input.unknown"))
artifact, err := reader.Read(context.Background(), promptkit.File("input.unknown"))
if err != nil {
t.Fatalf("read unknown-extension path: %v", err)
}
@@ -64,9 +64,9 @@ func TestRestrictedArtifactReaderReadsContainedFiles(t *testing.T) {
t.Fatalf("unexpected fallback content type: %q", artifact.ContentType)
}
for _, ref := range []scriptorium.ArtifactRef{
{Type: scriptorium.ArtifactRefFile, URI: filepath.Join("..", filepath.Base(outside), "secret.txt")},
{Type: scriptorium.ArtifactRefFile, URI: filepath.Join(outside, "secret.txt")},
for _, ref := range []promptkit.ArtifactRef{
{Type: promptkit.ArtifactRefFile, URI: filepath.Join("..", filepath.Base(outside), "secret.txt")},
{Type: promptkit.ArtifactRefFile, URI: filepath.Join(outside, "secret.txt")},
} {
_, err := reader.Read(context.Background(), ref)
if !errors.Is(err, ErrFileOutsideRoot) {
@@ -90,7 +90,7 @@ func TestRestrictedArtifactReaderFollowsSymlinkAfterLexicalCheck(t *testing.T) {
if err != nil {
t.Fatalf("construct restricted reader: %v", err)
}
artifact, err := reader.Read(context.Background(), scriptorium.File("linked.txt"))
artifact, err := reader.Read(context.Background(), promptkit.File("linked.txt"))
if err != nil {
t.Fatalf("read symlink inside root: %v", err)
}
@@ -105,7 +105,7 @@ func TestRestrictedArtifactReaderWithoutRootDeniesFiles(t *testing.T) {
t.Fatalf("construct rootless reader: %v", err)
}
artifact, err := reader.Read(context.Background(), scriptorium.Inline("inline"))
artifact, err := reader.Read(context.Background(), promptkit.Inline("inline"))
if err != nil {
t.Fatalf("read inline artifact: %v", err)
}
@@ -113,7 +113,7 @@ func TestRestrictedArtifactReaderWithoutRootDeniesFiles(t *testing.T) {
t.Fatalf("unexpected inline artifact: %#v", artifact)
}
_, err = reader.Read(context.Background(), scriptorium.File("input.txt"))
_, err = reader.Read(context.Background(), promptkit.File("input.txt"))
if !errors.Is(err, ErrFileNotAllowed) {
t.Fatalf("expected ErrFileNotAllowed, got %v", err)
}
@@ -132,11 +132,11 @@ func TestRestrictedArtifactReaderEnforcesLimits(t *testing.T) {
if err != nil {
t.Fatalf("construct limited reader: %v", err)
}
artifact, err := reader.Read(context.Background(), scriptorium.File("exact.txt"))
artifact, err := reader.Read(context.Background(), promptkit.File("exact.txt"))
if err != nil || string(artifact.Body) != "12345" {
t.Fatalf("expected exact-limit artifact, got %#v and %v", artifact, err)
}
_, err = reader.Read(context.Background(), scriptorium.File("large.txt"))
_, err = reader.Read(context.Background(), promptkit.File("large.txt"))
if !errors.Is(err, ErrFileTooLarge) {
t.Fatalf("expected ErrFileTooLarge, got %v", err)
}
@@ -145,7 +145,7 @@ func TestRestrictedArtifactReaderEnforcesLimits(t *testing.T) {
if err != nil {
t.Fatalf("construct unlimited reader: %v", err)
}
artifact, err = unlimited.Read(context.Background(), scriptorium.File("large.txt"))
artifact, err = unlimited.Read(context.Background(), promptkit.File("large.txt"))
if err != nil || string(artifact.Body) != "123456" {
t.Fatalf("expected unlimited artifact, got %#v and %v", artifact, err)
}
@@ -163,9 +163,9 @@ func TestRestrictedArtifactReaderRejectsCanceledAndMalformedReferences(t *testin
canceledCtx, cancel := context.WithCancel(context.Background())
cancel()
for _, ref := range []scriptorium.ArtifactRef{
scriptorium.Inline("input"),
scriptorium.File("input.txt"),
for _, ref := range []promptkit.ArtifactRef{
promptkit.Inline("input"),
promptkit.File("input.txt"),
} {
_, err := reader.Read(canceledCtx, ref)
if !errors.Is(err, context.Canceled) {
@@ -173,10 +173,10 @@ func TestRestrictedArtifactReaderRejectsCanceledAndMalformedReferences(t *testin
}
}
for _, ref := range []scriptorium.ArtifactRef{
{Type: scriptorium.ArtifactRefType("unsupported")},
{Type: scriptorium.ArtifactRefInline},
{Type: scriptorium.ArtifactRefFile},
for _, ref := range []promptkit.ArtifactRef{
{Type: promptkit.ArtifactRefType("unsupported")},
{Type: promptkit.ArtifactRefInline},
{Type: promptkit.ArtifactRefFile},
} {
if _, err := reader.Read(context.Background(), ref); err == nil {
t.Fatalf("expected malformed reference %#v to fail", ref)

View File

@@ -8,12 +8,12 @@ import (
"net/http"
"strings"
"gitea.maximumdirect.net/eric/scriptorium"
"gitea.maximumdirect.net/eric/promptkit"
"gitea.maximumdirect.net/eric/scriptorium/internal/defaults"
)
type Runner interface {
Run(ctx context.Context, req scriptorium.RunRequest) (*scriptorium.RunResult, error)
Run(ctx context.Context, req promptkit.RunRequest) (*promptkit.RunResult, error)
}
type Handler struct {
@@ -81,21 +81,21 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
mappedInputs := make(map[string]scriptorium.ArtifactRef, len(req.Inputs))
mappedInputs := make(map[string]promptkit.ArtifactRef, len(req.Inputs))
for name, in := range req.Inputs {
mappedInputs[name] = scriptorium.ArtifactRef{
Type: scriptorium.ArtifactRefType(in.Type),
mappedInputs[name] = promptkit.ArtifactRef{
Type: promptkit.ArtifactRefType(in.Type),
URI: in.URI,
Body: in.Body,
}
}
var model *scriptorium.ExecutionTargetOverride
var model *promptkit.ExecutionTargetOverride
if req.Model != nil {
model = executionTargetOverrideFromModelOverrideDTO(req.Model)
}
res, err := h.runner.Run(r.Context(), scriptorium.RunRequest{
res, err := h.runner.Run(r.Context(), promptkit.RunRequest{
PromptID: req.PromptID,
PromptVersion: req.PromptVersion,
ProfileID: req.ProfileID,
@@ -152,11 +152,11 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
writeLimitedJSON(w, http.StatusOK, resp, h.options.MaxResponseBytes)
}
func executionTargetOverrideFromModelOverrideDTO(dto *modelOverrideRequestDTO) *scriptorium.ExecutionTargetOverride {
func executionTargetOverrideFromModelOverrideDTO(dto *modelOverrideRequestDTO) *promptkit.ExecutionTargetOverride {
if dto == nil {
return nil
}
return &scriptorium.ExecutionTargetOverride{
return &promptkit.ExecutionTargetOverride{
Endpoint: dto.Endpoint,
Model: dto.Model,
Temperature: dto.Temperature,
@@ -170,7 +170,7 @@ func executionTargetOverrideFromModelOverrideDTO(dto *modelOverrideRequestDTO) *
}
}
func modelParamsDTOFromExecutionTarget(target scriptorium.ExecutionTarget) modelParamsDTO {
func modelParamsDTOFromExecutionTarget(target promptkit.ExecutionTarget) modelParamsDTO {
return modelParamsDTO{
Endpoint: target.Endpoint,
Model: target.Model,
@@ -185,7 +185,7 @@ func modelParamsDTOFromExecutionTarget(target scriptorium.ExecutionTarget) model
}
}
func mapValidation(v scriptorium.ValidationResult) validationDTO {
func mapValidation(v promptkit.ValidationResult) validationDTO {
return validationDTO{
Status: string(v.Status),
Mode: string(v.Mode),
@@ -198,31 +198,31 @@ func mapValidation(v scriptorium.ValidationResult) validationDTO {
func mapRunError(err error) (int, string, string) {
switch {
case errors.Is(err, scriptorium.ErrPromptNotFound):
case errors.Is(err, promptkit.ErrPromptNotFound):
return http.StatusNotFound, "prompt_not_found", "prompt definition not found"
case errors.Is(err, scriptorium.ErrProfileNotFound):
case errors.Is(err, promptkit.ErrProfileNotFound):
return http.StatusNotFound, "profile_not_found", "execution profile not found"
case errors.Is(err, scriptorium.ErrProfileRequired):
case errors.Is(err, promptkit.ErrProfileRequired):
return http.StatusBadRequest, "profile_required", "profile_id is required when prompt default_profile is not set"
case errors.Is(err, scriptorium.ErrAPIKeyEnvMissing):
case errors.Is(err, promptkit.ErrAPIKeyEnvMissing):
return http.StatusBadRequest, "api_key_env_missing", "api_key_env is set but the environment variable is missing"
case errors.Is(err, scriptorium.ErrPromptLoad):
case errors.Is(err, promptkit.ErrPromptLoad):
return http.StatusBadRequest, "prompt_load_failed", "failed to load prompt definition"
case errors.Is(err, scriptorium.ErrProfileLoad):
case errors.Is(err, promptkit.ErrProfileLoad):
return http.StatusBadRequest, "profile_load_failed", "failed to load execution profile"
case errors.Is(err, scriptorium.ErrInvalidRequest):
case errors.Is(err, promptkit.ErrInvalidRequest):
return http.StatusBadRequest, "invalid_request", "invalid run request"
case errors.Is(err, ErrFileNotAllowed), errors.Is(err, ErrFileOutsideRoot):
return http.StatusBadRequest, "artifact_not_allowed", "file input artifact is not allowed"
case errors.Is(err, ErrFileTooLarge):
return http.StatusRequestEntityTooLarge, "artifact_too_large", "file input artifact is too large"
case errors.Is(err, scriptorium.ErrArtifactLoad):
case errors.Is(err, promptkit.ErrArtifactLoad):
return http.StatusBadRequest, "artifact_read_failed", "failed to read input artifact"
case errors.Is(err, scriptorium.ErrPromptRender):
case errors.Is(err, promptkit.ErrPromptRender):
return http.StatusBadRequest, "prompt_render_failed", "failed to render prompt"
case errors.Is(err, scriptorium.ErrLLMGenerate):
case errors.Is(err, promptkit.ErrLLMGenerate):
return http.StatusBadGateway, "llm_failed", "model generation request failed"
case errors.Is(err, scriptorium.ErrValidation):
case errors.Is(err, promptkit.ErrValidation):
return http.StatusInternalServerError, "validation_runtime_failed", "validation runtime failed"
default:
return http.StatusInternalServerError, "internal_error", "internal server error"

View File

@@ -14,16 +14,16 @@ import (
"testing"
"time"
"gitea.maximumdirect.net/eric/scriptorium"
"gitea.maximumdirect.net/eric/promptkit"
)
type fakeRunner struct {
result *scriptorium.RunResult
result *promptkit.RunResult
err error
last scriptorium.RunRequest
last promptkit.RunRequest
}
func (f *fakeRunner) Run(ctx context.Context, req scriptorium.RunRequest) (*scriptorium.RunResult, error) {
func (f *fakeRunner) Run(ctx context.Context, req promptkit.RunRequest) (*promptkit.RunResult, error) {
f.last = req
if f.err != nil {
return nil, f.err
@@ -37,7 +37,7 @@ func TestMaintainedHTTPRunExampleMatchesRequestContract(t *testing.T) {
t.Fatalf("read maintained HTTP request example: %v", err)
}
runner := &fakeRunner{result: &scriptorium.RunResult{}}
runner := &fakeRunner{result: &promptkit.RunResult{}}
h := NewHandler(runner)
req := httptest.NewRequest(http.MethodPost, "/v1/runs", bytes.NewReader(body))
w := httptest.NewRecorder()
@@ -65,8 +65,8 @@ func TestMaintainedHTTPRunExampleMatchesRequestContract(t *testing.T) {
type handlerLLMClient struct{}
func (handlerLLMClient) Generate(ctx context.Context, req scriptorium.GenerateRequest) (*scriptorium.GenerateResponse, error) {
return &scriptorium.GenerateResponse{Content: "ok"}, nil
func (handlerLLMClient) Generate(ctx context.Context, req promptkit.GenerateRequest) (*promptkit.GenerateResponse, error) {
return &promptkit.GenerateResponse{Content: "ok"}, nil
}
func TestHandlerPostRunsSuccessWithExplicitProfileID(t *testing.T) {
@@ -75,16 +75,16 @@ func TestHandlerPostRunsSuccessWithExplicitProfileID(t *testing.T) {
const envName = "SCRIPTORIUM_API_KEY"
const secret = "never-include-me"
r := &fakeRunner{result: &scriptorium.RunResult{
r := &fakeRunner{result: &promptkit.RunResult{
RunID: "11111111-1111-4111-8111-111111111111",
Artifact: scriptorium.Artifact{
Artifact: promptkit.Artifact{
Name: "output",
ContentType: "text/plain",
Body: []byte("hello"),
Size: 5,
Hash: "abc",
},
Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationPassed, Mode: scriptorium.ValidationBasic, IsValid: true},
Validation: promptkit.ValidationResult{Status: promptkit.ValidationPassed, Mode: promptkit.ValidationBasic, IsValid: true},
PromptID: "prompt-1",
PromptVersion: "1.0.0",
PromptHash: "phash",
@@ -92,7 +92,7 @@ func TestHandlerPostRunsSuccessWithExplicitProfileID(t *testing.T) {
SelectedProfileID: "exec-default",
ModelName: "m1",
Endpoint: "http://llm/v1",
EffectiveModelParams: scriptorium.ExecutionTarget{
EffectiveModelParams: promptkit.ExecutionTarget{
Endpoint: "http://llm/v1",
Model: "m1",
Temperature: 0.2,
@@ -103,7 +103,7 @@ func TestHandlerPostRunsSuccessWithExplicitProfileID(t *testing.T) {
APIKeyEnv: envName,
},
InputHashes: map[string]string{"transcript": "h1"},
Usage: scriptorium.TokenUsage{
Usage: promptkit.TokenUsage{
PromptTokens: 1,
CompletionTokens: 2,
TotalTokens: 3,
@@ -292,13 +292,13 @@ func TestHandlerFileRefsOutsideArtifactRootAreRejected(t *testing.T) {
}
func TestHandlerPostRunsSuccessUsingPromptDefaultProfile(t *testing.T) {
r := &fakeRunner{result: &scriptorium.RunResult{
Artifact: scriptorium.Artifact{Body: []byte("ok")},
r := &fakeRunner{result: &promptkit.RunResult{
Artifact: promptkit.Artifact{Body: []byte("ok")},
PromptID: "prompt-1",
PromptVersion: "1.0.0",
SelectedProfileID: "prompt-default",
Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationPassed, Mode: scriptorium.ValidationBasic, IsValid: true},
EffectiveModelParams: scriptorium.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1"},
Validation: promptkit.ValidationResult{Status: promptkit.ValidationPassed, Mode: promptkit.ValidationBasic, IsValid: true},
EffectiveModelParams: promptkit.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1"},
}}
h := NewHandler(r)
@@ -327,10 +327,10 @@ func TestHandlerPostRunsSuccessUsingPromptDefaultProfile(t *testing.T) {
}
func TestHandlerModelOverrideMapsAllSupportedExecutionFields(t *testing.T) {
r := &fakeRunner{result: &scriptorium.RunResult{
Artifact: scriptorium.Artifact{Body: []byte("ok")},
Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationPassed, Mode: scriptorium.ValidationBasic, IsValid: true},
EffectiveModelParams: scriptorium.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1"},
r := &fakeRunner{result: &promptkit.RunResult{
Artifact: promptkit.Artifact{Body: []byte("ok")},
Validation: promptkit.ValidationResult{Status: promptkit.ValidationPassed, Mode: promptkit.ValidationBasic, IsValid: true},
EffectiveModelParams: promptkit.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1"},
}}
h := NewHandler(r)
@@ -387,10 +387,10 @@ func TestHandlerModelOverrideMapsAllSupportedExecutionFields(t *testing.T) {
}
func TestHandlerModelOverrideAcceptsJSONCompatibleExtraParams(t *testing.T) {
r := &fakeRunner{result: &scriptorium.RunResult{
Artifact: scriptorium.Artifact{Body: []byte("ok")},
Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationPassed, Mode: scriptorium.ValidationBasic, IsValid: true},
EffectiveModelParams: scriptorium.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1"},
r := &fakeRunner{result: &promptkit.RunResult{
Artifact: promptkit.Artifact{Body: []byte("ok")},
Validation: promptkit.ValidationResult{Status: promptkit.ValidationPassed, Mode: promptkit.ValidationBasic, IsValid: true},
EffectiveModelParams: promptkit.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1"},
}}
h := NewHandler(r)
@@ -431,10 +431,10 @@ func TestHandlerModelOverrideAcceptsJSONCompatibleExtraParams(t *testing.T) {
}
func TestHandlerModelOverrideExplicitZeroTemperatureMapsAsPresent(t *testing.T) {
r := &fakeRunner{result: &scriptorium.RunResult{
Artifact: scriptorium.Artifact{Body: []byte("ok")},
Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationPassed, Mode: scriptorium.ValidationBasic, IsValid: true},
EffectiveModelParams: scriptorium.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1", Temperature: 0},
r := &fakeRunner{result: &promptkit.RunResult{
Artifact: promptkit.Artifact{Body: []byte("ok")},
Validation: promptkit.ValidationResult{Status: promptkit.ValidationPassed, Mode: promptkit.ValidationBasic, IsValid: true},
EffectiveModelParams: promptkit.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1", Temperature: 0},
}}
h := NewHandler(r)
@@ -458,10 +458,10 @@ func TestHandlerModelOverrideExplicitZeroTemperatureMapsAsPresent(t *testing.T)
}
func TestHandlerModelOverrideOmittedTemperatureMapsAsAbsent(t *testing.T) {
r := &fakeRunner{result: &scriptorium.RunResult{
Artifact: scriptorium.Artifact{Body: []byte("ok")},
Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationPassed, Mode: scriptorium.ValidationBasic, IsValid: true},
EffectiveModelParams: scriptorium.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1", Temperature: 0.7},
r := &fakeRunner{result: &promptkit.RunResult{
Artifact: promptkit.Artifact{Body: []byte("ok")},
Validation: promptkit.ValidationResult{Status: promptkit.ValidationPassed, Mode: promptkit.ValidationBasic, IsValid: true},
EffectiveModelParams: promptkit.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1", Temperature: 0.7},
}}
h := NewHandler(r)
@@ -495,16 +495,16 @@ func TestHandlerModelOverrideOmittedTemperatureMapsAsAbsent(t *testing.T) {
}
func TestHandlerResponseMetadataModelParamsIncludesAllSupportedFields(t *testing.T) {
r := &fakeRunner{result: &scriptorium.RunResult{
Artifact: scriptorium.Artifact{
r := &fakeRunner{result: &promptkit.RunResult{
Artifact: promptkit.Artifact{
Name: "output",
ContentType: "text/plain",
Body: []byte("ok"),
Size: 2,
Hash: "abc",
},
Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationPassed, Mode: scriptorium.ValidationBasic, IsValid: true},
EffectiveModelParams: scriptorium.ExecutionTarget{
Validation: promptkit.ValidationResult{Status: promptkit.ValidationPassed, Mode: promptkit.ValidationBasic, IsValid: true},
EffectiveModelParams: promptkit.ExecutionTarget{
Endpoint: "http://llm/v1",
Model: "gpt-test",
Temperature: 0.4,
@@ -624,10 +624,10 @@ func TestHandlerMalformedJSONBelowLimitStillBadRequest(t *testing.T) {
}
func TestHandlerResponseTooLarge(t *testing.T) {
h := NewHandlerWithOptions(&fakeRunner{result: &scriptorium.RunResult{
Artifact: scriptorium.Artifact{Body: []byte(strings.Repeat("x", 128))},
Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationPassed, Mode: scriptorium.ValidationBasic, IsValid: true},
EffectiveModelParams: scriptorium.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1"},
h := NewHandlerWithOptions(&fakeRunner{result: &promptkit.RunResult{
Artifact: promptkit.Artifact{Body: []byte(strings.Repeat("x", 128))},
Validation: promptkit.ValidationResult{Status: promptkit.ValidationPassed, Mode: promptkit.ValidationBasic, IsValid: true},
EffectiveModelParams: promptkit.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1"},
}}, HandlerOptions{MaxRequestBytes: 1024, MaxResponseBytes: 64})
req := httptest.NewRequest(http.MethodPost, "/v1/runs", bytes.NewBufferString(`{"prompt_id":"p","inputs":{"x":{"type":"file","uri":"a"}}}`))
w := httptest.NewRecorder()
@@ -638,11 +638,11 @@ func TestHandlerResponseTooLarge(t *testing.T) {
}
func TestHandlerRawOutputDoesNotBypassResponseLimit(t *testing.T) {
h := NewHandlerWithOptions(&fakeRunner{result: &scriptorium.RunResult{
Artifact: scriptorium.Artifact{Body: []byte("ok")},
h := NewHandlerWithOptions(&fakeRunner{result: &promptkit.RunResult{
Artifact: promptkit.Artifact{Body: []byte("ok")},
RawOutput: strings.Repeat("raw", 80),
Validation: scriptorium.ValidationResult{Status: scriptorium.ValidationPassed, Mode: scriptorium.ValidationBasic, IsValid: true},
EffectiveModelParams: scriptorium.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1"},
Validation: promptkit.ValidationResult{Status: promptkit.ValidationPassed, Mode: promptkit.ValidationBasic, IsValid: true},
EffectiveModelParams: promptkit.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1"},
}}, HandlerOptions{MaxRequestBytes: 1024, MaxResponseBytes: 128})
req := httptest.NewRequest(http.MethodPost, "/v1/runs", bytes.NewBufferString(`{
"prompt_id":"p",
@@ -710,20 +710,20 @@ func TestHandlerPublicErrorMapping(t *testing.T) {
message string
avoidCause string
}{
{name: "prompt not found", err: scriptorium.ErrPromptNotFound, status: http.StatusNotFound, code: "prompt_not_found", message: "prompt definition not found"},
{name: "prompt load", err: wrap(scriptorium.ErrPromptLoad, fmt.Errorf("read failed")), status: http.StatusBadRequest, code: "prompt_load_failed", message: "failed to load prompt definition", avoidCause: "read failed"},
{name: "missing profile/default", err: wrap(scriptorium.ErrProfileRequired, scriptorium.ErrInvalidRequest), status: http.StatusBadRequest, code: "profile_required", message: "profile_id is required when prompt default_profile is not set"},
{name: "profile not found", err: scriptorium.ErrProfileNotFound, status: http.StatusNotFound, code: "profile_not_found", message: "execution profile not found"},
{name: "profile load", err: wrap(scriptorium.ErrProfileLoad, fmt.Errorf("read failed")), status: http.StatusBadRequest, code: "profile_load_failed", message: "failed to load execution profile", avoidCause: "read failed"},
{name: "api key env missing", err: wrap(scriptorium.ErrAPIKeyEnvMissing, scriptorium.ErrInvalidRequest), status: http.StatusBadRequest, code: "api_key_env_missing", message: "api_key_env is set but the environment variable is missing"},
{name: "invalid request", err: scriptorium.ErrInvalidRequest, status: http.StatusBadRequest, code: "invalid_request", message: "invalid run request"},
{name: "prompt not found", err: promptkit.ErrPromptNotFound, status: http.StatusNotFound, code: "prompt_not_found", message: "prompt definition not found"},
{name: "prompt load", err: wrap(promptkit.ErrPromptLoad, fmt.Errorf("read failed")), status: http.StatusBadRequest, code: "prompt_load_failed", message: "failed to load prompt definition", avoidCause: "read failed"},
{name: "missing profile/default", err: wrap(promptkit.ErrProfileRequired, promptkit.ErrInvalidRequest), status: http.StatusBadRequest, code: "profile_required", message: "profile_id is required when prompt default_profile is not set"},
{name: "profile not found", err: promptkit.ErrProfileNotFound, status: http.StatusNotFound, code: "profile_not_found", message: "execution profile not found"},
{name: "profile load", err: wrap(promptkit.ErrProfileLoad, fmt.Errorf("read failed")), status: http.StatusBadRequest, code: "profile_load_failed", message: "failed to load execution profile", avoidCause: "read failed"},
{name: "api key env missing", err: wrap(promptkit.ErrAPIKeyEnvMissing, promptkit.ErrInvalidRequest), status: http.StatusBadRequest, code: "api_key_env_missing", message: "api_key_env is set but the environment variable is missing"},
{name: "invalid request", err: promptkit.ErrInvalidRequest, status: http.StatusBadRequest, code: "invalid_request", message: "invalid run request"},
{name: "file denied", err: ErrFileNotAllowed, status: http.StatusBadRequest, code: "artifact_not_allowed", message: "file input artifact is not allowed"},
{name: "file outside root", err: ErrFileOutsideRoot, status: http.StatusBadRequest, code: "artifact_not_allowed", message: "file input artifact is not allowed"},
{name: "file too large", err: ErrFileTooLarge, status: http.StatusRequestEntityTooLarge, code: "artifact_too_large", message: "file input artifact is too large"},
{name: "artifact", err: wrap(scriptorium.ErrArtifactLoad, fmt.Errorf("read failed")), status: http.StatusBadRequest, code: "artifact_read_failed", message: "failed to read input artifact", avoidCause: "read failed"},
{name: "prompt render", err: wrap(scriptorium.ErrPromptRender, fmt.Errorf("render failed")), status: http.StatusBadRequest, code: "prompt_render_failed", message: "failed to render prompt", avoidCause: "render failed"},
{name: "llm", err: wrap(scriptorium.ErrLLMGenerate, fmt.Errorf("llm failed")), status: http.StatusBadGateway, code: "llm_failed", message: "model generation request failed", avoidCause: "llm failed"},
{name: "validation runtime", err: wrap(scriptorium.ErrValidation, fmt.Errorf("validator broke")), status: http.StatusInternalServerError, code: "validation_runtime_failed", message: "validation runtime failed", avoidCause: "validator broke"},
{name: "artifact", err: wrap(promptkit.ErrArtifactLoad, fmt.Errorf("read failed")), status: http.StatusBadRequest, code: "artifact_read_failed", message: "failed to read input artifact", avoidCause: "read failed"},
{name: "prompt render", err: wrap(promptkit.ErrPromptRender, fmt.Errorf("render failed")), status: http.StatusBadRequest, code: "prompt_render_failed", message: "failed to render prompt", avoidCause: "render failed"},
{name: "llm", err: wrap(promptkit.ErrLLMGenerate, fmt.Errorf("llm failed")), status: http.StatusBadGateway, code: "llm_failed", message: "model generation request failed", avoidCause: "llm failed"},
{name: "validation runtime", err: wrap(promptkit.ErrValidation, fmt.Errorf("validator broke")), status: http.StatusInternalServerError, code: "validation_runtime_failed", message: "validation runtime failed", avoidCause: "validator broke"},
}
for _, tc := range tests {
@@ -776,12 +776,12 @@ func TestHandlerRawAPIKeyRejectedByStrictJSON(t *testing.T) {
}
func TestHandlerValidationFailureStillSuccessAndRawOutputOptIn(t *testing.T) {
h := NewHandler(&fakeRunner{result: &scriptorium.RunResult{
Artifact: scriptorium.Artifact{Body: []byte("bad json")},
h := NewHandler(&fakeRunner{result: &promptkit.RunResult{
Artifact: promptkit.Artifact{Body: []byte("bad json")},
RawOutput: "bad json",
Validation: scriptorium.ValidationResult{
Status: scriptorium.ValidationFailed,
Mode: scriptorium.ValidationJSON,
Validation: promptkit.ValidationResult{
Status: promptkit.ValidationFailed,
Mode: promptkit.ValidationJSON,
Errors: []string{"invalid JSON"},
},
}})
@@ -839,22 +839,22 @@ func newArtifactRootHandlerWithLimit(t *testing.T, root string, maxArtifactBytes
if err != nil {
t.Fatalf("expected restricted artifact reader: %v", err)
}
return NewHandler(newHandlerEngine(t, scriptorium.WithArtifactReader(reader)))
return NewHandler(newHandlerEngine(t, promptkit.WithArtifactReader(reader)))
}
func newHandlerEngine(t *testing.T, options ...scriptorium.Option) *scriptorium.Engine {
func newHandlerEngine(t *testing.T, options ...promptkit.Option) *promptkit.Engine {
t.Helper()
return newHandlerEngineWithOptions(t, append(options, scriptorium.WithLLMClient(handlerLLMClient{}))...)
return newHandlerEngineWithOptions(t, append(options, promptkit.WithLLMClient(handlerLLMClient{}))...)
}
func newHandlerEngineWithDefaultClient(t *testing.T, options ...scriptorium.Option) *scriptorium.Engine {
func newHandlerEngineWithDefaultClient(t *testing.T, options ...promptkit.Option) *promptkit.Engine {
t.Helper()
return newHandlerEngineWithOptions(t, options...)
}
func newHandlerEngineWithOptions(t *testing.T, options ...scriptorium.Option) *scriptorium.Engine {
func newHandlerEngineWithOptions(t *testing.T, options ...promptkit.Option) *promptkit.Engine {
t.Helper()
promptDir := t.TempDir()
@@ -879,7 +879,7 @@ model: model
t.Fatalf("write profile fixture: %v", err)
}
engine, err := scriptorium.NewEngine(scriptorium.Config{
engine, err := promptkit.NewEngine(promptkit.Config{
PromptDir: promptDir,
ProfileDir: profileDir,
}, options...)

View File

@@ -9,7 +9,7 @@ import (
"sort"
"strings"
"gitea.maximumdirect.net/eric/scriptorium"
"gitea.maximumdirect.net/eric/promptkit"
)
var ErrUnknownPreparedRunFormat = errors.New("unknown prepared run format")
@@ -26,7 +26,7 @@ const (
// PreparedRunFormatter serializes a prepared run without performing use case work.
type PreparedRunFormatter interface {
Format(prepared *scriptorium.PreparedRun) ([]byte, error)
Format(prepared *promptkit.PreparedRun) ([]byte, error)
}
// ParsePreparedRunOutputFormat parses a format name.
@@ -56,7 +56,7 @@ func FormatterForPreparedRun(outputFormat PreparedRunOutputFormat) (PreparedRunF
}
// FormatPreparedRun formats a prepared run using the selected format.
func FormatPreparedRun(prepared *scriptorium.PreparedRun, outputFormat PreparedRunOutputFormat) ([]byte, error) {
func FormatPreparedRun(prepared *promptkit.PreparedRun, outputFormat PreparedRunOutputFormat) ([]byte, error) {
formatter, err := FormatterForPreparedRun(outputFormat)
if err != nil {
return nil, err
@@ -65,7 +65,7 @@ func FormatPreparedRun(prepared *scriptorium.PreparedRun, outputFormat PreparedR
}
// FormatPreparedRunByName parses a format name and formats a prepared run.
func FormatPreparedRunByName(prepared *scriptorium.PreparedRun, rawFormat string) ([]byte, error) {
func FormatPreparedRunByName(prepared *promptkit.PreparedRun, rawFormat string) ([]byte, error) {
outputFormat, err := ParsePreparedRunOutputFormat(rawFormat)
if err != nil {
return nil, err
@@ -75,7 +75,7 @@ func FormatPreparedRunByName(prepared *scriptorium.PreparedRun, rawFormat string
type jsonPreparedRunFormatter struct{}
func (jsonPreparedRunFormatter) Format(prepared *scriptorium.PreparedRun) ([]byte, error) {
func (jsonPreparedRunFormatter) Format(prepared *promptkit.PreparedRun) ([]byte, error) {
if prepared == nil {
return nil, errors.New("prepared run is nil")
}
@@ -84,7 +84,7 @@ func (jsonPreparedRunFormatter) Format(prepared *scriptorium.PreparedRun) ([]byt
type textPreparedRunFormatter struct{}
func (textPreparedRunFormatter) Format(prepared *scriptorium.PreparedRun) ([]byte, error) {
func (textPreparedRunFormatter) Format(prepared *promptkit.PreparedRun) ([]byte, error) {
if prepared == nil {
return nil, errors.New("prepared run is nil")
}
@@ -146,7 +146,7 @@ func (textPreparedRunFormatter) Format(prepared *scriptorium.PreparedRun) ([]byt
fmt.Fprintln(&b, "messages:")
roleOrder := make([]string, 0)
byRole := make(map[string][]scriptorium.RenderedMessage)
byRole := make(map[string][]promptkit.RenderedMessage)
for _, msg := range prepared.Messages {
if _, exists := byRole[msg.Role]; !exists {
roleOrder = append(roleOrder, msg.Role)

View File

@@ -6,7 +6,7 @@ import (
"strings"
"testing"
"gitea.maximumdirect.net/eric/scriptorium"
"gitea.maximumdirect.net/eric/promptkit"
)
func TestTextFormatterIncludesPreparedRunDetails(t *testing.T) {
@@ -107,12 +107,12 @@ func TestTextFormatterDoesNotIncludeDirectAPIKeyValue(t *testing.T) {
func TestTextFormatterIncludesMessageCacheControlBeforeContent(t *testing.T) {
prepared := samplePreparedRun()
prepared.Messages = []scriptorium.RenderedMessage{
prepared.Messages = []promptkit.RenderedMessage{
{
Role: "system",
Content: "System guidance.",
CacheControl: &scriptorium.CacheControl{
Type: scriptorium.CacheControlEphemeral,
CacheControl: &promptkit.CacheControl{
Type: promptkit.CacheControlEphemeral,
TTL: "1h",
},
},
@@ -147,12 +147,12 @@ func TestTextFormatterIncludesSessionIDWhenPresent(t *testing.T) {
func TestTextFormatterOmitsEmptyCacheControlTTL(t *testing.T) {
prepared := samplePreparedRun()
prepared.Messages = []scriptorium.RenderedMessage{
prepared.Messages = []promptkit.RenderedMessage{
{
Role: "system",
Content: "System guidance.",
CacheControl: &scriptorium.CacheControl{
Type: scriptorium.CacheControlEphemeral,
CacheControl: &promptkit.CacheControl{
Type: promptkit.CacheControlEphemeral,
},
},
}
@@ -230,12 +230,12 @@ func TestJSONFormatterEmitsValidJSONAndIncludesPreparedRunFields(t *testing.T) {
func TestJSONFormatterIncludesMessageCacheControlOnlyWhenPresent(t *testing.T) {
prepared := samplePreparedRun()
prepared.Messages = []scriptorium.RenderedMessage{
prepared.Messages = []promptkit.RenderedMessage{
{
Role: "system",
Content: "System guidance.",
CacheControl: &scriptorium.CacheControl{
Type: scriptorium.CacheControlEphemeral,
CacheControl: &promptkit.CacheControl{
Type: promptkit.CacheControlEphemeral,
TTL: "1h",
},
},
@@ -261,7 +261,7 @@ func TestJSONFormatterIncludesMessageCacheControlOnlyWhenPresent(t *testing.T) {
if !ok {
t.Fatalf("expected first message cache_control, got %#v", decoded.Messages[0])
}
if cacheControl["type"] != string(scriptorium.CacheControlEphemeral) || cacheControl["ttl"] != "1h" {
if cacheControl["type"] != string(promptkit.CacheControlEphemeral) || cacheControl["ttl"] != "1h" {
t.Fatalf("unexpected cache_control payload: %#v", cacheControl)
}
if _, ok := decoded.Messages[1]["cache_control"]; ok {
@@ -340,13 +340,13 @@ func TestFormatPreparedRunByNameUnknownFailsClearly(t *testing.T) {
}
}
func samplePreparedRun() *scriptorium.PreparedRun {
return &scriptorium.PreparedRun{
func samplePreparedRun() *promptkit.PreparedRun {
return &promptkit.PreparedRun{
PromptID: "prompt.id",
PromptVersion: "v1",
PromptHash: "prompt-hash",
SelectedProfileID: "local-fast",
EffectiveModelParams: scriptorium.ExecutionTarget{
EffectiveModelParams: promptkit.ExecutionTarget{
Endpoint: "http://llm/v1",
Model: "gpt-test",
Temperature: 0.4,
@@ -362,7 +362,7 @@ func samplePreparedRun() *scriptorium.PreparedRun {
"glossary": "hash-glossary",
},
RenderedPromptHash: "rendered-hash",
Messages: []scriptorium.RenderedMessage{
Messages: []promptkit.RenderedMessage{
{Role: "system", Content: "System guidance."},
{Role: "user", Content: "Summarize the transcript.\nInclude key entities."},
{Role: "user", Content: "Second user message."},