From e13610481d669f36b3688c209869cf102270ac3f Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 28 Jul 2026 14:04:29 +0000 Subject: [PATCH] Adopt Promptkit at application boundaries --- go.mod | 1 + go.sum | 2 + internal/adapter/cli/run.go | 30 ++-- internal/adapter/cli/run_test.go | 18 +-- internal/adapter/http/artifact_reader.go | 22 +-- internal/adapter/http/artifact_reader_test.go | 42 +++--- internal/adapter/http/handler.go | 44 +++--- internal/adapter/http/handler_test.go | 132 +++++++++--------- internal/format/prepared_run.go | 14 +- internal/format/prepared_run_test.go | 30 ++-- 10 files changed, 169 insertions(+), 166 deletions(-) diff --git a/go.mod b/go.mod index 8c941f4..ce5844d 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 7e38d45..3c26d56 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/internal/adapter/cli/run.go b/internal/adapter/cli/run.go index 4efe540..f6b5242 100644 --- a/internal/adapter/cli/run.go +++ b/internal/adapter/cli/run.go @@ -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 } diff --git a/internal/adapter/cli/run_test.go b/internal/adapter/cli/run_test.go index a8f3430..458af60 100644 --- a/internal/adapter/cli/run_test.go +++ b/internal/adapter/cli/run_test.go @@ -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, diff --git a/internal/adapter/http/artifact_reader.go b/internal/adapter/http/artifact_reader.go index 7490b8b..1c9638c 100644 --- a/internal/adapter/http/artifact_reader.go +++ b/internal/adapter/http/artifact_reader.go @@ -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, diff --git a/internal/adapter/http/artifact_reader_test.go b/internal/adapter/http/artifact_reader_test.go index 26ec808..a42aae1 100644 --- a/internal/adapter/http/artifact_reader_test.go +++ b/internal/adapter/http/artifact_reader_test.go @@ -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) diff --git a/internal/adapter/http/handler.go b/internal/adapter/http/handler.go index 4e97ee3..22bd1fd 100644 --- a/internal/adapter/http/handler.go +++ b/internal/adapter/http/handler.go @@ -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" diff --git a/internal/adapter/http/handler_test.go b/internal/adapter/http/handler_test.go index c07838a..b80ba59 100644 --- a/internal/adapter/http/handler_test.go +++ b/internal/adapter/http/handler_test.go @@ -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...) diff --git a/internal/format/prepared_run.go b/internal/format/prepared_run.go index fc1cb79..5aec67b 100644 --- a/internal/format/prepared_run.go +++ b/internal/format/prepared_run.go @@ -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) diff --git a/internal/format/prepared_run_test.go b/internal/format/prepared_run_test.go index 442a5c1..25390f1 100644 --- a/internal/format/prepared_run_test.go +++ b/internal/format/prepared_run_test.go @@ -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."},