240 lines
11 KiB
Go
240 lines
11 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 is the exact, case-sensitive configured prompt ID.
|
|
PromptID string
|
|
// ProfileID selects the exact effective profile identity. If empty, the
|
|
// prompt's default profile is used; the profile need not be ready for
|
|
// execution.
|
|
ProfileID string
|
|
// Vars supplies string values for caller-variable lookups and simple
|
|
// conditions. Presence is determined by exact map key, including an empty
|
|
// value. Inspection reports caller keys and presence, not their values.
|
|
// A supplied value may nevertheless appear as a resolved input identifier.
|
|
Vars map[string]string
|
|
// SessionID, when nonblank after the normal session-ID normalization,
|
|
// bypasses the prompt's session template during discovery.
|
|
SessionID string
|
|
}
|
|
|
|
// RequirementKind describes how a selected prompt may depend on an input or
|
|
// caller variable for the supplied context. It is independent of whether a
|
|
// caller-variable value is present.
|
|
type RequirementKind string
|
|
|
|
const (
|
|
// RequirementRequired identifies an input declared required or a dependency
|
|
// reached by a recognized definite access.
|
|
RequirementRequired RequirementKind = "required"
|
|
// RequirementConditional identifies a dependency whose reachability or
|
|
// identity remains unresolved for the supplied context.
|
|
RequirementConditional RequirementKind = "conditional"
|
|
// RequirementOptional identifies a dependency known not to be required,
|
|
// including a nonrequired declaration, optional lookup, or reference used
|
|
// only on a known skipped path.
|
|
RequirementOptional RequirementKind = "optional"
|
|
)
|
|
|
|
// InputRequirement describes a declared or template-referenced prompt input.
|
|
type InputRequirement struct {
|
|
// Name is the exact input identifier.
|
|
Name string
|
|
// Kind is the strongest classification established for this name.
|
|
Kind RequirementKind
|
|
// Declared reports whether the name appears in the effective input
|
|
// declarations; helper-only references have Declared false.
|
|
Declared bool
|
|
// ContentType preserves effective metadata presence: nil means absent or
|
|
// null, while a non-nil pointer may refer to an empty string.
|
|
ContentType *string
|
|
// Description preserves effective metadata presence independently of
|
|
// Explanation, including an explicitly empty string.
|
|
Description *string
|
|
// Explanation is safe human-readable context, such as why a reference is
|
|
// optional because it occurs only on a known skipped path. It is not a
|
|
// machine-readable contract.
|
|
Explanation string
|
|
}
|
|
|
|
// VariableRequirement describes a caller-map key referenced by a prompt.
|
|
type VariableRequirement struct {
|
|
// Name is the exact caller-map key, not a template-local name.
|
|
Name string
|
|
// Kind is the strongest classification established for this key.
|
|
Kind RequirementKind
|
|
// Supplied reports exact-key presence in the request Vars map. An empty
|
|
// string is supplied; the value itself is not returned.
|
|
Supplied bool
|
|
// Explanation is safe human-readable context and is not a machine-readable
|
|
// contract.
|
|
Explanation string
|
|
}
|
|
|
|
// RequirementIssueCode identifies a limitation that can affect discovery.
|
|
type RequirementIssueCode string
|
|
|
|
const (
|
|
// RequirementIssueMissingVariable means an absent caller value prevents
|
|
// resolving a dependency decision or input name.
|
|
RequirementIssueMissingVariable RequirementIssueCode = "missing_variable"
|
|
// RequirementIssueInputContent means unavailable input content affects
|
|
// dependency discovery.
|
|
RequirementIssueInputContent RequirementIssueCode = "input_content"
|
|
// RequirementIssueDynamicInputName means a referenced input name cannot be
|
|
// determined for the supplied context.
|
|
RequirementIssueDynamicInputName RequirementIssueCode = "dynamic_input_name"
|
|
// RequirementIssueUnsupportedConstruct means a dependency-relevant
|
|
// expression or scope is outside the supported discovery analysis.
|
|
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 is the machine-readable limitation category.
|
|
Code RequirementIssueCode
|
|
// InputName identifies an affected input when one is known; empty means no
|
|
// particular input was identified.
|
|
InputName string
|
|
// VariableName identifies an affected caller key when one is known; empty
|
|
// means no particular key was identified.
|
|
VariableName string
|
|
// Description is a safe human-readable summary, not a string to parse.
|
|
Description string
|
|
}
|
|
|
|
// RequirementInspection contains discovered dependencies for one selected
|
|
// prompt context. Input and variable entries are sorted by exact name. Issues
|
|
// are deduplicated by code and associated names, then sorted by that tuple.
|
|
// Their descriptions and entry explanations are safe summaries, not
|
|
// machine-readable details. PromptVersion is caller-owned: nil means no label
|
|
// was declared; a non-nil empty string is explicit.
|
|
//
|
|
// Complete describes the supported analysis's discovery coverage, not whether
|
|
// requirements are supplied, templates will render, artifacts or schemas are
|
|
// valid, or execution is ready. A complete result can still contain required
|
|
// inputs or missing required variables. Valid templates using dependency-
|
|
// relevant constructs outside the deliberately small recognition scope may
|
|
// produce useful partial inventories with Complete false; this is not a
|
|
// template-definition error or an authoring prohibition.
|
|
type RequirementInspection struct {
|
|
// PromptID is the selected exact prompt ID.
|
|
PromptID string
|
|
// PromptVersion is the selected definition's descriptive version label. Nil
|
|
// means no label was declared; a non-nil empty string is an explicit label.
|
|
PromptVersion *string
|
|
// ProfileID is the selected profile identity; it does not indicate
|
|
// execution readiness.
|
|
ProfileID string
|
|
// Inputs contains declared inputs and recognizable template references,
|
|
// including entries known to be optional.
|
|
Inputs []InputRequirement
|
|
// Variables contains recognizable caller-map keys, including keys used
|
|
// only on known skipped paths.
|
|
Variables []VariableRequirement
|
|
// Complete reports discovery coverage, not requirement satisfaction or
|
|
// execution readiness.
|
|
Complete bool
|
|
// Issues contains deduplicated, structured discovery limitations.
|
|
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.
|
|
//
|
|
// Discovery recognizes literal input names, direct caller-map keys, literal-
|
|
// key root-map lookups, simple caller-derived input names, and if/with
|
|
// conditions based on direct values, literals, negation, or two-string
|
|
// equality/inequality. It classifies recognizable references on known skipped
|
|
// paths as optional. Other dependency-relevant expressions or scopes may leave
|
|
// a useful partial result with Complete false. The returned inventories do
|
|
// not reject otherwise valid templates or prohibit their use.
|
|
//
|
|
// Results exclude artifact payloads, prompt contents, and unrelated supplied
|
|
// values. Resolved input identifiers may equal the caller values that supply
|
|
// them; those identifiers are intentionally included in the inventory.
|
|
//
|
|
// 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. When cancellation prevents lookup or analysis, no inspection
|
|
// is returned and the context error remains discoverable with errors.Is. 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, 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: copyRequirementString(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 ©
|
|
}
|