package ckprompt import ( "context" "errors" "fmt" "go.maximumdirect.net/ckconfig" commandkit "go.maximumdirect.net/commandkit" promptkit "go.maximumdirect.net/promptkit" ) // PreparationRequest carries the configuration selection and seed native request // supplied to preparation callbacks and builders. type PreparationRequest struct { // Selection identifies the configuration sources used to load the engine. Selection ckconfig.Selection // Request contains the native seed request populated by command inputs. Request promptkit.RunRequest // Namespaces selects the native engine view; nil preserves the loader view. Namespaces *promptkit.Namespaces } // WithRequestPreparation attaches native prepared-request inspection. The // optional builder may replace the complete seed request before engine loading. // Preparation may read resources and returns rendered application content; it // does not call the model or retain an execution handle. func WithRequestPreparation[I, S any]( suite Suite[I, S], load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error), build func(context.Context, PreparationRequest, I, S) (promptkit.RunRequest, error), ) Suite[I, S] { if load == nil { issues := append([]error(nil), suite.issues...) suite.issues = append(issues, errors.New("PromptKit engine loader is nil")) return suite } suite.PrepareRequest = func(ctx context.Context, request PreparationRequest, input I, state S) (commandkit.Acquired[*promptkit.PreparedRun], error) { if err := promptKitContextError(ctx); err != nil { return commandkit.Acquired[*promptkit.PreparedRun]{}, fmt.Errorf("PromptKit request preparation before building: %w", err) } concrete := request.Request if build != nil { built, err := build(ctx, request, input, state) if err != nil { return commandkit.Acquired[*promptkit.PreparedRun]{}, err } concrete = built } if err := promptKitContextError(ctx); err != nil { return commandkit.Acquired[*promptkit.PreparedRun]{}, fmt.Errorf("PromptKit request preparation after building: %w", err) } engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load) if err != nil { return commandkit.Acquired[*promptkit.PreparedRun]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err } if err := promptKitContextError(ctx); err != nil { return commandkit.Acquired[*promptkit.PreparedRun]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit request preparation before native preparation: %w", err) } engine, err = namespacedEngine(engine, request.Namespaces) if err != nil { messages = messages.withOperationError(err) return commandkit.Acquired[*promptkit.PreparedRun]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err } prepared, err := engine.Prepare(ctx, concrete) messages = messages.withOperationError(err) return commandkit.Acquired[*promptkit.PreparedRun]{Value: prepared, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err } return suite } func newPreparationCommand[I, S any](suite Suite[I, S]) commandkit.Command { spec := newConcreteRequestSpec(suite) spec.Name = "prepared" spec.Summary = "Show a prepared PromptKit request without calling the model." spec.Description = "Prepares the request using resource I/O and reports rendered application content without calling the model." spec.Run = func(invocation commandkit.Invocation, input *concreteRequestInput[I]) commandkit.Outcome { return runRequestPreparation(invocation, suite, input) } return commandkit.NewCommand(spec) } func runRequestPreparation[I, S any](invocation commandkit.Invocation, suite Suite[I, S], input *concreteRequestInput[I]) commandkit.Outcome { if err := promptKitContextError(invocation.Context); err != nil { return definitionFailure("PromptKit request preparation was canceled.", commandkit.Explanation{}, err) } var state S if suite.NewState != nil { state = suite.NewState() } if err := promptKitContextError(invocation.Context); err != nil { return definitionFailure("PromptKit request preparation was canceled.", commandkit.Explanation{}, err) } request := PreparationRequest{Selection: input.request.Selection, Request: input.request.Request, Namespaces: input.request.Namespaces} acquired, err := suite.PrepareRequest(invocation.Context, request, input.selected, state) queuePromptDiagnostics(invocation.Reporter(), acquired.Diagnostics) if err != nil { return definitionFailure(fmt.Sprintf("Unable to prepare the PromptKit request for prompt %q.", input.promptID), acquired.Explanation, err) } if err := promptKitContextError(invocation.Context); err != nil { return definitionFailure(fmt.Sprintf("Preparation of the PromptKit request for prompt %q was canceled.", input.promptID), acquired.Explanation, err) } _ = invocation.Reporter().Report(commandkit.Report{Value: acquired.Value}) return commandkit.Success() }