package ckprompt import ( "fmt" "strconv" commandkit "go.maximumdirect.net/commandkit" promptkit "go.maximumdirect.net/promptkit" ) // requestFlagDefinition keeps the canonical spellings, help text, and optional // command-scoped aliases together for consumers of the standard flag sets. type requestFlagDefinition struct { meta commandkit.FlagMeta aliases []string } var requestFlags = struct { Endpoint requestFlagDefinition Model requestFlagDefinition Temperature requestFlagDefinition MaxTokens requestFlagDefinition TopP requestFlagDefinition TimeoutSeconds requestFlagDefinition ServiceTier requestFlagDefinition ReasoningEffort requestFlagDefinition APIKeyEnv requestFlagDefinition TransientRetries requestFlagDefinition ExtraParams requestFlagDefinition Prompt requestFlagDefinition Profile requestFlagDefinition Backend requestFlagDefinition SessionID requestFlagDefinition Var requestFlagDefinition InputText requestFlagDefinition InputFile requestFlagDefinition }{ Endpoint: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-endpoint", Usage: "Execution endpoint override."}, []string{"endpoint"}}, Model: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-model", Usage: "Execution model override."}, []string{"model"}}, Temperature: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-temperature", Usage: "Execution temperature override."}, []string{"temperature"}}, MaxTokens: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-max-tokens", Usage: "Execution maximum tokens override."}, []string{"max-tokens"}}, TopP: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-top-p", Usage: "Execution top-p override."}, []string{"top-p"}}, TimeoutSeconds: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-timeout-seconds", Usage: "Execution timeout override in seconds."}, []string{"timeout-seconds"}}, ServiceTier: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-service-tier", Usage: "Execution service tier."}, []string{"service-tier"}}, ReasoningEffort: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-reasoning-effort", Usage: "Execution reasoning effort override."}, []string{"reasoning-effort"}}, APIKeyEnv: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-api-key-env", Usage: "Execution API key environment variable."}, []string{"api-key-env"}}, TransientRetries: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-transient-retries", Usage: "Inference transient retry count."}, []string{"transient-retries"}}, ExtraParams: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-extra-params", Usage: "Execution provider parameters as a JSON object."}, []string{"extra-params"}}, Prompt: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-prompt", Usage: "PromptKit prompt ID."}, []string{"prompt"}}, Profile: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-profile", Usage: "PromptKit profile ID."}, []string{"profile"}}, Backend: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-backend", Usage: "PromptKit backend ID."}, []string{"backend"}}, SessionID: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-session-id", Usage: "PromptKit session ID."}, []string{"session-id"}}, Var: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-var", Usage: "Caller variable assignment KEY=VALUE."}, []string{"var"}}, InputText: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-input-text", Usage: "Literal prompt input assignment NAME=TEXT."}, []string{"input-text", "llm-input", "input"}}, InputFile: requestFlagDefinition{commandkit.FlagMeta{Name: "llm-input-file", Usage: "Prompt input file assignment NAME=PATH."}, []string{"input-file"}}, } // ExecutionInput contains request-scoped execution controls supplied on the CLI. // A nil override or retry count leaves the corresponding native default intact. type ExecutionInput struct { Execution *promptkit.ExecutionTargetOverride TransientRetries *int } // ExecutionFlags exposes independently selectable execution-control bindings. type ExecutionFlags[I any] struct { Endpoint, Model, Temperature, MaxTokens, TopP, TimeoutSeconds commandkit.Flag[I] ServiceTier, ReasoningEffort, APIKeyEnv, TransientRetries, ExtraParams commandkit.Flag[I] } // All returns all execution controls. func (f ExecutionFlags[I]) All() []commandkit.Flag[I] { return appendRequestBindings(nil, "", nil, f.bindings()...) } func (f ExecutionFlags[I]) bindings() []requestBinding[I] { return []requestBinding[I]{ {f.Endpoint, requestFlags.Endpoint}, {f.Model, requestFlags.Model}, {f.Temperature, requestFlags.Temperature}, {f.MaxTokens, requestFlags.MaxTokens}, {f.TopP, requestFlags.TopP}, {f.TimeoutSeconds, requestFlags.TimeoutSeconds}, {f.ServiceTier, requestFlags.ServiceTier}, {f.ReasoningEffort, requestFlags.ReasoningEffort}, {f.APIKeyEnv, requestFlags.APIKeyEnv}, {f.TransientRetries, requestFlags.TransientRetries}, {f.ExtraParams, requestFlags.ExtraParams}, } } // NewExecutionFlags binds execution controls to a field of the caller's input. func NewExecutionFlags[I any](destination func(*I) *ExecutionInput) ExecutionFlags[I] { target := func(input *I) *promptkit.ExecutionTargetOverride { value := destination(input) if value.Execution == nil { value.Execution = &promptkit.ExecutionTargetOverride{} } return value.Execution } return ExecutionFlags[I]{ Endpoint: executionFlag(requestFlags.Endpoint.meta, parseDefinitionString, func(i *I, v string) { target(i).Endpoint = v }), Model: executionFlag(requestFlags.Model.meta, parseDefinitionString, func(i *I, v string) { target(i).Model = v }), Temperature: executionFlag(requestFlags.Temperature.meta, parseExecutionFloat, func(i *I, v float64) { target(i).Temperature = &v }), MaxTokens: executionFlag(requestFlags.MaxTokens.meta, strconv.Atoi, func(i *I, v int) { target(i).MaxTokens = &v }), TopP: executionFlag(requestFlags.TopP.meta, parseExecutionFloat, func(i *I, v float64) { target(i).TopP = &v }), TimeoutSeconds: executionFlag(requestFlags.TimeoutSeconds.meta, strconv.Atoi, func(i *I, v int) { target(i).TimeoutSeconds = &v }), ServiceTier: executionFlag(requestFlags.ServiceTier.meta, parseDefinitionString, func(i *I, v string) { target(i).ServiceTier = v }), ReasoningEffort: executionFlag(requestFlags.ReasoningEffort.meta, parseDefinitionString, func(i *I, v string) { target(i).ReasoningEffort = &v }), APIKeyEnv: executionFlag(requestFlags.APIKeyEnv.meta, parseDefinitionString, func(i *I, v string) { target(i).APIKeyEnv = v }), TransientRetries: executionFlag(requestFlags.TransientRetries.meta, strconv.Atoi, func(i *I, v int) { destination(i).TransientRetries = &v }), ExtraParams: executionFlag(requestFlags.ExtraParams.meta, parseExtraParams, func(i *I, v map[string]any) { target(i).ExtraParams = v }), } } func executionFlag[I, V any](meta commandkit.FlagMeta, parse func(string) (V, error), set func(*I, V)) commandkit.Flag[I] { return commandkit.ValueFlag(meta, (*V)(nil), parse, func(i *I, value commandkit.Supplied[V]) { if value.Set { set(i, value.Value) } }) } func parseExtraParams(raw string) (map[string]any, error) { value, err := commandkit.ParseCLIJSON(raw) if err != nil { return nil, err } object, ok := value.(map[string]any) if !ok { return nil, fmt.Errorf("provider parameters must be a JSON object") } return object, nil } // ResourceInput retains whether each resource identifier was explicitly supplied. type ResourceInput struct { Prompt, Profile, Backend, SessionID commandkit.Supplied[string] } // ResourceFlags exposes independently selectable resource and session bindings. type ResourceFlags[I any] struct { Prompt, Profile, Backend, SessionID commandkit.Flag[I] } // All returns all resource bindings. func (f ResourceFlags[I]) All() []commandkit.Flag[I] { return []commandkit.Flag[I]{f.Prompt, f.Profile, f.Backend, f.SessionID} } // NewResourceFlags binds resource identifiers to a field of the caller's input. func NewResourceFlags[I any](destination func(*I) *ResourceInput) ResourceFlags[I] { flag := func(meta commandkit.FlagMeta, selectField func(*ResourceInput) *commandkit.Supplied[string]) commandkit.Flag[I] { return commandkit.ValueFlag(meta, (*string)(nil), parseDefinitionString, func(i *I, v commandkit.Supplied[string]) { *selectField(destination(i)) = v }) } return ResourceFlags[I]{ Prompt: flag(requestFlags.Prompt.meta, func(v *ResourceInput) *commandkit.Supplied[string] { return &v.Prompt }), Profile: flag(requestFlags.Profile.meta, func(v *ResourceInput) *commandkit.Supplied[string] { return &v.Profile }), Backend: flag(requestFlags.Backend.meta, func(v *ResourceInput) *commandkit.Supplied[string] { return &v.Backend }), SessionID: flag(requestFlags.SessionID.meta, func(v *ResourceInput) *commandkit.Supplied[string] { return &v.SessionID }), } } // PromptInputs contains caller variables and native artifact references. type PromptInputs struct { Vars map[string]string Inputs map[string]promptkit.ArtifactRef } // PromptInputFlags exposes variable and named artifact input bindings. type PromptInputFlags[I any] struct { Variable, Text, File commandkit.Flag[I] } // All returns all variable and input bindings. func (f PromptInputFlags[I]) All() []commandkit.Flag[I] { return []commandkit.Flag[I]{f.Variable, f.Text, f.File} } // NewPromptInputFlags binds caller variables and input references to a field of // the caller's input. func NewPromptInputFlags[I any](destination func(*I) *PromptInputs) PromptInputFlags[I] { variable := commandkit.RepeatFlag(requestFlags.Var.meta, []variableAssignment(nil), parseVariableAssignment, func(i *I, v commandkit.Supplied[[]variableAssignment]) { if !v.Set { return } values := make(map[string]string, len(v.Value)) for _, assignment := range v.Value { values[assignment.key] = assignment.value } destination(i).Vars = values }) inputFlag := func(meta commandkit.FlagMeta, makeRef func(string) promptkit.ArtifactRef) commandkit.Flag[I] { return commandkit.OccurrenceFlag(meta, parseInputAssignment, func(i *I, v commandkit.Supplied[inputAssignment]) { input := destination(i) if input.Inputs == nil { input.Inputs = make(map[string]promptkit.ArtifactRef) } input.Inputs[v.Value.name] = makeRef(v.Value.value) }) } return PromptInputFlags[I]{Variable: variable, Text: inputFlag(requestFlags.InputText.meta, promptkit.Inline), File: inputFlag(requestFlags.InputFile.meta, promptkit.File)} } func parseExecutionFloat(value string) (float64, error) { return strconv.ParseFloat(value, 64) }