Migrate PromptKit reports to shared rendering
This commit is contained in:
@@ -1,17 +1,14 @@
|
||||
package ckprompt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"go.maximumdirect.net/ckconfig"
|
||||
commandkit "go.maximumdirect.net/commandkit"
|
||||
promptkit "go.maximumdirect.net/promptkit"
|
||||
"go.yaml.in/yaml/v4"
|
||||
)
|
||||
|
||||
type definitionInput[I any] struct {
|
||||
@@ -27,7 +24,6 @@ type catalogInput[I any] struct {
|
||||
allNamespaces commandkit.Supplied[bool]
|
||||
kinds []promptkit.ResourceKind
|
||||
filter promptkit.NamespaceFilter
|
||||
format string
|
||||
captured commandkit.FlagValues[I]
|
||||
selected I
|
||||
}
|
||||
@@ -35,7 +31,6 @@ type catalogInput[I any] struct {
|
||||
type requirementsInput[I any] struct {
|
||||
selection ckconfig.Selection
|
||||
inspection promptkit.RequirementInspectionRequest
|
||||
format string
|
||||
captured commandkit.FlagValues[I]
|
||||
selected I
|
||||
}
|
||||
@@ -138,8 +133,6 @@ func newCatalogListCommand[I, S any](suite Suite[I, S], name string) commandkit.
|
||||
func(input **catalogInput[I], value commandkit.Supplied[string]) { (*input).selection.Config = value }),
|
||||
commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Configuration overlay path.", Completion: commandkit.CompleteFiles()}, (*string)(nil), parseDefinitionString,
|
||||
func(input **catalogInput[I], value commandkit.Supplied[string]) { (*input).selection.Overlay = value }),
|
||||
commandkit.ValueFlag(commandkit.FlagMeta{Name: "format", Usage: "Catalog format: yaml.", Completion: completionChoices("yaml")}, stringDefault("yaml"), parseDefinitionString,
|
||||
func(input **catalogInput[I], value commandkit.Supplied[string]) { (*input).format = value.Value }),
|
||||
)
|
||||
if name == "prompts" || name == "profiles" || name == "backends" {
|
||||
flags = append(flags,
|
||||
@@ -169,9 +162,6 @@ func newCatalogListCommand[I, S any](suite Suite[I, S], name string) commandkit.
|
||||
if input == nil {
|
||||
return errors.New("PromptKit catalog input must not be nil")
|
||||
}
|
||||
if err := checkCatalogFormat(input.format); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := prepareCatalogFilter(input, name); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -185,21 +175,21 @@ func newCatalogListCommand[I, S any](suite Suite[I, S], name string) commandkit.
|
||||
Run: func(invocation commandkit.Invocation, input *catalogInput[I]) commandkit.Outcome {
|
||||
switch name {
|
||||
case "prompts":
|
||||
return runCatalogList(invocation, suite, input, name, suite.ListPrompts, encodePromptCatalog)
|
||||
return runCatalogList(invocation, suite, input, name, suite.ListPrompts, promptCatalogReport)
|
||||
case "profiles":
|
||||
return runCatalogList(invocation, suite, input, name, suite.ListProfiles, encodeProfileCatalog)
|
||||
return runCatalogList(invocation, suite, input, name, suite.ListProfiles, stringCatalogReport)
|
||||
case "backends":
|
||||
return runCatalogList(invocation, suite, input, name, suite.ListBackends, encodeProfileCatalog)
|
||||
return runCatalogList(invocation, suite, input, name, suite.ListBackends, stringCatalogReport)
|
||||
case "namespaces":
|
||||
list := func(ctx context.Context, request CatalogRequest, value I, state S) (commandkit.Acquired[[]string], error) {
|
||||
return suite.ListNamespaces(ctx, NamespaceRequest{Selection: request.Selection, Kinds: input.kinds}, value, state)
|
||||
}
|
||||
return runCatalogList(invocation, suite, input, name, list, encodeProfileCatalog)
|
||||
return runCatalogList(invocation, suite, input, name, list, stringCatalogReport)
|
||||
case "collections":
|
||||
list := func(ctx context.Context, request CatalogRequest, value I, state S) (commandkit.Acquired[[]promptkit.CollectionInfo], error) {
|
||||
return suite.ListCollections(ctx, request.Selection, value, state)
|
||||
}
|
||||
return runCatalogList(invocation, suite, input, name, list, encodeCollectionCatalog)
|
||||
return runCatalogList(invocation, suite, input, name, list, collectionCatalogReport)
|
||||
default:
|
||||
return definitionFailure("Unable to list the selected PromptKit catalog.", commandkit.Explanation{}, fmt.Errorf("unknown PromptKit catalog %q", name))
|
||||
}
|
||||
@@ -230,20 +220,6 @@ func prepareCatalogFilter[I any](input *catalogInput[I], name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkCatalogFormat(format string) error {
|
||||
if format != "yaml" {
|
||||
return fmt.Errorf("unsupported catalog format %q", format)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkRequirementsFormat(format string) error {
|
||||
if format != "yaml" {
|
||||
return fmt.Errorf("unsupported requirements format %q; expected yaml", format)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func prepareCatalogInput[I, S any](suite Suite[I, S], input *catalogInput[I]) (I, error) {
|
||||
return prepareSuiteInput(suite, input.selection, input.captured)
|
||||
}
|
||||
@@ -313,8 +289,6 @@ func newRequirementsCommand[I, S any](suite Suite[I, S]) commandkit.Command {
|
||||
func(input **requirementsInput[I], value commandkit.Supplied[string]) {
|
||||
(*input).inspection.SessionID = value.Value
|
||||
}),
|
||||
commandkit.ValueFlag(commandkit.FlagMeta{Name: "format", Usage: "Report format: yaml.", Completion: completionChoices("yaml")}, stringDefault("yaml"), parseDefinitionString,
|
||||
func(input **requirementsInput[I], value commandkit.Supplied[string]) { (*input).format = value.Value }),
|
||||
commandkit.RepeatFlag(commandkit.FlagMeta{Name: "var", Usage: "Caller variable assignment KEY=VALUE."}, []variableAssignment(nil), parseVariableAssignment,
|
||||
func(input **requirementsInput[I], value commandkit.Supplied[[]variableAssignment]) {
|
||||
variables := make(map[string]string, len(value.Value))
|
||||
@@ -339,9 +313,6 @@ func newRequirementsCommand[I, S any](suite Suite[I, S]) commandkit.Command {
|
||||
}),
|
||||
},
|
||||
CheckInput: func(input *requirementsInput[I]) error {
|
||||
if err := checkRequirementsFormat(input.format); err != nil {
|
||||
return err
|
||||
}
|
||||
selected, err := prepareSuiteInput(suite, input.selection, input.captured)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -382,7 +353,7 @@ func runCatalogList[T any, I, S any](
|
||||
input *catalogInput[I],
|
||||
catalog string,
|
||||
list func(context.Context, CatalogRequest, I, S) (commandkit.Acquired[T], error),
|
||||
encode func(T) ([]byte, error),
|
||||
project func(T) any,
|
||||
) commandkit.Outcome {
|
||||
if invocation.Context != nil {
|
||||
if err := invocation.Context.Err(); err != nil {
|
||||
@@ -409,9 +380,8 @@ func runCatalogList[T any, I, S any](
|
||||
return definitionFailure("PromptKit catalog listing was canceled.", acquired.Explanation, err)
|
||||
}
|
||||
}
|
||||
return renderPreparedPromptReport(invocation, acquired.Explanation, func() ([]byte, error) {
|
||||
return encode(acquired.Value)
|
||||
})
|
||||
_ = invocation.Reporter().Report(commandkit.Report{Value: project(acquired.Value)})
|
||||
return commandkit.Success()
|
||||
}
|
||||
|
||||
func runDefinitionInspection[I, S any](invocation commandkit.Invocation, suite Suite[I, S], input *definitionInput[I], kind string) commandkit.Outcome {
|
||||
@@ -467,35 +437,35 @@ func runDefinitionInspection[I, S any](invocation commandkit.Invocation, suite S
|
||||
}
|
||||
|
||||
type requirementsInspectionReport struct {
|
||||
PromptID string `yaml:"prompt_id"`
|
||||
ProfileID string `yaml:"profile_id"`
|
||||
Complete bool `yaml:"complete"`
|
||||
Inputs []requirementInputReport `yaml:"inputs"`
|
||||
Variables []requirementVariableReport `yaml:"variables"`
|
||||
Issues []requirementIssueReport `yaml:"issues"`
|
||||
PromptID string `json:"prompt_id"`
|
||||
ProfileID string `json:"profile_id"`
|
||||
Complete bool `json:"complete"`
|
||||
Inputs []requirementInputReport `json:"inputs"`
|
||||
Variables []requirementVariableReport `json:"variables"`
|
||||
Issues []requirementIssueReport `json:"issues"`
|
||||
}
|
||||
|
||||
type requirementInputReport struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Declared bool `yaml:"declared"`
|
||||
ContentType *string `yaml:"content_type"`
|
||||
Description *string `yaml:"description"`
|
||||
Explanation string `yaml:"explanation"`
|
||||
Name string `json:"name"`
|
||||
Kind string `json:"kind"`
|
||||
Declared bool `json:"declared"`
|
||||
ContentType *string `json:"content_type"`
|
||||
Description *string `json:"description"`
|
||||
Explanation string `json:"explanation"`
|
||||
}
|
||||
|
||||
type requirementVariableReport struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Supplied bool `yaml:"supplied"`
|
||||
Explanation string `yaml:"explanation"`
|
||||
Name string `json:"name"`
|
||||
Kind string `json:"kind"`
|
||||
Supplied bool `json:"supplied"`
|
||||
Explanation string `json:"explanation"`
|
||||
}
|
||||
|
||||
type requirementIssueReport struct {
|
||||
Code string `yaml:"code"`
|
||||
InputName string `yaml:"input_name"`
|
||||
VariableName string `yaml:"variable_name"`
|
||||
Description string `yaml:"description"`
|
||||
Code string `json:"code"`
|
||||
InputName string `json:"input_name"`
|
||||
VariableName string `json:"variable_name"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
func requirementsReportFromInspection(inspection *promptkit.RequirementInspection) requirementsInspectionReport {
|
||||
@@ -566,76 +536,40 @@ func runRequirementsInspection[I, S any](invocation commandkit.Invocation, suite
|
||||
return definitionFailure(fmt.Sprintf("Requirements inspection for PromptKit prompt %q was canceled.", input.inspection.PromptID), acquired.Explanation, err)
|
||||
}
|
||||
}
|
||||
return renderPreparedPromptReport(invocation, acquired.Explanation, func() ([]byte, error) {
|
||||
return encodePromptYAMLBytes(requirementsReportFromInspection(acquired.Value))
|
||||
})
|
||||
}
|
||||
|
||||
// These reports were already prepared before delivery. Keep that boundary
|
||||
// inside managed rendering, with cancellation owned by the command outcome.
|
||||
func renderPreparedPromptReport(invocation commandkit.Invocation, explanation commandkit.Explanation, prepare func() ([]byte, error)) commandkit.Outcome {
|
||||
var canceled error
|
||||
_ = invocation.Reporter().RenderOutput(func(writer io.Writer) error {
|
||||
output, err := prepare()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if canceled = promptKitContextError(invocation.Context); canceled != nil {
|
||||
return nil
|
||||
}
|
||||
_, err = writer.Write(output)
|
||||
return err
|
||||
})
|
||||
if canceled != nil {
|
||||
return definitionFailure("PromptKit report preparation was canceled.", explanation, canceled)
|
||||
}
|
||||
_ = invocation.Reporter().Report(commandkit.Report{Value: requirementsReportFromInspection(acquired.Value)})
|
||||
return commandkit.Success()
|
||||
}
|
||||
|
||||
func encodePromptYAML(writer io.Writer, value any) error {
|
||||
encoder := yaml.NewEncoder(writer)
|
||||
encoder.SetIndent(2)
|
||||
return encoder.Encode(value)
|
||||
}
|
||||
|
||||
type promptCatalogEntry struct {
|
||||
ID string `yaml:"id"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
func encodePromptCatalog(prompts []promptkit.PromptIdentity) ([]byte, error) {
|
||||
func promptCatalogReport(prompts []promptkit.PromptIdentity) any {
|
||||
entries := make([]promptCatalogEntry, len(prompts))
|
||||
for index, prompt := range prompts {
|
||||
entries[index] = promptCatalogEntry{ID: prompt.ID}
|
||||
}
|
||||
return encodePromptYAMLBytes(entries)
|
||||
return entries
|
||||
}
|
||||
|
||||
func encodeProfileCatalog(profiles []string) ([]byte, error) {
|
||||
if profiles == nil {
|
||||
profiles = []string{}
|
||||
func stringCatalogReport(values []string) any {
|
||||
if values == nil {
|
||||
return []string{}
|
||||
}
|
||||
return encodePromptYAMLBytes(profiles)
|
||||
return values
|
||||
}
|
||||
|
||||
type collectionCatalogEntry struct {
|
||||
Namespace string `yaml:"namespace"`
|
||||
Version *string `yaml:"version,omitempty"`
|
||||
Namespace string `json:"namespace"`
|
||||
Version *string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
func encodeCollectionCatalog(collections []promptkit.CollectionInfo) ([]byte, error) {
|
||||
func collectionCatalogReport(collections []promptkit.CollectionInfo) any {
|
||||
entries := make([]collectionCatalogEntry, len(collections))
|
||||
for index, collection := range collections {
|
||||
entries[index] = collectionCatalogEntry{Namespace: collection.Namespace, Version: collection.ReleaseVersion}
|
||||
}
|
||||
return encodePromptYAMLBytes(entries)
|
||||
}
|
||||
|
||||
func encodePromptYAMLBytes(value any) ([]byte, error) {
|
||||
var output bytes.Buffer
|
||||
if err := encodePromptYAML(&output, value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return output.Bytes(), nil
|
||||
return entries
|
||||
}
|
||||
|
||||
func queuePromptDiagnostics(reporter *commandkit.Reporter, diagnostics []commandkit.Diagnostic) {
|
||||
|
||||
Reference in New Issue
Block a user