@@ -204,10 +204,16 @@ func NewRunCommand(cfg *command.Config) *cobra.Command {
204
204
If you know which model you want to run inference with, you can run the request in a single command
205
205
as %[1]sgh models run [model] [prompt]%[1]s
206
206
207
+ When using prompt files, you can pass template variables using the %[1]s--var%[1]s flag:
208
+ %[1]sgh models run --file prompt.yml --var name=Alice --var topic=AI%[1]s
209
+
207
210
The return value will be the response to your prompt from the selected model.
208
211
` , "`" ),
209
- Example : "gh models run openai/gpt-4o-mini \" how many types of hyena are there?\" " ,
210
- Args : cobra .ArbitraryArgs ,
212
+ Example : heredoc .Doc (`
213
+ gh models run openai/gpt-4o-mini "how many types of hyena are there?"
214
+ gh models run --file prompt.yml --var name=Alice --var topic="machine learning"
215
+ ` ),
216
+ Args : cobra .ArbitraryArgs ,
211
217
RunE : func (cmd * cobra.Command , args []string ) error {
212
218
filePath , _ := cmd .Flags ().GetString ("file" )
213
219
var pf * prompt.File
@@ -223,6 +229,12 @@ func NewRunCommand(cfg *command.Config) *cobra.Command {
223
229
}
224
230
}
225
231
232
+ // Parse template variables from flags
233
+ templateVars , err := parseTemplateVariables (cmd .Flags ())
234
+ if err != nil {
235
+ return err
236
+ }
237
+
226
238
cmdHandler := newRunCommandHandler (cmd , cfg , args )
227
239
if cmdHandler == nil {
228
240
return nil
@@ -270,16 +282,22 @@ func NewRunCommand(cfg *command.Config) *cobra.Command {
270
282
}
271
283
272
284
// If there is no prompt file, add the initialPrompt to the conversation.
273
- // If a prompt file is passed, load the messages from the file, templating {{input}}
274
- // using the initialPrompt.
285
+ // If a prompt file is passed, load the messages from the file, templating variables
286
+ // using the provided template variables and initialPrompt.
275
287
if pf == nil {
276
288
conversation .AddMessage (azuremodels .ChatMessageRoleUser , initialPrompt )
277
289
} else {
278
290
interactiveMode = false
279
291
280
- // Template the messages with the input
281
- templateData := map [string ]interface {}{
282
- "input" : initialPrompt ,
292
+ // Template the messages with the variables
293
+ templateData := make (map [string ]interface {})
294
+
295
+ // Add the input variable (backward compatibility)
296
+ templateData ["input" ] = initialPrompt
297
+
298
+ // Add custom variables
299
+ for key , value := range templateVars {
300
+ templateData [key ] = value
283
301
}
284
302
285
303
for _ , m := range pf .Messages {
@@ -385,6 +403,7 @@ func NewRunCommand(cfg *command.Config) *cobra.Command {
385
403
}
386
404
387
405
cmd .Flags ().String ("file" , "" , "Path to a .prompt.yml file." )
406
+ cmd .Flags ().StringSlice ("var" , []string {}, "Template variables for prompt files (can be used multiple times: --var name=value)" )
388
407
cmd .Flags ().String ("max-tokens" , "" , "Limit the maximum tokens for the model response." )
389
408
cmd .Flags ().String ("temperature" , "" , "Controls randomness in the response, use lower to be more deterministic." )
390
409
cmd .Flags ().String ("top-p" , "" , "Controls text diversity by selecting the most probable words until a set probability is reached." )
@@ -393,6 +412,43 @@ func NewRunCommand(cfg *command.Config) *cobra.Command {
393
412
return cmd
394
413
}
395
414
415
+ // parseTemplateVariables parses template variables from the --var flags
416
+ func parseTemplateVariables (flags * pflag.FlagSet ) (map [string ]string , error ) {
417
+ varFlags , err := flags .GetStringSlice ("var" )
418
+ if err != nil {
419
+ return nil , err
420
+ }
421
+
422
+ templateVars := make (map [string ]string )
423
+ for _ , varFlag := range varFlags {
424
+ // Handle empty strings
425
+ if strings .TrimSpace (varFlag ) == "" {
426
+ continue
427
+ }
428
+
429
+ parts := strings .SplitN (varFlag , "=" , 2 )
430
+ if len (parts ) != 2 {
431
+ return nil , fmt .Errorf ("invalid variable format '%s', expected 'key=value'" , varFlag )
432
+ }
433
+
434
+ key := strings .TrimSpace (parts [0 ])
435
+ value := parts [1 ] // Don't trim value to preserve intentional whitespace
436
+
437
+ if key == "" {
438
+ return nil , fmt .Errorf ("variable key cannot be empty in '%s'" , varFlag )
439
+ }
440
+
441
+ // Check for duplicate keys
442
+ if _ , exists := templateVars [key ]; exists {
443
+ return nil , fmt .Errorf ("duplicate variable key '%s'" , key )
444
+ }
445
+
446
+ templateVars [key ] = value
447
+ }
448
+
449
+ return templateVars , nil
450
+ }
451
+
396
452
type runCommandHandler struct {
397
453
ctx context.Context
398
454
cfg * command.Config
@@ -445,7 +501,7 @@ func (h *runCommandHandler) getModelNameFromArgs(models []*azuremodels.ModelSumm
445
501
}
446
502
447
503
func validateModelName (modelName string , models []* azuremodels.ModelSummary ) (string , error ) {
448
- noMatchErrorMessage := "The specified model name is not found. Run 'gh models list' to see available models or 'gh models run' to select interactively."
504
+ noMatchErrorMessage := fmt . Sprintf ( "The specified model '%s' is not found. Run 'gh models list' to see available models or 'gh models run' to select interactively." , modelName )
449
505
450
506
if modelName == "" {
451
507
return "" , errors .New (noMatchErrorMessage )
0 commit comments