Skip to content

Commit f46dc26

Browse files
committed
chore: refactor flag parsing to use var binding and shorthands
Refactors flag parsing to use var binding, cobra style validation and shorthands. Also disambiguates CLI output format and inference output format
1 parent 72cd1b9 commit f46dc26

8 files changed

Lines changed: 307 additions & 254 deletions

File tree

cmd/auth.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,48 +20,48 @@ var authCmd = &cobra.Command{
2020
Long: "Login, logout, check status, and switch environments.",
2121
}
2222

23+
var authLoginKey string
24+
2325
var authLoginCmd = &cobra.Command{
2426
Use: "login",
2527
Short: "Authenticate with an API key",
2628
RunE: func(cmd *cobra.Command, args []string) error {
27-
key, _ := cmd.Flags().GetString("key")
28-
29-
if key == "" {
29+
if authLoginKey == "" {
3030
fmt.Fprint(os.Stderr, "Enter your Runware API key: ")
3131
if term.IsTerminal(int(os.Stdin.Fd())) {
3232
raw, err := term.ReadPassword(int(os.Stdin.Fd()))
3333
if err != nil {
3434
return fmt.Errorf("failed to read API key: %w", err)
3535
}
3636
fmt.Fprintln(os.Stderr)
37-
key = string(raw)
37+
authLoginKey = string(raw)
3838
} else {
3939
scanner := bufio.NewScanner(os.Stdin)
4040
if scanner.Scan() {
41-
key = scanner.Text()
41+
authLoginKey = scanner.Text()
4242
}
4343
}
4444
}
4545

46-
key = strings.TrimSpace(key)
47-
if key == "" {
46+
authLoginKey = strings.TrimSpace(authLoginKey)
47+
if authLoginKey == "" {
4848
return fmt.Errorf("API key cannot be empty")
4949
}
5050

5151
// Validate key by pinging the API
52-
client := api.NewClient(key, config.GetBaseURL(), flagVerbose)
52+
client := api.NewClient(authLoginKey, config.GetBaseURL(), flagVerbose)
5353
_, err := client.Ping(context.Background())
5454
if err != nil {
5555
output.Error("Invalid API key. Authentication failed.")
5656
return err
5757
}
5858

5959
env := config.GetEnvironment()
60-
if err := config.SetAPIKey(env, key); err != nil {
60+
if err := config.SetAPIKey(env, authLoginKey); err != nil {
6161
return fmt.Errorf("failed to save API key: %w", err)
6262
}
6363

64-
output.Success(fmt.Sprintf("Authenticated successfully (%s) — key: %s", env, config.MaskKey(key)))
64+
output.Success(fmt.Sprintf("Authenticated successfully (%s) — key: %s", env, config.MaskKey(authLoginKey)))
6565
return nil
6666
},
6767
}
@@ -145,7 +145,7 @@ var authSwitchCmd = &cobra.Command{
145145
}
146146

147147
func init() {
148-
authLoginCmd.Flags().String("key", "", "API key (or provide interactively)")
148+
authLoginCmd.Flags().StringVarP(&authLoginKey, "key", "k", "", "API key (or provide interactively)")
149149
authCmd.AddCommand(authLoginCmd)
150150
authCmd.AddCommand(authLogoutCmd)
151151
authCmd.AddCommand(authStatusCmd)

cmd/inference_audio.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func init() {
5252
f.Float64VarP(&audioFlags.duration, "duration", "d", defaultMinAudioDuration, "Audio duration in seconds (10-300)")
5353
f.IntVarP(&audioFlags.count, "count", "n", 1, "Number of audio files to generate (max 3)")
5454
f.StringVarP(&audioFlags.outputDir, "output", "o", "", "Output directory")
55-
f.StringVarP(&audioFlags.outputFormat, "output-format", "f", "", "Audio format: mp3")
55+
f.StringVarP(&audioFlags.outputFormat, "output-format", "f", "", "Format of generated audio: mp3")
5656
f.BoolVarP(&audioFlags.noDownload, "no-download", "D", false, "Print audio URLs instead of downloading")
5757
f.BoolVarP(&audioFlags.includeCost, "include-cost", "c", false, "Include cost info in response")
5858
f.IntVarP(&audioFlags.sampleRate, "sample-rate", "r", 0, "Sample rate in Hz (8000-48000)")

cmd/inference_image.go

Lines changed: 76 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,27 @@ import (
1717
"github.com/spf13/cobra"
1818
)
1919

20+
var imageFlags struct {
21+
model string
22+
width int
23+
height int
24+
steps int
25+
cfgScale float64
26+
scheduler string
27+
seed int64
28+
negative string
29+
count int
30+
outputDir string
31+
outputFormat string
32+
noDownload bool
33+
sourcePath string
34+
strength float64
35+
maskPath string
36+
preset string
37+
dryRun bool
38+
downloadTimeout time.Duration
39+
}
40+
2041
var imageInferenceCmd = &cobra.Command{
2142
Use: "image [prompt]",
2243
Short: "Generate images from text or image input",
@@ -26,30 +47,31 @@ Examples:
2647
runware inference image "a cat riding a rocket"
2748
runware inference image "make it cinematic" --source ./input.png --strength 0.7
2849
runware inference image "replace with a dog" --source ./photo.png --mask ./mask.png`,
29-
Args: cobra.ExactArgs(1),
30-
RunE: runImageInference,
50+
Args: cobra.ExactArgs(1),
51+
PreRunE: preRunImageInference,
52+
RunE: runImageInference,
3153
}
3254

3355
func init() {
3456
f := imageInferenceCmd.Flags()
35-
f.String("model", "", "Model identifier")
36-
f.Int("width", 0, "Image width")
37-
f.Int("height", 0, "Image height")
38-
f.Int("steps", 0, "Number of inference steps")
39-
f.Float64("cfg", 0, "CFG scale")
40-
f.String("scheduler", "", "Scheduler (e.g. euler, dpm++)")
41-
f.Int64("seed", 0, "Seed for reproducibility")
42-
f.String("negative", "", "Negative prompt")
43-
f.Int("count", 1, "Number of images to generate")
44-
f.String("output", "", "Output directory")
45-
f.String("output-format", "", "Image format: png, jpg, webp")
46-
f.Bool("no-download", false, "Print image URLs instead of downloading")
47-
f.String("source", "", "Source image path for img2img")
48-
f.Float64("strength", 0.7, "img2img strength (0.0-1.0)")
49-
f.String("mask", "", "Mask image path for inpainting")
50-
f.String("preset", "", "Named preset to apply")
51-
f.Bool("dry-run", false, "Print the API request without executing")
52-
f.Duration("download-timeout", defaultImageDownloadTimeout, "timeout to use when downloading image inference results")
57+
f.StringVarP(&imageFlags.model, "model", "m", "", "Model identifier")
58+
f.IntVarP(&imageFlags.width, "width", "W", 0, "Image width")
59+
f.IntVarP(&imageFlags.height, "height", "H", 0, "Image height")
60+
f.IntVarP(&imageFlags.steps, "steps", "s", 0, "Number of inference steps")
61+
f.Float64VarP(&imageFlags.cfgScale, "cfg", "c", 0, "CFG scale")
62+
f.StringVarP(&imageFlags.scheduler, "scheduler", "S", "", "Scheduler (e.g. euler, dpm++)")
63+
f.Int64VarP(&imageFlags.seed, "seed", "e", 0, "Seed for reproducibility")
64+
f.StringVarP(&imageFlags.negative, "negative", "N", "", "Negative prompt")
65+
f.IntVarP(&imageFlags.count, "count", "n", 1, "Number of images to generate")
66+
f.StringVarP(&imageFlags.outputDir, "output", "o", "", "Output directory")
67+
f.StringVarP(&imageFlags.outputFormat, "output-format", "f", "", "Format of generated images: png, jpg, webp")
68+
f.BoolVarP(&imageFlags.noDownload, "no-download", "D", false, "Print image URLs instead of downloading")
69+
f.StringVarP(&imageFlags.sourcePath, "source", "i", "", "Source image path for img2img")
70+
f.Float64VarP(&imageFlags.strength, "strength", "k", 0.7, "img2img strength (0.0-1.0)")
71+
f.StringVarP(&imageFlags.maskPath, "mask", "M", "", "Mask image path for inpainting")
72+
f.StringVarP(&imageFlags.preset, "preset", "p", "", "Named preset to apply")
73+
f.BoolVarP(&imageFlags.dryRun, "dry-run", "X", false, "Print the API request without executing")
74+
f.DurationVarP(&imageFlags.downloadTimeout, "download-timeout", "T", defaultImageDownloadTimeout, "Timeout for downloading image results")
5375

5476
imageInferenceCmd.RegisterFlagCompletionFunc("scheduler", func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) { //nolint:errcheck,gosec
5577
return []cobra.Completion{
@@ -88,11 +110,17 @@ func init() {
88110

89111
// completePresetNames provides dynamic completion for preset names from config.
90112
func completePresetNames(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
91-
// Ensure config is loaded for completion context
92113
config.Init() //nolint:errcheck,gosec
93114
return config.ListPresets(), cobra.ShellCompDirectiveNoFileComp
94115
}
95116

117+
func preRunImageInference(cmd *cobra.Command, _ []string) error {
118+
if imageFlags.maskPath != "" && imageFlags.sourcePath == "" {
119+
return fmt.Errorf("--mask requires --source to be set")
120+
}
121+
return nil
122+
}
123+
96124
func runImageInference(cmd *cobra.Command, args []string) error {
97125
ctx := cmd.Context()
98126
if ctx == nil {
@@ -108,7 +136,7 @@ func runImageInference(cmd *cobra.Command, args []string) error {
108136
cfg := config.Get()
109137
prompt := args[0]
110138

111-
// Start with config defaults for universal fields
139+
// Start with config defaults
112140
model := cfg.Defaults.Model
113141
width := cfg.Defaults.Width
114142
height := cfg.Defaults.Height
@@ -121,11 +149,10 @@ func runImageInference(cmd *cobra.Command, args []string) error {
121149
var scheduler string
122150

123151
// Apply preset if specified
124-
presetName, _ := cmd.Flags().GetString("preset")
125-
if presetName != "" {
126-
preset := config.GetPreset(presetName)
152+
if imageFlags.preset != "" {
153+
preset := config.GetPreset(imageFlags.preset)
127154
if preset == nil {
128-
return fmt.Errorf("preset '%s' not found", presetName)
155+
return fmt.Errorf("preset '%s' not found", imageFlags.preset)
129156
}
130157
if preset.Model != "" {
131158
model = preset.Model
@@ -149,57 +176,41 @@ func runImageInference(cmd *cobra.Command, args []string) error {
149176

150177
// Override with explicit CLI flags
151178
if cmd.Flags().Changed("model") {
152-
model, _ = cmd.Flags().GetString("model")
179+
model = imageFlags.model
153180
}
154181
if cmd.Flags().Changed("width") {
155-
width, _ = cmd.Flags().GetInt("width")
182+
width = imageFlags.width
156183
}
157184
if cmd.Flags().Changed("height") {
158-
height, _ = cmd.Flags().GetInt("height")
185+
height = imageFlags.height
159186
}
160187
if cmd.Flags().Changed("steps") {
161-
steps, _ = cmd.Flags().GetInt("steps")
188+
steps = imageFlags.steps
162189
}
163190
if cmd.Flags().Changed("cfg") {
164-
cfgScale, _ = cmd.Flags().GetFloat64("cfg")
191+
cfgScale = imageFlags.cfgScale
165192
}
166193
if cmd.Flags().Changed("scheduler") {
167-
scheduler, _ = cmd.Flags().GetString("scheduler")
194+
scheduler = imageFlags.scheduler
168195
}
169196
if cmd.Flags().Changed("output") {
170-
outputDir, _ = cmd.Flags().GetString("output")
197+
outputDir = imageFlags.outputDir
171198
}
172199
if cmd.Flags().Changed("output-format") {
173-
outputFormat, _ = cmd.Flags().GetString("output-format")
200+
outputFormat = imageFlags.outputFormat
174201
}
175202

176-
count, _ := cmd.Flags().GetInt("count")
177-
seed, _ := cmd.Flags().GetInt64("seed")
178-
negative, _ := cmd.Flags().GetString("negative")
179-
noDownload, _ := cmd.Flags().GetBool("no-download")
180-
dryRun, _ := cmd.Flags().GetBool("dry-run")
181-
sourcePath, _ := cmd.Flags().GetString("source")
182-
strength, _ := cmd.Flags().GetFloat64("strength")
183-
maskPath, _ := cmd.Flags().GetString("mask")
184-
downloadTimeout, _ := cmd.Flags().GetDuration("download-timeout")
185-
186-
// Validation
187-
if maskPath != "" && sourcePath == "" {
188-
return fmt.Errorf("--mask requires --source to be set")
189-
}
190-
191-
// Build request — universal fields always included
203+
// Build request
192204
req := &api.ImageInferenceRequest{
193205
TaskUUID: api.NewUUID(),
194206
PositivePrompt: prompt,
195207
Model: model,
196208
Width: width,
197209
Height: height,
198-
NumberResults: count,
210+
NumberResults: imageFlags.count,
199211
OutputFormat: api.OutputFormat(outputFormat),
200212
}
201213

202-
// Model-specific fields — only included when explicitly set
203214
if steps > 0 {
204215
req.Steps = steps
205216
}
@@ -209,34 +220,34 @@ func runImageInference(cmd *cobra.Command, args []string) error {
209220
if scheduler != "" {
210221
req.Scheduler = scheduler
211222
}
212-
if negative != "" {
213-
req.NegativePrompt = negative
223+
if imageFlags.negative != "" {
224+
req.NegativePrompt = imageFlags.negative
214225
}
215226
if cmd.Flags().Changed("seed") {
216-
req.Seed = seed
227+
req.Seed = imageFlags.seed
217228
}
218229

219230
// Handle source image (img2img)
220-
if sourcePath != "" {
221-
encoded, err := encodeImageFile(sourcePath)
231+
if imageFlags.sourcePath != "" {
232+
encoded, err := encodeImageFile(imageFlags.sourcePath)
222233
if err != nil {
223234
return fmt.Errorf("failed to read source image: %w", err)
224235
}
225236
req.InputImage = encoded
226-
req.Strength = strength
237+
req.Strength = imageFlags.strength
227238
}
228239

229240
// Handle mask image (inpainting)
230-
if maskPath != "" {
231-
encoded, err := encodeImageFile(maskPath)
241+
if imageFlags.maskPath != "" {
242+
encoded, err := encodeImageFile(imageFlags.maskPath)
232243
if err != nil {
233244
return fmt.Errorf("failed to read mask image: %w", err)
234245
}
235246
req.MaskImage = encoded
236247
}
237248

238-
// Dry run — print request and exit
239-
if dryRun {
249+
// Dry run
250+
if imageFlags.dryRun {
240251
data, _ := json.MarshalIndent([]any{req}, "", " ")
241252
fmt.Println(string(data))
242253
return nil
@@ -247,7 +258,6 @@ func runImageInference(cmd *cobra.Command, args []string) error {
247258
s.Start()
248259

249260
client := api.NewClient(key, config.GetBaseURL(), flagVerbose)
250-
251261
results, err := client.ImageInference(ctx, req)
252262
if err != nil {
253263
s.Stop()
@@ -271,14 +281,13 @@ func runImageInference(cmd *cobra.Command, args []string) error {
271281
return output.Print(format, results, nil, nil)
272282
}
273283

274-
// Table output — download or print URLs
275-
if noDownload {
284+
// Table output — no-download: print URLs
285+
if imageFlags.noDownload {
276286
headers := []any{tableHeaderNum, tableHeaderURL, tableHeaderSeed}
277287
var rows [][]any
278288
for i, r := range results {
279289
rows = append(rows, []any{i + 1, r.ImageURL, r.Seed})
280290
}
281-
282291
s.Stop()
283292
return output.Print(format, results, headers, rows)
284293
}
@@ -301,7 +310,7 @@ func runImageInference(cmd *cobra.Command, args []string) error {
301310
filename := fmt.Sprintf("runware_%s_%d.%s", time.Now().Format("20060102_150405"), i+1, ext)
302311
destPath := filepath.Join(outputDir, filename)
303312

304-
if err := rhttp.Download(ctx, r.ImageURL, destPath, downloadTimeout); err != nil {
313+
if err := rhttp.Download(ctx, r.ImageURL, destPath, imageFlags.downloadTimeout); err != nil {
305314
output.Error(fmt.Sprintf("Failed to download image %d: %s", i+1, err))
306315
continue
307316
}

0 commit comments

Comments
 (0)