Files
promptkit/requirements.go

159 lines
5.8 KiB
Go

package promptkit
import (
"context"
"gitea.maximumdirect.net/eric/promptkit/internal/domain"
"gitea.maximumdirect.net/eric/promptkit/internal/usecase"
)
// RequirementInspectionRequest selects a prompt and supplies caller context
// for requirement discovery. It requires no artifact bindings or credentials.
type RequirementInspectionRequest struct {
PromptID string
PromptVersion string
ProfileID string
Vars map[string]string
SessionID string
}
// RequirementKind describes how a selected prompt may depend on an input or
// caller variable for the supplied context.
type RequirementKind string
const (
// RequirementRequired identifies a declaration or definite access.
RequirementRequired RequirementKind = "required"
// RequirementConditional identifies a dependency whose reachability is
// unresolved for the supplied context.
RequirementConditional RequirementKind = "conditional"
// RequirementOptional identifies a dependency known not to be required,
// including a reference used only on a known skipped path.
RequirementOptional RequirementKind = "optional"
)
// InputRequirement describes a declared or template-referenced prompt input.
type InputRequirement struct {
Name string
Kind RequirementKind
Declared bool
ContentType *string
Description *string
Explanation string
}
// VariableRequirement describes a caller-map key referenced by a prompt.
type VariableRequirement struct {
Name string
Kind RequirementKind
Supplied bool
Explanation string
}
// RequirementIssueCode identifies a limitation that can affect discovery.
type RequirementIssueCode string
const (
RequirementIssueMissingVariable RequirementIssueCode = "missing_variable"
RequirementIssueInputContent RequirementIssueCode = "input_content"
RequirementIssueDynamicInputName RequirementIssueCode = "dynamic_input_name"
RequirementIssueUnsupportedConstruct RequirementIssueCode = "unsupported_construct"
)
// RequirementIssue describes one discovery limitation using safe summary
// text. Descriptions are not intended as machine-readable details.
type RequirementIssue struct {
Code RequirementIssueCode
InputName string
VariableName string
Description string
}
// RequirementInspection contains discovered dependencies for one selected
// prompt context. Complete describes discovery, not renderability or readiness.
type RequirementInspection struct {
PromptID string
PromptVersion string
ProfileID string
Inputs []InputRequirement
Variables []VariableRequirement
Complete bool
Issues []RequirementIssue
}
// InspectRequirements discovers inputs and caller variables for one selected
// prompt before its input artifacts exist. It uses exact, case-sensitive
// prompt selection and requires a profile ID either in the request or the
// selected prompt's default. The selected profile establishes identity only;
// it need not be execution-ready. A normalized nonblank SessionID bypasses the
// captured session template. Vars are copied before analysis, and returned
// slices and metadata pointers belong to the caller.
//
// This method does not perform artifact, schema, credential, rendering,
// validation, capacity, observation, provider, or execution work. Complete
// describes dependency discovery, not whether every dependency is supplied or
// the prompt is ready to execute. Discovery limitations return a partial
// inspection and no error; selection and operational failures return no
// inspection. Blank prompt IDs, invalid session IDs, and absent profile
// selection match ErrInvalidRequest; an absent profile selection also matches
// ErrProfileRequired. Missing prompt/profile IDs match ErrPromptNotFound or
// ErrProfileNotFound. Other selection/snapshot failures match ErrPromptLoad or
// ErrProfileLoad. A nil Engine matches ErrInvalidConfig.
func (e *Engine) InspectRequirements(
ctx context.Context,
req RequirementInspectionRequest,
) (*RequirementInspection, error) {
if e == nil || e.internal == nil {
return nil, ErrInvalidConfig
}
inspection, err := e.internal.InspectRequirements(ctx, usecase.RequirementInspectionRequest{
PromptID: req.PromptID, PromptVersion: req.PromptVersion, ProfileID: req.ProfileID,
Vars: copyStringMap(req.Vars), SessionID: req.SessionID,
})
if err != nil {
return nil, mapPublicError(err)
}
return fromDomainRequirementInspection(inspection), nil
}
func fromDomainRequirementInspection(inspection *domain.RequirementInspection) *RequirementInspection {
if inspection == nil {
return nil
}
result := &RequirementInspection{
PromptID: inspection.PromptID, PromptVersion: inspection.PromptVersion,
ProfileID: inspection.ProfileID, Complete: inspection.Complete,
Inputs: make([]InputRequirement, len(inspection.Inputs)),
Variables: make([]VariableRequirement, len(inspection.Variables)),
Issues: make([]RequirementIssue, len(inspection.Issues)),
}
for index, input := range inspection.Inputs {
result.Inputs[index] = InputRequirement{
Name: input.Name, Kind: RequirementKind(input.Kind), Declared: input.Declared,
ContentType: copyRequirementString(input.ContentType), Description: copyRequirementString(input.Description),
Explanation: input.Explanation,
}
}
for index, variable := range inspection.Variables {
result.Variables[index] = VariableRequirement{
Name: variable.Name, Kind: RequirementKind(variable.Kind), Supplied: variable.Supplied,
Explanation: variable.Explanation,
}
}
for index, issue := range inspection.Issues {
result.Issues[index] = RequirementIssue{
Code: RequirementIssueCode(issue.Code), InputName: issue.InputName,
VariableName: issue.VariableName, Description: issue.Description,
}
}
return result
}
func copyRequirementString(value *string) *string {
if value == nil {
return nil
}
copy := *value
return &copy
}