Relaxed CLI requirements when defaults are specified in the profile or application defaults
This commit is contained in:
@@ -41,6 +41,11 @@ type runConfig struct {
|
||||
maxTokens int
|
||||
schemaDir string
|
||||
timeout time.Duration
|
||||
|
||||
llmBaseURLSet bool
|
||||
modelSet bool
|
||||
temperatureSet bool
|
||||
maxTokensSet bool
|
||||
}
|
||||
|
||||
type serveConfig struct {
|
||||
@@ -127,16 +132,21 @@ func runCommand(args []string, stdout, stderr io.Writer) int {
|
||||
validate.NewStandardValidator(cfg.schemaDir),
|
||||
)
|
||||
|
||||
res, runErr := runner.Run(context.Background(), domain.RunRequest{
|
||||
ProfileID: cfg.profileID,
|
||||
Inputs: inputs,
|
||||
Vars: varMappings,
|
||||
Model: &domain.ModelTarget{
|
||||
var modelOverride *domain.ModelTarget
|
||||
if cfg.llmBaseURLSet || cfg.modelSet || cfg.temperatureSet || cfg.maxTokensSet {
|
||||
modelOverride = &domain.ModelTarget{
|
||||
Endpoint: cfg.llmBaseURL,
|
||||
Model: cfg.model,
|
||||
Temperature: cfg.temperature,
|
||||
MaxTokens: cfg.maxTokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
res, runErr := runner.Run(context.Background(), domain.RunRequest{
|
||||
ProfileID: cfg.profileID,
|
||||
Inputs: inputs,
|
||||
Vars: varMappings,
|
||||
Model: modelOverride,
|
||||
})
|
||||
if runErr != nil {
|
||||
fmt.Fprintf(stderr, "run error: %v\n", runErr)
|
||||
@@ -227,18 +237,15 @@ func parseRunArgs(args []string) (*runConfig, error) {
|
||||
if len(cfg.inputRaw) == 0 {
|
||||
return nil, errors.New("at least one --input is required")
|
||||
}
|
||||
if strings.TrimSpace(cfg.llmBaseURL) == "" {
|
||||
return nil, errors.New("--llm-base-url is required")
|
||||
}
|
||||
if strings.TrimSpace(cfg.model) == "" {
|
||||
return nil, errors.New("--model is required")
|
||||
}
|
||||
|
||||
cfg.profileDir = filepath.Clean(cfg.profileDir)
|
||||
cfg.schemaDir = filepath.Clean(cfg.schemaDir)
|
||||
if cfg.outputPath != "" {
|
||||
cfg.outputPath = filepath.Clean(cfg.outputPath)
|
||||
}
|
||||
cfg.llmBaseURLSet = flagWasSet(fs, "llm-base-url")
|
||||
cfg.modelSet = flagWasSet(fs, "model")
|
||||
cfg.temperatureSet = flagWasSet(fs, "temperature")
|
||||
cfg.maxTokensSet = flagWasSet(fs, "max-tokens")
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
@@ -312,6 +319,16 @@ func parseMapping(value string) (string, string, error) {
|
||||
return key, val, nil
|
||||
}
|
||||
|
||||
func flagWasSet(fs *flag.FlagSet, name string) bool {
|
||||
set := false
|
||||
fs.Visit(func(f *flag.Flag) {
|
||||
if f.Name == name {
|
||||
set = true
|
||||
}
|
||||
})
|
||||
return set
|
||||
}
|
||||
|
||||
func writeOutput(stdout io.Writer, outputPath string, body []byte) error {
|
||||
if outputPath == "" {
|
||||
_, err := stdout.Write(body)
|
||||
@@ -351,6 +368,6 @@ 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 --profile-dir DIR --profile-id ID --input name=path [--input ...] --llm-base-url URL --model NAME [--var k=v] [--out path] [--timeout 10m]")
|
||||
fmt.Fprintln(w, " run: scriptorium run --profile-dir DIR --profile-id ID --input name=path [--input ...] [--llm-base-url URL] [--model NAME] [--var k=v] [--out path] [--timeout 10m]")
|
||||
fmt.Fprintln(w, " serve: scriptorium serve --addr :8080 --profile-dir DIR --llm-base-url URL [--schema-dir DIR] [--llm-api-key KEY] [--model NAME] [--timeout 10m]")
|
||||
}
|
||||
|
||||
@@ -65,15 +65,19 @@ func TestParseRunArgsRequiredFlags(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatal("expected missing --input error")
|
||||
}
|
||||
}
|
||||
|
||||
_, err = parseRunArgs([]string{"--profile-dir", "./profiles", "--profile-id", "p", "--input", "a=b", "--model", "m"})
|
||||
if err == nil {
|
||||
t.Fatal("expected missing --llm-base-url error")
|
||||
func TestParseRunArgsAllowsOmittedModelAndBaseURL(t *testing.T) {
|
||||
cfg, err := parseRunArgs([]string{
|
||||
"--profile-dir", "./profiles",
|
||||
"--profile-id", "p",
|
||||
"--input", "a=b",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("expected valid args without model/base url, got %v", err)
|
||||
}
|
||||
|
||||
_, err = parseRunArgs([]string{"--profile-dir", "./profiles", "--profile-id", "p", "--input", "a=b", "--llm-base-url", "http://x/v1"})
|
||||
if err == nil {
|
||||
t.Fatal("expected missing --model error")
|
||||
if cfg.llmBaseURL != "" || cfg.model != "" {
|
||||
t.Fatalf("expected empty model/baseurl, got model=%q base=%q", cfg.model, cfg.llmBaseURL)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,4 +171,31 @@ func TestRunCommandVarsOptional(t *testing.T) {
|
||||
if !strings.Contains(stderr.String(), "llm client error") {
|
||||
t.Fatalf("expected llm client error after parsing succeeds, got stderr=%q", stderr.String())
|
||||
}
|
||||
if stdout.Len() != 0 {
|
||||
t.Fatalf("expected no stdout output on error, got %q", stdout.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteOutputAndSummaryUseSeparateWriters(t *testing.T) {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
if err := writeOutput(&stdout, "", []byte("artifact-body")); err != nil {
|
||||
t.Fatalf("unexpected writeOutput error: %v", err)
|
||||
}
|
||||
printSummary(&stderr, &domain.RunResult{
|
||||
ProfileID: "p",
|
||||
ProfileVersion: "1",
|
||||
ModelName: "m",
|
||||
Validation: domain.ValidationResult{Status: domain.ValidationPassed, Mode: domain.ValidationBasic},
|
||||
PromptHash: "h",
|
||||
InputHashes: map[string]string{"in": "x"},
|
||||
})
|
||||
|
||||
if stdout.String() != "artifact-body" {
|
||||
t.Fatalf("expected artifact output on stdout, got %q", stdout.String())
|
||||
}
|
||||
if !strings.Contains(stderr.String(), "profile=p@1") {
|
||||
t.Fatalf("expected summary on stderr, got %q", stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user