Centralize remaining runtime constants and add precedence/default fallback coverage

This commit is contained in:
2026-05-05 11:01:44 -05:00
parent d82bcd0581
commit ddb4254124
5 changed files with 70 additions and 6 deletions

View File

@@ -206,7 +206,7 @@ func serveCommand(args []string, stderr io.Writer) int {
srv := &http.Server{
Addr: cfg.addr,
Handler: h,
ReadHeaderTimeout: 10 * time.Second,
ReadHeaderTimeout: defaults.HTTPReadHeaderTimeoutDefault,
}
fmt.Fprintf(stderr, "serving on %s\n", cfg.addr)
@@ -397,5 +397,5 @@ func printSummary(stderr io.Writer, res *domain.RunResult) {
func printUsage(w io.Writer) {
fmt.Fprintln(w, "usage: scriptorium <run|serve> ...")
fmt.Fprintln(w, " run: scriptorium run --prompt-dir DIR --profile-dir DIR --prompt ID --input name=path [--input ...] [--profile ID] [--llm-base-url URL] [--model NAME] [--api-key-env ENV] [--temperature N] [--max-tokens N] [--top-p N] [--var k=v] [--out path] [--timeout 10m]")
fmt.Fprintln(w, " serve: scriptorium serve --addr :8080 --prompt-dir DIR --profile-dir DIR [--llm-base-url URL] [--schema-dir DIR] [--model NAME] [--timeout 10m]")
fmt.Fprintf(w, " serve: scriptorium serve --addr %s --prompt-dir DIR --profile-dir DIR [--llm-base-url URL] [--schema-dir DIR] [--model NAME] [--timeout 10m]\n", defaults.HTTPAddrDefault)
}

View File

@@ -5,6 +5,7 @@ import (
"crypto/sha256"
"errors"
"fmt"
"gitea.maximumdirect.net/eric/scriptorium/internal/defaults"
"gitea.maximumdirect.net/eric/scriptorium/internal/domain"
"mime"
"os"
@@ -67,7 +68,7 @@ func (r *inlineReader) Read(ctx context.Context, ref domain.ArtifactRef) (*domai
body := []byte(ref.Body)
return &domain.Artifact{
ContentType: "text/plain",
ContentType: defaults.ContentTypeTextPlain,
Body: body,
Size: int64(len(body)),
Hash: fmt.Sprintf("%x", sha256.Sum256(body)),
@@ -95,7 +96,7 @@ func (r *fileReader) Read(ctx context.Context, ref domain.ArtifactRef) (*domain.
contentType := mime.TypeByExtension(filepath.Ext(ref.URI))
if contentType == "" {
contentType = "text/plain" // Default
contentType = defaults.ContentTypeTextPlain
}
return &domain.Artifact{

View File

@@ -13,6 +13,7 @@ const (
ContentTypeTextPlain = "text/plain"
ContentTypeTextMarkdown = "text/markdown"
ContentTypeApplicationJSON = "application/json"
OpenAIChatCompletionsPath = "/chat/completions"
ExecutionDefaultTemperature = 0.0
ExecutionDefaultMaxTokens = 0
@@ -21,7 +22,8 @@ const (
)
var (
LLMRequestTimeoutDefault = 10 * time.Minute
LLMRequestTimeoutDefault = 10 * time.Minute
HTTPReadHeaderTimeoutDefault = 10 * time.Second
)
func ExecutionTargetDefault() domain.ExecutionTarget {

View File

@@ -90,7 +90,7 @@ func (c *OpenAICompatibleClient) Generate(ctx context.Context, req domain.Genera
if endpoint == "" {
return nil, fmt.Errorf("%w: endpoint is required", ErrInvalidRequest)
}
endpoint = strings.TrimRight(endpoint, "/") + "/chat/completions"
endpoint = strings.TrimRight(endpoint, "/") + defaults.OpenAIChatCompletionsPath
wireReq := openAIChatRequest{
Model: model,

View File

@@ -10,6 +10,7 @@ import (
"strings"
"testing"
"gitea.maximumdirect.net/eric/scriptorium/internal/defaults"
"gitea.maximumdirect.net/eric/scriptorium/internal/domain"
"gitea.maximumdirect.net/eric/scriptorium/internal/validate"
)
@@ -164,6 +165,12 @@ func TestRunnerRunSuccessful(t *testing.T) {
if res.EffectiveModelParams.Model != "m" || res.Endpoint != "http://override/v1" {
t.Fatalf("unexpected model params: %+v", res.EffectiveModelParams)
}
if res.Artifact.Name != defaults.OutputArtifactName {
t.Fatalf("expected default output artifact name %q, got %q", defaults.OutputArtifactName, res.Artifact.Name)
}
if res.Artifact.ContentType != defaults.ContentTypeTextMarkdown {
t.Fatalf("expected markdown content type %q, got %q", defaults.ContentTypeTextMarkdown, res.Artifact.ContentType)
}
if res.RawOutput != "# recap" {
t.Fatalf("expected raw output, got %q", res.RawOutput)
}
@@ -323,6 +330,36 @@ func TestRunnerRunSelectedProfileBeatsBuiltInDefault(t *testing.T) {
}
}
func TestRunnerRunBuiltInDefaultsUsedWhenProfileOmitsOptionalFields(t *testing.T) {
promptRepo := &fakePromptRepo{def: promptDef(domain.FormatText, domain.ValidationNone, 0)}
execRepo := &fakeExecutionProfileRepo{profiles: map[string]*domain.ExecutionProfile{
"exec": {ID: "exec", Endpoint: "http://profile/v1", Model: "profile-model"},
}}
llmClient := &fakeLLM{resp: &domain.GenerateResponse{Content: "ok"}}
runner := NewRunner(promptRepo, execRepo, defaultArtifactReader(), defaultRenderer(), llmClient, nil)
res, err := runner.Run(context.Background(), domain.RunRequest{
PromptID: "p",
ProfileID: "exec",
Inputs: singleInputRef(),
})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if res.EffectiveModelParams.Temperature != defaults.ExecutionDefaultTemperature {
t.Fatalf("expected default temperature %v, got %v", defaults.ExecutionDefaultTemperature, res.EffectiveModelParams.Temperature)
}
if res.EffectiveModelParams.TopP != defaults.ExecutionDefaultTopP {
t.Fatalf("expected default top_p %v, got %v", defaults.ExecutionDefaultTopP, res.EffectiveModelParams.TopP)
}
if res.EffectiveModelParams.MaxTokens != defaults.ExecutionDefaultMaxTokens {
t.Fatalf("expected default max_tokens %d, got %d", defaults.ExecutionDefaultMaxTokens, res.EffectiveModelParams.MaxTokens)
}
if res.EffectiveModelParams.TimeoutSeconds != defaults.ExecutionDefaultTimeoutSeconds {
t.Fatalf("expected default timeout_seconds %d, got %d", defaults.ExecutionDefaultTimeoutSeconds, res.EffectiveModelParams.TimeoutSeconds)
}
}
func TestRunnerRunAPIKeyEnvResolvesFromEnvironment(t *testing.T) {
t.Setenv("SCRIPTORIUM_TEST_API_KEY", "secret")
promptRepo := &fakePromptRepo{def: promptDef(domain.FormatText, domain.ValidationNone, 0)}
@@ -506,6 +543,30 @@ func TestRunnerRunStructuredRepairRemainsBoundedAndUsesEffectiveModelSettings(t
}
}
func TestBuildOutputArtifactDefaults(t *testing.T) {
tests := []struct {
name string
format domain.OutputFormat
contentType string
}{
{name: "text", format: domain.FormatText, contentType: defaults.ContentTypeTextPlain},
{name: "markdown", format: domain.FormatMarkdown, contentType: defaults.ContentTypeTextMarkdown},
{name: "json", format: domain.FormatJSON, contentType: defaults.ContentTypeApplicationJSON},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
art := buildOutputArtifact("body", tc.format)
if art.Name != defaults.OutputArtifactName {
t.Fatalf("expected artifact name %q, got %q", defaults.OutputArtifactName, art.Name)
}
if art.ContentType != tc.contentType {
t.Fatalf("expected content type %q, got %q", tc.contentType, art.ContentType)
}
})
}
}
func promptDef(format domain.OutputFormat, mode domain.ValidationMode, attempts int) *domain.PromptDefinition {
return &domain.PromptDefinition{
ID: "p",