Wire config loading into CLI setup
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
httpadapter "gitea.maximumdirect.net/eric/scriptorium/internal/adapter/http"
|
httpadapter "gitea.maximumdirect.net/eric/scriptorium/internal/adapter/http"
|
||||||
artifactadapter "gitea.maximumdirect.net/eric/scriptorium/internal/artifact"
|
artifactadapter "gitea.maximumdirect.net/eric/scriptorium/internal/artifact"
|
||||||
|
appconfig "gitea.maximumdirect.net/eric/scriptorium/internal/config"
|
||||||
"gitea.maximumdirect.net/eric/scriptorium/internal/defaults"
|
"gitea.maximumdirect.net/eric/scriptorium/internal/defaults"
|
||||||
"gitea.maximumdirect.net/eric/scriptorium/internal/domain"
|
"gitea.maximumdirect.net/eric/scriptorium/internal/domain"
|
||||||
renderformat "gitea.maximumdirect.net/eric/scriptorium/internal/format"
|
renderformat "gitea.maximumdirect.net/eric/scriptorium/internal/format"
|
||||||
@@ -32,6 +33,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type runConfig struct {
|
type runConfig struct {
|
||||||
|
configPath string
|
||||||
|
|
||||||
promptDir string
|
promptDir string
|
||||||
profileDir string
|
profileDir string
|
||||||
promptID string
|
promptID string
|
||||||
@@ -48,6 +51,8 @@ type runConfig struct {
|
|||||||
schemaDir string
|
schemaDir string
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
|
|
||||||
|
defaultRenderFormat renderformat.PreparedRunOutputFormat
|
||||||
|
|
||||||
llmBaseURLSet bool
|
llmBaseURLSet bool
|
||||||
apiKeyEnvSet bool
|
apiKeyEnvSet bool
|
||||||
modelSet bool
|
modelSet bool
|
||||||
@@ -63,6 +68,8 @@ type renderConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type serveConfig struct {
|
type serveConfig struct {
|
||||||
|
configPath string
|
||||||
|
|
||||||
addr string
|
addr string
|
||||||
promptDir string
|
promptDir string
|
||||||
profileDir string
|
profileDir string
|
||||||
@@ -230,8 +237,9 @@ func parseRunArgs(args []string) (*runConfig, error) {
|
|||||||
fs := flag.NewFlagSet("run", flag.ContinueOnError)
|
fs := flag.NewFlagSet("run", flag.ContinueOnError)
|
||||||
fs.SetOutput(io.Discard)
|
fs.SetOutput(io.Discard)
|
||||||
|
|
||||||
|
registerConfigPathFlag(fs, &cfg.configPath)
|
||||||
registerExecutionRequestFlags(fs, cfg)
|
registerExecutionRequestFlags(fs, cfg)
|
||||||
fs.StringVar(&cfg.schemaDir, "schema-dir", defaults.SchemaDirDefault, "base directory for validation schemas")
|
fs.StringVar(&cfg.schemaDir, "schema-dir", "", "base directory for validation schemas")
|
||||||
|
|
||||||
if err := fs.Parse(args); err != nil {
|
if err := fs.Parse(args); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -251,10 +259,11 @@ func parseRenderArgs(args []string) (*renderConfig, error) {
|
|||||||
fs := flag.NewFlagSet("render", flag.ContinueOnError)
|
fs := flag.NewFlagSet("render", flag.ContinueOnError)
|
||||||
fs.SetOutput(io.Discard)
|
fs.SetOutput(io.Discard)
|
||||||
|
|
||||||
|
registerConfigPathFlag(fs, &cfg.configPath)
|
||||||
registerExecutionRequestFlags(fs, &cfg.runConfig)
|
registerExecutionRequestFlags(fs, &cfg.runConfig)
|
||||||
|
|
||||||
var rawFormat string
|
var rawFormat string
|
||||||
fs.StringVar(&rawFormat, "format", string(renderformat.DefaultPreparedRunOutputFormat), "render output format (text|json)")
|
fs.StringVar(&rawFormat, "format", "", "render output format (text|json)")
|
||||||
|
|
||||||
if err := fs.Parse(args); err != nil {
|
if err := fs.Parse(args); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -263,11 +272,15 @@ func parseRenderArgs(args []string) (*renderConfig, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := renderformat.ParsePreparedRunOutputFormat(rawFormat)
|
if flagWasSet(fs, "format") {
|
||||||
if err != nil {
|
format, err := renderformat.ParsePreparedRunOutputFormat(rawFormat)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg.outputFormat = format
|
||||||
|
} else {
|
||||||
|
cfg.outputFormat = cfg.defaultRenderFormat
|
||||||
}
|
}
|
||||||
cfg.outputFormat = format
|
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,10 +289,11 @@ func parseServeArgs(args []string) (*serveConfig, error) {
|
|||||||
fs := flag.NewFlagSet("serve", flag.ContinueOnError)
|
fs := flag.NewFlagSet("serve", flag.ContinueOnError)
|
||||||
fs.SetOutput(io.Discard)
|
fs.SetOutput(io.Discard)
|
||||||
|
|
||||||
fs.StringVar(&cfg.addr, "addr", defaults.HTTPAddrDefault, "HTTP listen address")
|
registerConfigPathFlag(fs, &cfg.configPath)
|
||||||
|
fs.StringVar(&cfg.addr, "addr", "", "HTTP listen address")
|
||||||
fs.StringVar(&cfg.promptDir, "prompt-dir", "", "directory containing prompt definition YAML files")
|
fs.StringVar(&cfg.promptDir, "prompt-dir", "", "directory containing prompt definition YAML files")
|
||||||
fs.StringVar(&cfg.profileDir, "profile-dir", "", "directory containing execution profile YAML files")
|
fs.StringVar(&cfg.profileDir, "profile-dir", "", "directory containing execution profile YAML files")
|
||||||
fs.StringVar(&cfg.schemaDir, "schema-dir", defaults.SchemaDirDefault, "base directory for validation schemas")
|
fs.StringVar(&cfg.schemaDir, "schema-dir", "", "base directory for validation schemas")
|
||||||
|
|
||||||
if err := fs.Parse(args); err != nil {
|
if err := fs.Parse(args); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -288,6 +302,21 @@ func parseServeArgs(args []string) (*serveConfig, error) {
|
|||||||
return nil, fmt.Errorf("unexpected positional args: %v", fs.Args())
|
return nil, fmt.Errorf("unexpected positional args: %v", fs.Args())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settings, err := resolveAppSettings(fs, cfg.configPath, appconfig.CLIOverrides{
|
||||||
|
PromptDir: cfg.promptDirIfSet(fs),
|
||||||
|
ProfileDir: cfg.profileDirIfSet(fs),
|
||||||
|
SchemaDir: cfg.schemaDirIfSet(fs),
|
||||||
|
ServerAddr: cfg.addrIfSet(fs),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.promptDir = settings.PromptDir
|
||||||
|
cfg.profileDir = settings.ProfileDir
|
||||||
|
cfg.schemaDir = settings.SchemaDir
|
||||||
|
cfg.addr = settings.ServerAddr
|
||||||
|
|
||||||
if strings.TrimSpace(cfg.promptDir) == "" {
|
if strings.TrimSpace(cfg.promptDir) == "" {
|
||||||
return nil, errors.New("--prompt-dir is required")
|
return nil, errors.New("--prompt-dir is required")
|
||||||
}
|
}
|
||||||
@@ -325,6 +354,20 @@ func finalizeExecutionRequestConfig(fs *flag.FlagSet, cfg *runConfig) error {
|
|||||||
return fmt.Errorf("unexpected positional args: %v", fs.Args())
|
return fmt.Errorf("unexpected positional args: %v", fs.Args())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settings, err := resolveAppSettings(fs, cfg.configPath, appconfig.CLIOverrides{
|
||||||
|
PromptDir: cfg.promptDirIfSet(fs),
|
||||||
|
ProfileDir: cfg.profileDirIfSet(fs),
|
||||||
|
SchemaDir: cfg.schemaDirIfSet(fs),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.promptDir = settings.PromptDir
|
||||||
|
cfg.profileDir = settings.ProfileDir
|
||||||
|
cfg.schemaDir = settings.SchemaDir
|
||||||
|
cfg.defaultRenderFormat = settings.DefaultRenderFormat
|
||||||
|
|
||||||
if strings.TrimSpace(cfg.promptDir) == "" {
|
if strings.TrimSpace(cfg.promptDir) == "" {
|
||||||
return errors.New("--prompt-dir is required")
|
return errors.New("--prompt-dir is required")
|
||||||
}
|
}
|
||||||
@@ -352,6 +395,73 @@ func finalizeExecutionRequestConfig(fs *flag.FlagSet, cfg *runConfig) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *runConfig) promptDirIfSet(fs *flag.FlagSet) string {
|
||||||
|
if flagWasSet(fs, "prompt-dir") {
|
||||||
|
return c.promptDir
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *runConfig) profileDirIfSet(fs *flag.FlagSet) string {
|
||||||
|
if flagWasSet(fs, "profile-dir") {
|
||||||
|
return c.profileDir
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *runConfig) schemaDirIfSet(fs *flag.FlagSet) string {
|
||||||
|
if flagWasSet(fs, "schema-dir") {
|
||||||
|
return c.schemaDir
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *serveConfig) promptDirIfSet(fs *flag.FlagSet) string {
|
||||||
|
if flagWasSet(fs, "prompt-dir") {
|
||||||
|
return c.promptDir
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *serveConfig) profileDirIfSet(fs *flag.FlagSet) string {
|
||||||
|
if flagWasSet(fs, "profile-dir") {
|
||||||
|
return c.profileDir
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *serveConfig) schemaDirIfSet(fs *flag.FlagSet) string {
|
||||||
|
if flagWasSet(fs, "schema-dir") {
|
||||||
|
return c.schemaDir
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *serveConfig) addrIfSet(fs *flag.FlagSet) string {
|
||||||
|
if flagWasSet(fs, "addr") {
|
||||||
|
return c.addr
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerConfigPathFlag(fs *flag.FlagSet, target *string) {
|
||||||
|
fs.StringVar(target, "config", "", fmt.Sprintf("application config path (default %s)", appconfig.DefaultConfigPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveAppSettings(fs *flag.FlagSet, configPath string, overrides appconfig.CLIOverrides) (appconfig.AppSettings, error) {
|
||||||
|
settings, err := appconfig.LoadConfig(configPath, flagWasSet(fs, "config"))
|
||||||
|
if err != nil {
|
||||||
|
return appconfig.AppSettings{}, fmt.Errorf("application config error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged, err := appconfig.ApplyCLIOverrides(settings, overrides)
|
||||||
|
if err != nil {
|
||||||
|
return appconfig.AppSettings{}, fmt.Errorf("application config error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return merged, nil
|
||||||
|
}
|
||||||
|
|
||||||
func buildRunRequestFromConfig(cfg *runConfig) (domain.RunRequest, error) {
|
func buildRunRequestFromConfig(cfg *runConfig) (domain.RunRequest, error) {
|
||||||
inputMappings, err := parseMappings(cfg.inputRaw, false)
|
inputMappings, err := parseMappings(cfg.inputRaw, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -482,7 +592,7 @@ func printSummary(stderr io.Writer, res *domain.RunResult) {
|
|||||||
|
|
||||||
func printUsage(w io.Writer) {
|
func printUsage(w io.Writer) {
|
||||||
fmt.Fprintln(w, "usage: scriptorium <run|render|serve> ...")
|
fmt.Fprintln(w, "usage: scriptorium <run|render|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, " run: scriptorium run [--config PATH] --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, " render: scriptorium render --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] [--format text|json] [--out path] [--timeout 10m]")
|
fmt.Fprintln(w, " render: scriptorium render [--config PATH] --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] [--format text|json] [--out path] [--timeout 10m]")
|
||||||
fmt.Fprintf(w, " serve: scriptorium serve --addr %s --prompt-dir DIR --profile-dir DIR [--schema-dir DIR]\n", defaults.HTTPAddrDefault)
|
fmt.Fprintf(w, " serve: scriptorium serve [--config PATH] --addr %s --prompt-dir DIR --profile-dir DIR [--schema-dir DIR]\n", defaults.HTTPAddrDefault)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -15,6 +16,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
appconfig "gitea.maximumdirect.net/eric/scriptorium/internal/config"
|
||||||
"gitea.maximumdirect.net/eric/scriptorium/internal/defaults"
|
"gitea.maximumdirect.net/eric/scriptorium/internal/defaults"
|
||||||
"gitea.maximumdirect.net/eric/scriptorium/internal/domain"
|
"gitea.maximumdirect.net/eric/scriptorium/internal/domain"
|
||||||
renderformat "gitea.maximumdirect.net/eric/scriptorium/internal/format"
|
renderformat "gitea.maximumdirect.net/eric/scriptorium/internal/format"
|
||||||
@@ -61,22 +63,24 @@ func TestParseMappingsMalformed(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseRunArgsRequiredFlags(t *testing.T) {
|
func TestParseRunArgsRequiredFlags(t *testing.T) {
|
||||||
_, err := parseRunArgs([]string{"--profile-dir", "./profiles", "--prompt", "p", "--input", "a=b"})
|
configPath := writeAppConfigFile(t, "")
|
||||||
|
|
||||||
|
_, err := parseRunArgs([]string{"--config", configPath, "--profile-dir", "./profiles", "--prompt", "p", "--input", "a=b"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected missing --prompt-dir error")
|
t.Fatal("expected missing --prompt-dir error")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = parseRunArgs([]string{"--prompt-dir", "./prompts", "--prompt", "p", "--input", "a=b"})
|
_, err = parseRunArgs([]string{"--config", configPath, "--prompt-dir", "./prompts", "--prompt", "p", "--input", "a=b"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected missing --profile-dir error")
|
t.Fatal("expected missing --profile-dir error")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = parseRunArgs([]string{"--prompt-dir", "./prompts", "--profile-dir", "./profiles", "--input", "a=b"})
|
_, err = parseRunArgs([]string{"--config", configPath, "--prompt-dir", "./prompts", "--profile-dir", "./profiles", "--input", "a=b"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected missing --prompt error")
|
t.Fatal("expected missing --prompt error")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = parseRunArgs([]string{"--prompt-dir", "./prompts", "--profile-dir", "./profiles", "--prompt", "p"})
|
_, err = parseRunArgs([]string{"--config", configPath, "--prompt-dir", "./prompts", "--profile-dir", "./profiles", "--prompt", "p"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected missing --input error")
|
t.Fatal("expected missing --input error")
|
||||||
}
|
}
|
||||||
@@ -140,17 +144,19 @@ func TestParseRunArgsRejectsRawLLMAPIKeyFlag(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseServeArgsRequiredFlags(t *testing.T) {
|
func TestParseServeArgsRequiredFlags(t *testing.T) {
|
||||||
_, err := parseServeArgs([]string{"--profile-dir", "./profiles"})
|
configPath := writeAppConfigFile(t, "")
|
||||||
|
|
||||||
|
_, err := parseServeArgs([]string{"--config", configPath, "--profile-dir", "./profiles"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected missing --prompt-dir error")
|
t.Fatal("expected missing --prompt-dir error")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = parseServeArgs([]string{"--prompt-dir", "./prompts"})
|
_, err = parseServeArgs([]string{"--config", configPath, "--prompt-dir", "./prompts"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected missing --profile-dir error")
|
t.Fatal("expected missing --profile-dir error")
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := parseServeArgs([]string{"--prompt-dir", "./prompts", "--profile-dir", "./profiles"})
|
cfg, err := parseServeArgs([]string{"--config", configPath, "--prompt-dir", "./prompts", "--profile-dir", "./profiles"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected valid serve args, got %v", err)
|
t.Fatalf("expected valid serve args, got %v", err)
|
||||||
}
|
}
|
||||||
@@ -266,6 +272,147 @@ func TestParseRenderArgsExplicitFormatsAndUnknown(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseRunArgsWithExplicitConfigLoadsDirectories(t *testing.T) {
|
||||||
|
configPath := writeAppConfigFile(t, `
|
||||||
|
prompt_dir: ./from-config/prompts
|
||||||
|
profile_dir: ./from-config/profiles
|
||||||
|
schema_dir: ./from-config/schemas
|
||||||
|
`)
|
||||||
|
|
||||||
|
cfg, err := parseRunArgs([]string{
|
||||||
|
"--config", configPath,
|
||||||
|
"--prompt", "p",
|
||||||
|
"--input", "a=b",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected valid args, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.promptDir != filepath.Clean("./from-config/prompts") {
|
||||||
|
t.Fatalf("expected prompt dir from config, got %q", cfg.promptDir)
|
||||||
|
}
|
||||||
|
if cfg.profileDir != filepath.Clean("./from-config/profiles") {
|
||||||
|
t.Fatalf("expected profile dir from config, got %q", cfg.profileDir)
|
||||||
|
}
|
||||||
|
if cfg.schemaDir != filepath.Clean("./from-config/schemas") {
|
||||||
|
t.Fatalf("expected schema dir from config, got %q", cfg.schemaDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseRunArgsMissingExplicitConfigReturnsError(t *testing.T) {
|
||||||
|
_, err := parseRunArgs([]string{
|
||||||
|
"--config", filepath.Join(t.TempDir(), "missing.yml"),
|
||||||
|
"--prompt", "p",
|
||||||
|
"--input", "a=b",
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected explicit config missing error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveAppSettingsMissingImplicitConfigDoesNotError(t *testing.T) {
|
||||||
|
fs := flag.NewFlagSet("test", flag.ContinueOnError)
|
||||||
|
settings, err := resolveAppSettings(fs, filepath.Join(t.TempDir(), "missing.yml"), appconfig.CLIOverrides{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
if settings.SchemaDir != defaults.SchemaDirDefault {
|
||||||
|
t.Fatalf("expected built-in schema dir, got %q", settings.SchemaDir)
|
||||||
|
}
|
||||||
|
if settings.ServerAddr != defaults.HTTPAddrDefault {
|
||||||
|
t.Fatalf("expected built-in server addr, got %q", settings.ServerAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseRunArgsCLIOverridesConfigDirectories(t *testing.T) {
|
||||||
|
configPath := writeAppConfigFile(t, `
|
||||||
|
prompt_dir: ./from-config/prompts
|
||||||
|
profile_dir: ./from-config/profiles
|
||||||
|
schema_dir: ./from-config/schemas
|
||||||
|
`)
|
||||||
|
|
||||||
|
cfg, err := parseRunArgs([]string{
|
||||||
|
"--config", configPath,
|
||||||
|
"--prompt-dir", "./from-cli/prompts",
|
||||||
|
"--profile-dir", "./from-cli/profiles",
|
||||||
|
"--schema-dir", "./from-cli/schemas",
|
||||||
|
"--prompt", "p",
|
||||||
|
"--input", "a=b",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected valid args, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.promptDir != filepath.Clean("./from-cli/prompts") {
|
||||||
|
t.Fatalf("expected CLI prompt dir override, got %q", cfg.promptDir)
|
||||||
|
}
|
||||||
|
if cfg.profileDir != filepath.Clean("./from-cli/profiles") {
|
||||||
|
t.Fatalf("expected CLI profile dir override, got %q", cfg.profileDir)
|
||||||
|
}
|
||||||
|
if cfg.schemaDir != filepath.Clean("./from-cli/schemas") {
|
||||||
|
t.Fatalf("expected CLI schema dir override, got %q", cfg.schemaDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseRenderArgsWithExplicitConfigLoadsDirectoriesAndFormat(t *testing.T) {
|
||||||
|
configPath := writeAppConfigFile(t, `
|
||||||
|
prompt_dir: ./from-config/prompts
|
||||||
|
profile_dir: ./from-config/profiles
|
||||||
|
defaults:
|
||||||
|
render_format: json
|
||||||
|
`)
|
||||||
|
|
||||||
|
cfg, err := parseRenderArgs([]string{
|
||||||
|
"--config", configPath,
|
||||||
|
"--prompt", "p",
|
||||||
|
"--input", "a=b",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected valid args, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.promptDir != filepath.Clean("./from-config/prompts") {
|
||||||
|
t.Fatalf("expected prompt dir from config, got %q", cfg.promptDir)
|
||||||
|
}
|
||||||
|
if cfg.profileDir != filepath.Clean("./from-config/profiles") {
|
||||||
|
t.Fatalf("expected profile dir from config, got %q", cfg.profileDir)
|
||||||
|
}
|
||||||
|
if cfg.outputFormat != renderformat.PreparedRunFormatJSON {
|
||||||
|
t.Fatalf("expected render format from config, got %q", cfg.outputFormat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseServeArgsWithExplicitConfigLoadsSettingsAndCLIAddrOverrides(t *testing.T) {
|
||||||
|
configPath := writeAppConfigFile(t, `
|
||||||
|
prompt_dir: ./from-config/prompts
|
||||||
|
profile_dir: ./from-config/profiles
|
||||||
|
schema_dir: ./from-config/schemas
|
||||||
|
server:
|
||||||
|
addr: 127.0.0.1:9000
|
||||||
|
`)
|
||||||
|
|
||||||
|
cfg, err := parseServeArgs([]string{
|
||||||
|
"--config", configPath,
|
||||||
|
"--addr", ":7777",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected valid args, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.promptDir != filepath.Clean("./from-config/prompts") {
|
||||||
|
t.Fatalf("expected prompt dir from config, got %q", cfg.promptDir)
|
||||||
|
}
|
||||||
|
if cfg.profileDir != filepath.Clean("./from-config/profiles") {
|
||||||
|
t.Fatalf("expected profile dir from config, got %q", cfg.profileDir)
|
||||||
|
}
|
||||||
|
if cfg.schemaDir != filepath.Clean("./from-config/schemas") {
|
||||||
|
t.Fatalf("expected schema dir from config, got %q", cfg.schemaDir)
|
||||||
|
}
|
||||||
|
if cfg.addr != ":7777" {
|
||||||
|
t.Fatalf("expected CLI addr override, got %q", cfg.addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDetermineExitCode(t *testing.T) {
|
func TestDetermineExitCode(t *testing.T) {
|
||||||
if got := determineExitCode(errors.New("boom"), nil); got != ExitRuntimeError {
|
if got := determineExitCode(errors.New("boom"), nil); got != ExitRuntimeError {
|
||||||
t.Fatalf("expected runtime exit code, got %d", got)
|
t.Fatalf("expected runtime exit code, got %d", got)
|
||||||
@@ -822,3 +969,12 @@ func newTestLLMServer(content string, hitCounter *int32) *httptest.Server {
|
|||||||
_, _ = w.Write([]byte(fmt.Sprintf(`{"choices":[{"message":{"role":"assistant","content":%q}}],"usage":{"prompt_tokens":1,"completion_tokens":1,"total_tokens":2}}`, content)))
|
_, _ = w.Write([]byte(fmt.Sprintf(`{"choices":[{"message":{"role":"assistant","content":%q}}],"usage":{"prompt_tokens":1,"completion_tokens":1,"total_tokens":2}}`, content)))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeAppConfigFile(t *testing.T, content string) string {
|
||||||
|
t.Helper()
|
||||||
|
path := filepath.Join(t.TempDir(), "config.yml")
|
||||||
|
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
||||||
|
t.Fatalf("failed to write app config fixture: %v", err)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user