Remove prompt version selectors

This commit is contained in:
2026-10-17 05:47:14 +00:00
parent d40a010538
commit 8cbb7c0daf
36 changed files with 182 additions and 253 deletions

View File

@@ -114,7 +114,7 @@ func newAssembledWorkflowFixture(b *testing.B, name string) assembledWorkflowFix
b.Fatal(err) b.Fatal(err)
} }
request := promptkit.RunRequest{ request := promptkit.RunRequest{
PromptID: "workflow", PromptVersion: "v1", Vars: map[string]string{"name": "Ada"}, PromptID: "workflow", Vars: map[string]string{"name": "Ada"},
} }
return assembledWorkflowFixture{ return assembledWorkflowFixture{
engine: engine, request: request, expectedMessage: expectedMessage, response: response, engine: engine, request: request, expectedMessage: expectedMessage, response: response,

View File

@@ -25,7 +25,6 @@ func toDomainRunRequest(req RunRequest) (domain.RunRequest, error) {
} }
return domain.RunRequest{ return domain.RunRequest{
PromptID: req.PromptID, PromptID: req.PromptID,
PromptVersion: req.PromptVersion,
ProfileID: req.ProfileID, ProfileID: req.ProfileID,
SessionID: req.SessionID, SessionID: req.SessionID,
APIKey: req.APIKey, APIKey: req.APIKey,

View File

@@ -141,7 +141,7 @@ if err != nil {
} }
if len(prompts) > 0 { if len(prompts) > 0 {
selected := prompts[0] selected := prompts[0]
inspection, err := engine.InspectPrompt(ctx, selected.ID, selected.Version) inspection, err := engine.InspectPrompt(ctx, selected.ID)
if err != nil { if err != nil {
return err return err
} }
@@ -286,7 +286,7 @@ declared inputs and output workflow without creating placeholder inputs or
resolving a profile: resolving a profile:
```go ```go
inspection, err := engine.InspectPrompt(ctx, "meeting.summary", "") inspection, err := engine.InspectPrompt(ctx, "meeting.summary")
if err != nil { if err != nil {
return err return err
} }

View File

@@ -46,8 +46,8 @@ admission, or model collaborators. The root
exact contract. exact contract.
`InspectRequirements` uses the same metadata selection to resolve the `InspectRequirements` uses the same metadata selection to resolve the
request-profile/default identity, then obtains the exact captured requirement request-profile/default identity, then obtains the captured requirement
snapshot by the selected prompt ID and version. The analyzer receives only snapshot by the selected prompt ID. The analyzer receives only
those identity fields rather than an execution definition. It analyzes the those identity fields rather than an execution definition. It analyzes the
snapshot with the supplied caller variables and normalized direct-session snapshot with the supplied caller variables and normalized direct-session
bypass; it does not resolve target readiness or enter the preparation bypass; it does not resolve target readiness or enter the preparation

View File

@@ -46,7 +46,7 @@ real loader and final-value handoff.
## Prompt Definitions ## Prompt Definitions
The configuration adapter indexes each prompt by exact ID and version, The configuration adapter indexes each prompt by exact ID,
materializes file-backed message content using the exact effective field's materializes file-backed message content using the exact effective field's
origin and resource context, and checks static message/session template syntax origin and resource context, and checks static message/session template syntax
at construction. It stores captured content, compiled templates, declared at construction. It stores captured content, compiled templates, declared
@@ -65,7 +65,7 @@ configuration would replace origins of unchanged content-file fields.
The runner selects full definitions from this index for preparation and uses The runner selects full definitions from this index for preparation and uses
captured prompt metadata for `InspectPrompt` and `InspectRequirements`; the captured prompt metadata for `InspectPrompt` and `InspectRequirements`; the
internal requirement snapshot lookup selects by exact prompt ID and version. internal requirement snapshot lookup uses the selected prompt ID alone.
These paths do not rescan configuration or reopen message files. `InspectPrompt` These paths do not rescan configuration or reopen message files. `InspectPrompt`
returns declared metadata without rendering or resolving profile, input, or returns declared metadata without rendering or resolving profile, input, or
schema resources, and its public shape and prompt hashes do not include the schema resources, and its public shape and prompt hashes do not include the
@@ -73,8 +73,8 @@ captured presence state. Runtime rendering still checks request variables and
input helpers. The [prompt adapter tests](../../internal/configuration/prompts_test.go) input helpers. The [prompt adapter tests](../../internal/configuration/prompts_test.go)
cover resource origins, context containment, capture, and selection. cover resource origins, context containment, capture, and selection.
The configuration adapter shares exact ID/version selection across its The configuration adapter shares ID-only selection across its internal prompt
internal prompt lookups. The execution snapshot lookup returns an owned prompt lookups. The execution snapshot lookup returns an owned prompt
definition and cached hash; metadata inspection returns owned input metadata definition and cached hash; metadata inspection returns owned input metadata
without copying execution templates. Profile selection likewise has an without copying execution templates. Profile selection likewise has an
identity-only lookup for consumers that do not need the execution target, identity-only lookup for consumers that do not need the execution target,

View File

@@ -416,6 +416,14 @@ Validation: `GOWORK=off go test ./...` and focused vet of modified packages.
Search declarations/call sites for remaining selection/ambiguity logic; descriptive Search declarations/call sites for remaining selection/ambiguity logic; descriptive
label references are valid and should not be blanket-deleted. label references are valid and should not be blanket-deleted.
Completion: request-side version selectors and version parameters have been
removed from prompt lookup, inspection, requirement analysis, and smoke-test
selection. Generated structured-schema names now derive from the prompt ID;
result-side descriptive label fields remain. Updated the consumer guide and
GoDoc to reflect ID-only selection. Validation passed: `GOWORK=off go test
./...`, focused `GOWORK=off go vet` for modified packages,
`GOWORK=off python3 scripts/check-examples.py`, and `git diff --check`.
## Stage 6: Carry Optional Prompt Labels Through Results ## Stage 6: Carry Optional Prompt Labels Through Results
Scope: presence-aware metadata across internal values and the facade. Scope: presence-aware metadata across internal values and the facade.

View File

@@ -24,9 +24,8 @@ var (
// ErrInvalidRequest identifies a request whose required values, overrides, // ErrInvalidRequest identifies a request whose required values, overrides,
// credentials, or effective settings are invalid. // credentials, or effective settings are invalid.
ErrInvalidRequest = errors.New("invalid run request") ErrInvalidRequest = errors.New("invalid run request")
// ErrPromptNotFound identifies a requested prompt ID or version that is not // ErrPromptNotFound identifies a requested prompt ID that is not present in
// present in the selected prompt source. It does not also match // the captured configuration. It does not also match ErrPromptLoad.
// ErrPromptLoad.
ErrPromptNotFound = errors.New("prompt not found") ErrPromptNotFound = errors.New("prompt not found")
// ErrProfileNotFound identifies a selected profile ID that is absent from // ErrProfileNotFound identifies a selected profile ID that is absent from
// every configured profile source. It does not also match ErrProfileLoad. // every configured profile source. It does not also match ErrProfileLoad.
@@ -242,14 +241,11 @@ func NewEngine(cfg Config, opts ...Option) (*Engine, error) {
return &Engine{internal: assembled}, nil return &Engine{internal: assembled}, nil
} }
// InspectPrompt resolves one explicit prompt definition without selecting a // InspectPrompt resolves one prompt ID without selecting a profile or starting
// profile or starting execution work. // execution work.
// //
// InspectPrompt requires a nonblank promptID. It passes nonblank promptID and // InspectPrompt requires a nonblank promptID and performs an exact,
// promptVersion values unchanged to the engine's ordinary, case-sensitive // case-sensitive lookup in the captured configuration snapshot.
// prompt selection. An empty version succeeds only when that ID has one
// version; a nonempty version selects one exact ID/version pair from the
// captured configuration snapshot.
// //
// A successful result proves that the selected definition and any referenced // A successful result proves that the selected definition and any referenced
// message content files were structurally loaded. Inputs are returned in // message content files were structurally loaded. Inputs are returned in
@@ -268,21 +264,19 @@ func NewEngine(cfg Config, opts ...Option) (*Engine, error) {
// or content files; construct another Engine to observe source changes. // or content files; construct another Engine to observe source changes.
// //
// A nil Engine returns an error matching ErrInvalidConfig. A blank prompt ID // A nil Engine returns an error matching ErrInvalidConfig. A blank prompt ID
// matches ErrInvalidRequest. An absent exact ID or version matches // matches ErrInvalidRequest. An absent exact ID matches ErrPromptNotFound and
// ErrPromptNotFound and not ErrPromptLoad. An ambiguous omitted version matches // not ErrPromptLoad. Invalid definitions and resource failures reject
// ErrPromptLoad. Invalid definitions and resource failures reject construction // construction instead. Cancellation during lookup preserves the context
// instead. Cancellation during lookup preserves the context error. InspectPrompt // error. InspectPrompt returns no partial result on error.
// returns no partial result on error.
func (e *Engine) InspectPrompt( func (e *Engine) InspectPrompt(
ctx context.Context, ctx context.Context,
promptID string, promptID string,
promptVersion string,
) (*PromptInspection, error) { ) (*PromptInspection, error) {
if e == nil || e.internal == nil { if e == nil || e.internal == nil {
return nil, fmt.Errorf("%w: engine is nil", ErrInvalidConfig) return nil, fmt.Errorf("%w: engine is nil", ErrInvalidConfig)
} }
inspection, err := e.internal.InspectPrompt(ctx, promptID, promptVersion) inspection, err := e.internal.InspectPrompt(ctx, promptID)
if err != nil { if err != nil {
return nil, mapPublicError(err) return nil, mapPublicError(err)
} }

View File

@@ -18,9 +18,8 @@ func (r RunRequest) GoString() string {
func (r RunRequest) redactedString() string { func (r RunRequest) redactedString() string {
return fmt.Sprintf( return fmt.Sprintf(
"promptkit.RunRequest{PromptID:%q PromptVersion:%q ProfileID:%q APIKeySet:%t Inputs:%d Vars:%d ExecutionSet:%t ValidationSet:%t AppendedMessages:%d}", "promptkit.RunRequest{PromptID:%q ProfileID:%q APIKeySet:%t Inputs:%d Vars:%d ExecutionSet:%t ValidationSet:%t AppendedMessages:%d}",
r.PromptID, r.PromptID,
r.PromptVersion,
r.ProfileID, r.ProfileID,
r.APIKey != "", r.APIKey != "",
len(r.Inputs), len(r.Inputs),

View File

@@ -227,7 +227,7 @@ func BenchmarkInspectionSelection(b *testing.B) {
b.Fatal(err) b.Fatal(err)
} }
ctx := context.Background() ctx := context.Background()
definition, hash, err := result.GetPromptSnapshot(ctx, "inspection", "1") definition, hash, err := result.GetPromptSnapshot(ctx, "inspection")
if err != nil || definition.ID != "inspection" || definition.Version != "1" || hash == "" || if err != nil || definition.ID != "inspection" || definition.Version != "1" || hash == "" ||
len(definition.Inputs) != 1 || definition.Inputs[0].Name != "transcript" || !definition.Inputs[0].Required { len(definition.Inputs) != 1 || definition.Inputs[0].Name != "transcript" || !definition.Inputs[0].Required {
b.Fatalf("prompt lookup fixture is invalid: definition=%+v hash=%q err=%v", definition, hash, err) b.Fatalf("prompt lookup fixture is invalid: definition=%+v hash=%q err=%v", definition, hash, err)
@@ -237,7 +237,7 @@ func BenchmarkInspectionSelection(b *testing.B) {
len(profile.ResolvedTarget.ExtraParams["provider_options"].(map[string]any)) != scenario.entries { len(profile.ResolvedTarget.ExtraParams["provider_options"].(map[string]any)) != scenario.entries {
b.Fatalf("profile lookup fixture is invalid: profile=%+v err=%v", profile, err) b.Fatalf("profile lookup fixture is invalid: profile=%+v err=%v", profile, err)
} }
inspection, err := result.GetPromptInspection(ctx, "inspection", "1") inspection, err := result.GetPromptInspection(ctx, "inspection")
if err != nil || inspection.PromptID != "inspection" || inspection.PromptVersion != "1" || if err != nil || inspection.PromptID != "inspection" || inspection.PromptVersion != "1" ||
len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "transcript" { len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "transcript" {
b.Fatalf("prompt inspection fixture is invalid: inspection=%+v err=%v", inspection, err) b.Fatalf("prompt inspection fixture is invalid: inspection=%+v err=%v", inspection, err)
@@ -250,7 +250,7 @@ func BenchmarkInspectionSelection(b *testing.B) {
b.Run("prompt/"+scenario.name, func(b *testing.B) { b.Run("prompt/"+scenario.name, func(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for b.Loop() { for b.Loop() {
definition, hash, err := result.GetPromptSnapshot(ctx, "inspection", "1") definition, hash, err := result.GetPromptSnapshot(ctx, "inspection")
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
@@ -262,7 +262,7 @@ func BenchmarkInspectionSelection(b *testing.B) {
b.Run("prompt_inspection/"+scenario.name, func(b *testing.B) { b.Run("prompt_inspection/"+scenario.name, func(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for b.Loop() { for b.Loop() {
inspection, err := result.GetPromptInspection(ctx, "inspection", "1") inspection, err := result.GetPromptInspection(ctx, "inspection")
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }

View File

@@ -286,7 +286,7 @@ func (state *loaded) readContent(fieldPath configkit.Path, name string, lookupOr
return string(content), nil return string(content), nil
} }
func (state *loaded) lookupPromptEntry(id, version string) (*promptEntry, error) { func (state *loaded) lookupPromptEntry(id string) (*promptEntry, error) {
if state == nil || strings.TrimSpace(id) == "" { if state == nil || strings.TrimSpace(id) == "" {
return nil, errPromptNotFound return nil, errPromptNotFound
} }
@@ -322,7 +322,7 @@ func promptLookupError(err error) error {
} }
func (state *loaded) prompt(id string) (*domain.PromptDefinition, domain.PromptInspection, error) { func (state *loaded) prompt(id string) (*domain.PromptDefinition, domain.PromptInspection, error) {
entry, err := state.lookupPromptEntry(id, "") entry, err := state.lookupPromptEntry(id)
if err != nil { if err != nil {
return nil, domain.PromptInspection{}, err return nil, domain.PromptInspection{}, err
} }
@@ -330,13 +330,12 @@ func (state *loaded) prompt(id string) (*domain.PromptDefinition, domain.PromptI
} }
// GetPromptSnapshot returns an independently owned captured definition and its // GetPromptSnapshot returns an independently owned captured definition and its
// construction-time hash. The version argument is retained temporarily at the // construction-time hash.
// adapter boundary and does not participate in lookup. func (state *loaded) GetPromptSnapshot(ctx context.Context, id string) (*domain.PromptDefinition, string, error) {
func (state *loaded) GetPromptSnapshot(ctx context.Context, id, version string) (*domain.PromptDefinition, string, error) {
if err := ctx.Err(); err != nil { if err := ctx.Err(); err != nil {
return nil, "", err return nil, "", err
} }
entry, err := state.lookupPromptEntry(id, version) entry, err := state.lookupPromptEntry(id)
if err != nil { if err != nil {
return nil, "", promptLookupError(err) return nil, "", promptLookupError(err)
} }
@@ -345,11 +344,11 @@ func (state *loaded) GetPromptSnapshot(ctx context.Context, id, version string)
// GetPromptInspection returns an independently owned inspection of one // GetPromptInspection returns an independently owned inspection of one
// captured prompt without copying its execution templates. // captured prompt without copying its execution templates.
func (state *loaded) GetPromptInspection(ctx context.Context, id, version string) (*domain.PromptInspection, error) { func (state *loaded) GetPromptInspection(ctx context.Context, id string) (*domain.PromptInspection, error) {
if err := ctx.Err(); err != nil { if err := ctx.Err(); err != nil {
return nil, err return nil, err
} }
entry, err := state.lookupPromptEntry(id, version) entry, err := state.lookupPromptEntry(id)
if err != nil { if err != nil {
return nil, promptLookupError(err) return nil, promptLookupError(err)
} }
@@ -357,13 +356,13 @@ func (state *loaded) GetPromptInspection(ctx context.Context, id, version string
} }
// GetPromptDefinition implements the runner's prompt lookup boundary. // GetPromptDefinition implements the runner's prompt lookup boundary.
func (state *loaded) GetPromptDefinition(ctx context.Context, id, version string) (*domain.PromptDefinition, error) { func (state *loaded) GetPromptDefinition(ctx context.Context, id string) (*domain.PromptDefinition, error) {
definition, _, err := state.GetPromptSnapshot(ctx, id, version) definition, _, err := state.GetPromptSnapshot(ctx, id)
return definition, err return definition, err
} }
// GetCompiledTemplates returns construction-time parses for one prompt ID. // GetCompiledTemplates returns construction-time parses for one prompt ID.
func (state *loaded) GetCompiledTemplates(id, version string) (prompt.CompiledTemplates, bool) { func (state *loaded) GetCompiledTemplates(id string) (prompt.CompiledTemplates, bool) {
if state == nil { if state == nil {
return prompt.CompiledTemplates{}, false return prompt.CompiledTemplates{}, false
} }
@@ -377,7 +376,7 @@ func (state *loaded) GetCompiledTemplates(id, version string) (prompt.CompiledTe
// GetRequirementSnapshot returns captured templates and input metadata for // GetRequirementSnapshot returns captured templates and input metadata for
// one prompt ID. Returned metadata and slices are independently // one prompt ID. Returned metadata and slices are independently
// owned; the compiled templates are shared immutable construction-time parses. // owned; the compiled templates are shared immutable construction-time parses.
func (state *loaded) GetRequirementSnapshot(id, version string) (prompt.RequirementSnapshot, bool) { func (state *loaded) GetRequirementSnapshot(id string) (prompt.RequirementSnapshot, bool) {
if state == nil { if state == nil {
return prompt.RequirementSnapshot{}, false return prompt.RequirementSnapshot{}, false
} }

View File

@@ -161,11 +161,11 @@ func TestPromptInspectionLookupMatchesSnapshotSelectionAndOwnsInputs(t *testing.
} }
{ {
inspection, err := state.GetPromptInspection(context.Background(), "selected", "") inspection, err := state.GetPromptInspection(context.Background(), "selected")
if err != nil { if err != nil {
t.Fatalf("inspect prompt ID: %v", err) t.Fatalf("inspect prompt ID: %v", err)
} }
definition, hash, err := state.GetPromptSnapshot(context.Background(), "selected", "") definition, hash, err := state.GetPromptSnapshot(context.Background(), "selected")
if err != nil { if err != nil {
t.Fatalf("snapshot prompt ID: %v", err) t.Fatalf("snapshot prompt ID: %v", err)
} }
@@ -180,30 +180,30 @@ func TestPromptInspectionLookupMatchesSnapshotSelectionAndOwnsInputs(t *testing.
} }
} }
owned, err := state.GetPromptInspection(context.Background(), "selected", "") owned, err := state.GetPromptInspection(context.Background(), "selected")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
owned.Inputs[0].Name = "caller mutation" owned.Inputs[0].Name = "caller mutation"
owned.Inputs[0].Description = "caller mutation" owned.Inputs[0].Description = "caller mutation"
again, err := state.GetPromptInspection(context.Background(), "selected", "") again, err := state.GetPromptInspection(context.Background(), "selected")
if err != nil || again.Inputs[0].Name != "document" || again.Inputs[0].Description != "source document" { if err != nil || again.Inputs[0].Name != "document" || again.Inputs[0].Description != "source document" {
t.Fatalf("inspection mutation escaped into captured state: inspection=%+v err=%v", again, err) t.Fatalf("inspection mutation escaped into captured state: inspection=%+v err=%v", again, err)
} }
for _, id := range []string{"missing", " "} { for _, id := range []string{"missing", " "} {
inspection, err := state.GetPromptInspection(context.Background(), id, "") inspection, err := state.GetPromptInspection(context.Background(), id)
if inspection != nil || !errors.Is(err, promptdef.ErrPromptDefinitionNotFound) { if inspection != nil || !errors.Is(err, promptdef.ErrPromptDefinitionNotFound) {
t.Errorf("inspection lookup %q=(%+v, %v), want not found", id, inspection, err) t.Errorf("inspection lookup %q=(%+v, %v), want not found", id, inspection, err)
} }
definition, _, err := state.GetPromptSnapshot(context.Background(), id, "") definition, _, err := state.GetPromptSnapshot(context.Background(), id)
if definition != nil || !errors.Is(err, promptdef.ErrPromptDefinitionNotFound) { if definition != nil || !errors.Is(err, promptdef.ErrPromptDefinitionNotFound) {
t.Errorf("snapshot lookup %q=(%+v, %v), want not found", id, definition, err) t.Errorf("snapshot lookup %q=(%+v, %v), want not found", id, definition, err)
} }
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
cancel() cancel()
if _, err := state.GetPromptInspection(ctx, "selected", ""); !errors.Is(err, context.Canceled) { if _, err := state.GetPromptInspection(ctx, "selected"); !errors.Is(err, context.Canceled) {
t.Errorf("canceled inspection selection error=%v", err) t.Errorf("canceled inspection selection error=%v", err)
} }
} }
@@ -269,7 +269,7 @@ func TestRequirementSnapshotCapturesMetadataPresenceAndNormalizedValues(t *testi
t.Fatal(err) t.Fatal(err)
} }
snapshot, ok := state.GetRequirementSnapshot("example", "") snapshot, ok := state.GetRequirementSnapshot("example")
if !ok || len(snapshot.Inputs) != 4 || len(snapshot.Templates.Messages) != 1 || snapshot.Templates.Session == nil { if !ok || len(snapshot.Inputs) != 4 || len(snapshot.Templates.Messages) != 1 || snapshot.Templates.Session == nil {
t.Fatalf("requirement snapshot was not captured: %+v, %t", snapshot, ok) t.Fatalf("requirement snapshot was not captured: %+v, %t", snapshot, ok)
} }
@@ -299,7 +299,7 @@ func TestRequirementSnapshotCapturesMetadataPresenceAndNormalizedValues(t *testi
*snapshot.Inputs[2].ContentType = "caller mutation" *snapshot.Inputs[2].ContentType = "caller mutation"
snapshot.Inputs[3].Name = "caller mutation" snapshot.Inputs[3].Name = "caller mutation"
snapshot.Templates.Messages[0] = nil snapshot.Templates.Messages[0] = nil
again, ok := state.GetRequirementSnapshot("example", "") again, ok := state.GetRequirementSnapshot("example")
if !ok || *again.Inputs[2].ContentType != "" || again.Inputs[3].Name != "normalized" || again.Templates.Messages[0] == nil { if !ok || *again.Inputs[2].ContentType != "" || again.Inputs[3].Name != "normalized" || again.Templates.Messages[0] == nil {
t.Fatalf("snapshot mutation escaped into captured state: %+v, %t", again, ok) t.Fatalf("snapshot mutation escaped into captured state: %+v, %t", again, ok)
} }

View File

@@ -61,7 +61,6 @@ type CacheControl struct {
// RunRequest represents a request to generate a single artifact. // RunRequest represents a request to generate a single artifact.
type RunRequest struct { type RunRequest struct {
PromptID string PromptID string
PromptVersion string
ProfileID string ProfileID string
SessionID string SessionID string
APIKey string `json:"-" yaml:"-"` APIKey string `json:"-" yaml:"-"`
@@ -135,7 +134,7 @@ type Artifact struct {
Hash string Hash string
} }
// PromptIdentity identifies one configured prompt version. // PromptIdentity identifies one configured prompt and its descriptive version label.
type PromptIdentity struct { type PromptIdentity struct {
ID string ID string
Version string Version string

View File

@@ -39,7 +39,6 @@ type SmokeTestRequest struct {
Kind SmokeTestKind Kind SmokeTestKind
BackendID string BackendID string
PromptID string PromptID string
PromptVersion string
ProfileID string ProfileID string
Execution *ExecutionTargetOverride Execution *ExecutionTargetOverride
APIKey string `json:"-" yaml:"-"` APIKey string `json:"-" yaml:"-"`

View File

@@ -187,8 +187,8 @@ func fileSource(name string) (fs.FS, string, string, error) {
return os.DirFS(directory), filepath.ToSlash(base), directory, nil return os.DirFS(directory), filepath.ToSlash(base), directory, nil
} }
func (e *Engine) InspectPrompt(ctx context.Context, id, version string) (*domain.PromptInspection, error) { func (e *Engine) InspectPrompt(ctx context.Context, id string) (*domain.PromptInspection, error) {
return e.runner.InspectPrompt(ctx, id, version) return e.runner.InspectPrompt(ctx, id)
} }
func (e *Engine) InspectRequirements( func (e *Engine) InspectRequirements(
ctx context.Context, ctx context.Context,

View File

@@ -19,8 +19,7 @@ var (
) )
type rendererBenchmarkIdentity struct { type rendererBenchmarkIdentity struct {
id string id string
version string
} }
type rendererBenchmarkLookup struct { type rendererBenchmarkLookup struct {
@@ -28,8 +27,8 @@ type rendererBenchmarkLookup struct {
templates CompiledTemplates templates CompiledTemplates
} }
func (lookup rendererBenchmarkLookup) GetCompiledTemplates(id, version string) (CompiledTemplates, bool) { func (lookup rendererBenchmarkLookup) GetCompiledTemplates(id string) (CompiledTemplates, bool) {
if id != lookup.identity.id || version != lookup.identity.version { if id != lookup.identity.id {
return CompiledTemplates{}, false return CompiledTemplates{}, false
} }
return lookup.templates, true return lookup.templates, true
@@ -148,7 +147,7 @@ func BenchmarkRetainedRenderedOutput(b *testing.B) {
} }
compiled := CompiledTemplates{Messages: []*template.Template{mustCompileRendererBenchmarkTemplate(b, content)}} compiled := CompiledTemplates{Messages: []*template.Template{mustCompileRendererBenchmarkTemplate(b, content)}}
renderer := NewCompiledRenderer(rendererBenchmarkLookup{ renderer := NewCompiledRenderer(rendererBenchmarkLookup{
identity: rendererBenchmarkIdentity{id: definition.ID, version: definition.Version}, identity: rendererBenchmarkIdentity{id: definition.ID},
templates: compiled, templates: compiled,
}) })
inputs := map[string]*domain.Artifact{"document": {Name: "document", Body: body}} inputs := map[string]*domain.Artifact{"document": {Name: "document", Body: body}}
@@ -228,7 +227,7 @@ func newCompiledRenderingBenchmarkCase(
return compiledRenderingBenchmarkCase{ return compiledRenderingBenchmarkCase{
name: name, name: name,
renderer: NewCompiledRenderer(rendererBenchmarkLookup{ renderer: NewCompiledRenderer(rendererBenchmarkLookup{
identity: rendererBenchmarkIdentity{id: definition.ID, version: definition.Version}, identity: rendererBenchmarkIdentity{id: definition.ID},
templates: compiled, templates: compiled,
}), }),
definition: definition, inputs: inputs, vars: vars, ctx: context.Background(), definition: definition, inputs: inputs, vars: vars, ctx: context.Background(),

View File

@@ -59,7 +59,7 @@ func (r *goRenderer) Render(ctx context.Context, definition *domain.PromptDefini
var compiled CompiledTemplates var compiled CompiledTemplates
if r.templates != nil { if r.templates != nil {
var found bool var found bool
compiled, found = r.templates.GetCompiledTemplates(definition.ID, definition.Version) compiled, found = r.templates.GetCompiledTemplates(definition.ID)
if !found || len(compiled.Messages) != len(definition.Templates) { if !found || len(compiled.Messages) != len(definition.Templates) {
return nil, fmt.Errorf("%w: captured templates are unavailable", ErrRenderFailure) return nil, fmt.Errorf("%w: captured templates are unavailable", ErrRenderFailure)
} }

View File

@@ -518,8 +518,7 @@ func TestCompiledRendererKeepsConcurrentRequestsIndependent(t *testing.T) {
t.Fatalf("compile message template: %v", err) t.Fatalf("compile message template: %v", err)
} }
renderer := NewCompiledRenderer(compiledRendererTestLookup{ renderer := NewCompiledRenderer(compiledRendererTestLookup{
id: promptID, id: promptID,
version: version,
templates: CompiledTemplates{ templates: CompiledTemplates{
Session: sessionTemplate, Session: sessionTemplate,
Messages: []*template.Template{messageTemplate}, Messages: []*template.Template{messageTemplate},
@@ -562,12 +561,12 @@ func TestCompiledRendererKeepsConcurrentRequestsIndependent(t *testing.T) {
} }
type compiledRendererTestLookup struct { type compiledRendererTestLookup struct {
id, version string id string
templates CompiledTemplates templates CompiledTemplates
} }
func (lookup compiledRendererTestLookup) GetCompiledTemplates(id, version string) (CompiledTemplates, bool) { func (lookup compiledRendererTestLookup) GetCompiledTemplates(id string) (CompiledTemplates, bool) {
if id != lookup.id || version != lookup.version { if id != lookup.id {
return CompiledTemplates{}, false return CompiledTemplates{}, false
} }
return lookup.templates, true return lookup.templates, true

View File

@@ -27,7 +27,7 @@ type RequirementSnapshot struct {
// TemplateLookup provides parsed templates for a captured prompt identity. // TemplateLookup provides parsed templates for a captured prompt identity.
type TemplateLookup interface { type TemplateLookup interface {
GetCompiledTemplates(id, version string) (CompiledTemplates, bool) GetCompiledTemplates(id string) (CompiledTemplates, bool)
} }
// CompileTemplate checks static syntax with the same grammar and function // CompileTemplate checks static syntax with the same grammar and function

View File

@@ -12,5 +12,5 @@ var ErrPromptDefinitionNotFound = errors.New("prompt definition not found")
// Repository selects prompt definitions from a captured index. // Repository selects prompt definitions from a captured index.
type Repository interface { type Repository interface {
GetPromptDefinition(ctx context.Context, id string, version string) (*domain.PromptDefinition, error) GetPromptDefinition(ctx context.Context, id string) (*domain.PromptDefinition, error)
} }

View File

@@ -680,7 +680,7 @@ func TestRunnerPreparedExecutionDetachesTransferredSnapshotSources(t *testing.T)
reader, renderer, llmClient, validator, nil, reader, renderer, llmClient, validator, nil,
) )
request := domain.RunRequest{ request := domain.RunRequest{
PromptID: "p", PromptVersion: "1", ProfileID: "exec", PromptID: "p", ProfileID: "exec",
Inputs: map[string]domain.ArtifactRef{ Inputs: map[string]domain.ArtifactRef{
"transcript": {Type: domain.ArtifactRefFile, URI: "source"}, "transcript": {Type: domain.ArtifactRefFile, URI: "source"},
}, },

View File

@@ -119,7 +119,7 @@ func newPreparedSnapshotBenchmarkRunner(
nil, nil,
) )
request := domain.RunRequest{ request := domain.RunRequest{
PromptID: "p", PromptVersion: "1", ProfileID: "exec", PromptID: "p", ProfileID: "exec",
Inputs: singleInputRef(), Vars: map[string]string{"name": "Ada"}, Inputs: singleInputRef(), Vars: map[string]string{"name": "Ada"},
AppendedMessages: []domain.RenderedMessage{{ AppendedMessages: []domain.RenderedMessage{{
Role: domain.RoleAssistant, Content: "appended message", Role: domain.RoleAssistant, Content: "appended message",

View File

@@ -14,14 +14,10 @@ type resolvedPromptDefinition struct {
} }
type promptInspectionRepository interface { type promptInspectionRepository interface {
GetPromptInspection(context.Context, string, string) (*domain.PromptInspection, error) GetPromptInspection(context.Context, string) (*domain.PromptInspection, error)
} }
func (r *Runner) resolvePromptDefinition( func (r *Runner) resolvePromptDefinition(ctx context.Context, promptID string) (*resolvedPromptDefinition, error) {
ctx context.Context,
promptID string,
promptVersion string,
) (*resolvedPromptDefinition, error) {
if strings.TrimSpace(promptID) == "" { if strings.TrimSpace(promptID) == "" {
return nil, fmt.Errorf("%w: prompt id is required", ErrInvalidRequest) return nil, fmt.Errorf("%w: prompt id is required", ErrInvalidRequest)
} }
@@ -33,11 +29,11 @@ func (r *Runner) resolvePromptDefinition(
var hash string var hash string
var err error var err error
if indexed, ok := r.promptDefs.(interface { if indexed, ok := r.promptDefs.(interface {
GetPromptSnapshot(context.Context, string, string) (*domain.PromptDefinition, string, error) GetPromptSnapshot(context.Context, string) (*domain.PromptDefinition, string, error)
}); ok { }); ok {
definition, hash, err = indexed.GetPromptSnapshot(ctx, promptID, promptVersion) definition, hash, err = indexed.GetPromptSnapshot(ctx, promptID)
} else { } else {
definition, err = r.promptDefs.GetPromptDefinition(ctx, promptID, promptVersion) definition, err = r.promptDefs.GetPromptDefinition(ctx, promptID)
} }
if err != nil { if err != nil {
return nil, fmt.Errorf("%w: %w", ErrPromptLoad, err) return nil, fmt.Errorf("%w: %w", ErrPromptLoad, err)
@@ -55,11 +51,7 @@ func (r *Runner) resolvePromptDefinition(
return &resolvedPromptDefinition{definition: definition, hash: hash}, nil return &resolvedPromptDefinition{definition: definition, hash: hash}, nil
} }
func (r *Runner) resolvePromptInspection( func (r *Runner) resolvePromptInspection(ctx context.Context, promptID string) (*domain.PromptInspection, error) {
ctx context.Context,
promptID string,
promptVersion string,
) (*domain.PromptInspection, error) {
if strings.TrimSpace(promptID) == "" { if strings.TrimSpace(promptID) == "" {
return nil, fmt.Errorf("%w: prompt id is required", ErrInvalidRequest) return nil, fmt.Errorf("%w: prompt id is required", ErrInvalidRequest)
} }
@@ -67,7 +59,7 @@ func (r *Runner) resolvePromptInspection(
return nil, fmt.Errorf("%w: prompt repository is not configured", ErrPromptLoad) return nil, fmt.Errorf("%w: prompt repository is not configured", ErrPromptLoad)
} }
if indexed, ok := r.promptDefs.(promptInspectionRepository); ok { if indexed, ok := r.promptDefs.(promptInspectionRepository); ok {
inspection, err := indexed.GetPromptInspection(ctx, promptID, promptVersion) inspection, err := indexed.GetPromptInspection(ctx, promptID)
if err != nil { if err != nil {
return nil, fmt.Errorf("%w: %w", ErrPromptLoad, err) return nil, fmt.Errorf("%w: %w", ErrPromptLoad, err)
} }
@@ -77,7 +69,7 @@ func (r *Runner) resolvePromptInspection(
return inspection, nil return inspection, nil
} }
selection, err := r.resolvePromptDefinition(ctx, promptID, promptVersion) selection, err := r.resolvePromptDefinition(ctx, promptID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -93,11 +85,7 @@ func (r *Runner) resolvePromptInspection(
} }
// InspectPrompt resolves one explicit prompt without execution work. // InspectPrompt resolves one explicit prompt without execution work.
func (r *Runner) InspectPrompt( func (r *Runner) InspectPrompt(ctx context.Context, promptID string) (*domain.PromptInspection, error) {
ctx context.Context,
promptID string,
promptVersion string,
) (*domain.PromptInspection, error) {
if strings.TrimSpace(promptID) == "" { if strings.TrimSpace(promptID) == "" {
return nil, fmt.Errorf("%w: prompt id is required", ErrInvalidRequest) return nil, fmt.Errorf("%w: prompt id is required", ErrInvalidRequest)
} }
@@ -107,5 +95,5 @@ func (r *Runner) InspectPrompt(
default: default:
} }
return r.resolvePromptInspection(ctx, promptID, promptVersion) return r.resolvePromptInspection(ctx, promptID)
} }

View File

@@ -15,41 +15,35 @@ type inspectionPromptRepository struct {
err error err error
calls int calls int
id string id string
version string
} }
type promptMetadataRepository struct { type promptMetadataRepository struct {
inspection domain.PromptInspection inspection domain.PromptInspection
inspectionErr error inspectionErr error
lookupID string lookupID string
lookupVersion string
} }
func (r *inspectionPromptRepository) GetPromptDefinition( func (r *inspectionPromptRepository) GetPromptDefinition(
_ context.Context, _ context.Context,
id string, id string,
version string,
) (*domain.PromptDefinition, error) { ) (*domain.PromptDefinition, error) {
r.calls++ r.calls++
r.id = id r.id = id
r.version = version
if r.err != nil { if r.err != nil {
return nil, r.err return nil, r.err
} }
return r.definition, nil return r.definition, nil
} }
func (r *promptMetadataRepository) GetPromptDefinition(context.Context, string, string) (*domain.PromptDefinition, error) { func (r *promptMetadataRepository) GetPromptDefinition(context.Context, string) (*domain.PromptDefinition, error) {
return nil, errors.New("full prompt definition should not be requested") return nil, errors.New("full prompt definition should not be requested")
} }
func (r *promptMetadataRepository) GetPromptInspection( func (r *promptMetadataRepository) GetPromptInspection(
_ context.Context, _ context.Context,
id string, id string,
version string,
) (*domain.PromptInspection, error) { ) (*domain.PromptInspection, error) {
r.lookupID = id r.lookupID = id
r.lookupVersion = version
inspection := r.inspection inspection := r.inspection
inspection.Inputs = append([]domain.PromptInput(nil), inspection.Inputs...) inspection.Inputs = append([]domain.PromptInput(nil), inspection.Inputs...)
return &inspection, r.inspectionErr return &inspection, r.inspectionErr
@@ -74,7 +68,7 @@ func TestRunnerInspectPromptResolvesOneDefinitionWithoutExecutionCollaborators(t
repository := &inspectionPromptRepository{definition: definition} repository := &inspectionPromptRepository{definition: definition}
runner := &Runner{promptDefs: repository} runner := &Runner{promptDefs: repository}
inspection, err := runner.InspectPrompt(context.Background(), " prompt-id ", " version ") inspection, err := runner.InspectPrompt(context.Background(), " prompt-id ")
if err != nil { if err != nil {
t.Fatalf("inspect prompt: %v", err) t.Fatalf("inspect prompt: %v", err)
} }
@@ -82,8 +76,8 @@ func TestRunnerInspectPromptResolvesOneDefinitionWithoutExecutionCollaborators(t
if err != nil { if err != nil {
t.Fatalf("hash prompt definition: %v", err) t.Fatalf("hash prompt definition: %v", err)
} }
if repository.calls != 1 || repository.id != " prompt-id " || repository.version != " version " { if repository.calls != 1 || repository.id != " prompt-id " {
t.Fatalf("prompt lookup=(calls=%d id=%q version=%q), want one unchanged lookup", repository.calls, repository.id, repository.version) t.Fatalf("prompt lookup=(calls=%d id=%q), want one unchanged ID lookup", repository.calls, repository.id)
} }
if inspection.PromptID != definition.ID || if inspection.PromptID != definition.ID ||
inspection.PromptVersion != definition.Version || inspection.PromptVersion != definition.Version ||
@@ -95,7 +89,7 @@ func TestRunnerInspectPromptResolvesOneDefinitionWithoutExecutionCollaborators(t
} }
inspection.Inputs[0].Name = "changed" inspection.Inputs[0].Name = "changed"
second, err := runner.InspectPrompt(context.Background(), " prompt-id ", " version ") second, err := runner.InspectPrompt(context.Background(), " prompt-id ")
if err != nil { if err != nil {
t.Fatalf("inspect prompt again: %v", err) t.Fatalf("inspect prompt again: %v", err)
} }
@@ -112,16 +106,16 @@ func TestRunnerInspectPromptUsesOwnedMetadataWhenRepositorySupportsIt(t *testing
OutputContract: domain.OutputContract{Format: domain.FormatJSON, ValidationMode: domain.ValidationJSON}, OutputContract: domain.OutputContract{Format: domain.FormatJSON, ValidationMode: domain.ValidationJSON},
}} }}
runner := &Runner{promptDefs: repository} runner := &Runner{promptDefs: repository}
inspection, err := runner.InspectPrompt(context.Background(), " requested-id ", " requested-version ") inspection, err := runner.InspectPrompt(context.Background(), " requested-id ")
if err != nil || repository.lookupID != " requested-id " || repository.lookupVersion != " requested-version " || if err != nil || repository.lookupID != " requested-id " ||
inspection.PromptID != "captured.prompt" || inspection.PromptVersion != "v2" || inspection.PromptID != "captured.prompt" || inspection.PromptVersion != "v2" ||
inspection.PromptHash != "cached-prompt-hash" || inspection.DefaultProfileID != "partial-profile" || inspection.PromptHash != "cached-prompt-hash" || inspection.DefaultProfileID != "partial-profile" ||
!reflect.DeepEqual(inspection.Inputs, repository.inspection.Inputs) || inspection.OutputContract != repository.inspection.OutputContract { !reflect.DeepEqual(inspection.Inputs, repository.inspection.Inputs) || inspection.OutputContract != repository.inspection.OutputContract {
t.Fatalf("metadata inspection=(%+v, %v), lookup=(%q,%q)", inspection, err, repository.lookupID, repository.lookupVersion) t.Fatalf("metadata inspection=(%+v, %v), lookup=%q", inspection, err, repository.lookupID)
} }
inspection.Inputs[0].Name = "caller mutation" inspection.Inputs[0].Name = "caller mutation"
inspection.OutputContract.SchemaPath = "caller mutation" inspection.OutputContract.SchemaPath = "caller mutation"
second, err := runner.InspectPrompt(context.Background(), "requested-id", "requested-version") second, err := runner.InspectPrompt(context.Background(), "requested-id")
if err != nil || second.Inputs[0].Name != "document" || second.OutputContract.SchemaPath != "" { if err != nil || second.Inputs[0].Name != "document" || second.OutputContract.SchemaPath != "" {
t.Fatalf("metadata inspection was not caller-owned: (%+v, %v)", second, err) t.Fatalf("metadata inspection was not caller-owned: (%+v, %v)", second, err)
} }
@@ -133,7 +127,7 @@ func TestRunnerInspectPromptUsesCapturedHashWhenMetadataCapabilityIsAbsent(t *te
ID: "captured.prompt", Version: "v1", Inputs: []domain.PromptInput{{Name: "document"}}, ID: "captured.prompt", Version: "v1", Inputs: []domain.PromptInput{{Name: "document"}},
}, },
} }
inspection, err := (&Runner{promptDefs: repository}).InspectPrompt(context.Background(), " requested-id ", " requested-version ") inspection, err := (&Runner{promptDefs: repository}).InspectPrompt(context.Background(), " requested-id ")
if err != nil || inspection.PromptID != "captured.prompt" || inspection.PromptVersion != "v1" || if err != nil || inspection.PromptID != "captured.prompt" || inspection.PromptVersion != "v1" ||
inspection.PromptHash != "captured-hash" || inspection.Inputs[0].Name != "document" { inspection.PromptHash != "captured-hash" || inspection.Inputs[0].Name != "document" {
t.Fatalf("captured-hash fallback inspection=(%+v, %v)", inspection, err) t.Fatalf("captured-hash fallback inspection=(%+v, %v)", inspection, err)
@@ -143,7 +137,7 @@ func TestRunnerInspectPromptUsesCapturedHashWhenMetadataCapabilityIsAbsent(t *te
func TestRunnerInspectPromptClassifiesFailuresWithoutRepositoryWorkAfterCancellation(t *testing.T) { func TestRunnerInspectPromptClassifiesFailuresWithoutRepositoryWorkAfterCancellation(t *testing.T) {
t.Run("blank ID", func(t *testing.T) { t.Run("blank ID", func(t *testing.T) {
repository := &inspectionPromptRepository{} repository := &inspectionPromptRepository{}
_, err := (&Runner{promptDefs: repository}).InspectPrompt(context.Background(), " \t ", "version") _, err := (&Runner{promptDefs: repository}).InspectPrompt(context.Background(), " \t ")
if !errors.Is(err, ErrInvalidRequest) || repository.calls != 0 { if !errors.Is(err, ErrInvalidRequest) || repository.calls != 0 {
t.Fatalf("blank inspection=(%v, calls=%d), want invalid request without lookup", err, repository.calls) t.Fatalf("blank inspection=(%v, calls=%d), want invalid request without lookup", err, repository.calls)
} }
@@ -153,7 +147,7 @@ func TestRunnerInspectPromptClassifiesFailuresWithoutRepositoryWorkAfterCancella
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
cancel() cancel()
repository := &inspectionPromptRepository{} repository := &inspectionPromptRepository{}
_, err := (&Runner{promptDefs: repository}).InspectPrompt(ctx, "prompt", "version") _, err := (&Runner{promptDefs: repository}).InspectPrompt(ctx, "prompt")
if !errors.Is(err, ErrPromptLoad) || !errors.Is(err, context.Canceled) || repository.calls != 0 { if !errors.Is(err, ErrPromptLoad) || !errors.Is(err, context.Canceled) || repository.calls != 0 {
t.Fatalf("canceled inspection=(%v, calls=%d), want prompt load and context identities without lookup", err, repository.calls) t.Fatalf("canceled inspection=(%v, calls=%d), want prompt load and context identities without lookup", err, repository.calls)
} }
@@ -161,7 +155,7 @@ func TestRunnerInspectPromptClassifiesFailuresWithoutRepositoryWorkAfterCancella
t.Run("missing prompt", func(t *testing.T) { t.Run("missing prompt", func(t *testing.T) {
repository := &inspectionPromptRepository{err: promptdef.ErrPromptDefinitionNotFound} repository := &inspectionPromptRepository{err: promptdef.ErrPromptDefinitionNotFound}
_, err := (&Runner{promptDefs: repository}).InspectPrompt(context.Background(), "missing", "version") _, err := (&Runner{promptDefs: repository}).InspectPrompt(context.Background(), "missing")
if !errors.Is(err, ErrPromptLoad) || !errors.Is(err, promptdef.ErrPromptDefinitionNotFound) { if !errors.Is(err, ErrPromptLoad) || !errors.Is(err, promptdef.ErrPromptDefinitionNotFound) {
t.Fatalf("missing prompt error=%v, want prompt load and not-found identities", err) t.Fatalf("missing prompt error=%v, want prompt load and not-found identities", err)
} }
@@ -179,7 +173,7 @@ func TestRunnerInspectPromptClassifiesFailuresWithoutRepositoryWorkAfterCancella
} }
for _, tc := range cases { for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
_, err := tc.runner.InspectPrompt(context.Background(), "prompt", "version") _, err := tc.runner.InspectPrompt(context.Background(), "prompt")
if !errors.Is(err, ErrPromptLoad) { if !errors.Is(err, ErrPromptLoad) {
t.Fatalf("inspection error=%v, want prompt load", err) t.Fatalf("inspection error=%v, want prompt load", err)
} }
@@ -202,11 +196,11 @@ func TestRunnerPrepareUsesThePromptInspectionSelectionAndHash(t *testing.T) {
nil, nil,
) )
inspection, err := runner.InspectPrompt(context.Background(), definition.ID, definition.Version) inspection, err := runner.InspectPrompt(context.Background(), definition.ID)
if err != nil { if err != nil {
t.Fatalf("inspect prompt: %v", err) t.Fatalf("inspect prompt: %v", err)
} }
prepared, err := runner.Prepare(context.Background(), domain.RunRequest{PromptID: definition.ID, PromptVersion: definition.Version, ProfileID: "exec"}) prepared, err := runner.Prepare(context.Background(), domain.RunRequest{PromptID: definition.ID, ProfileID: "exec"})
if err != nil { if err != nil {
t.Fatalf("prepare prompt: %v", err) t.Fatalf("prepare prompt: %v", err)
} }

View File

@@ -12,15 +12,14 @@ import (
// RequirementInspectionRequest selects a captured prompt and supplies the // RequirementInspectionRequest selects a captured prompt and supplies the
// context used for dependency classification. // context used for dependency classification.
type RequirementInspectionRequest struct { type RequirementInspectionRequest struct {
PromptID string PromptID string
PromptVersion string ProfileID string
ProfileID string Vars map[string]string
Vars map[string]string SessionID string
SessionID string
} }
type requirementSnapshotRepository interface { type requirementSnapshotRepository interface {
GetRequirementSnapshot(id, version string) (prompt.RequirementSnapshot, bool) GetRequirementSnapshot(id string) (prompt.RequirementSnapshot, bool)
} }
// InspectRequirements discovers prompt inputs and caller variables without // InspectRequirements discovers prompt inputs and caller variables without
@@ -40,7 +39,7 @@ func (r *Runner) InspectRequirements(
return nil, fmt.Errorf("%w: %w", ErrPromptLoad, err) return nil, fmt.Errorf("%w: %w", ErrPromptLoad, err)
} }
selection, err := r.resolvePromptInspection(ctx, req.PromptID, req.PromptVersion) selection, err := r.resolvePromptInspection(ctx, req.PromptID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -66,7 +65,7 @@ func (r *Runner) InspectRequirements(
if !ok { if !ok {
return nil, fmt.Errorf("%w: prompt repository does not provide captured requirement snapshots", ErrPromptLoad) return nil, fmt.Errorf("%w: prompt repository does not provide captured requirement snapshots", ErrPromptLoad)
} }
snapshot, ok := repository.GetRequirementSnapshot(selection.PromptID, selection.PromptVersion) snapshot, ok := repository.GetRequirementSnapshot(selection.PromptID)
if !ok { if !ok {
return nil, fmt.Errorf("%w: captured requirement snapshot is unavailable", ErrPromptLoad) return nil, fmt.Errorf("%w: captured requirement snapshot is unavailable", ErrPromptLoad)
} }

View File

@@ -12,23 +12,20 @@ import (
) )
type requirementInspectionPromptRepository struct { type requirementInspectionPromptRepository struct {
definition *domain.PromptDefinition definition *domain.PromptDefinition
snapshot prompt.RequirementSnapshot snapshot prompt.RequirementSnapshot
lookupErr error lookupErr error
snapshotFound bool snapshotFound bool
lookupCalls int lookupCalls int
snapshotCalls int snapshotCalls int
lookupID string lookupID string
lookupVersion string snapshotID string
snapshotID string
snapshotVersion string
} }
type requirementPromptMetadataRepository struct { type requirementPromptMetadataRepository struct {
*requirementInspectionPromptRepository *requirementInspectionPromptRepository
inspection domain.PromptInspection inspection domain.PromptInspection
lookupID string lookupID string
lookupVersion string
} }
type inspectionProfileIdentityRepository struct { type inspectionProfileIdentityRepository struct {
@@ -51,7 +48,6 @@ func (repository *inspectionProfileIdentityRepository) GetProfileIdentity(
func (repository *requirementInspectionPromptRepository) GetPromptDefinition( func (repository *requirementInspectionPromptRepository) GetPromptDefinition(
context.Context, context.Context,
string, string,
string,
) (*domain.PromptDefinition, error) { ) (*domain.PromptDefinition, error) {
return repository.definition, repository.lookupErr return repository.definition, repository.lookupErr
} }
@@ -59,10 +55,8 @@ func (repository *requirementInspectionPromptRepository) GetPromptDefinition(
func (repository *requirementPromptMetadataRepository) GetPromptInspection( func (repository *requirementPromptMetadataRepository) GetPromptInspection(
_ context.Context, _ context.Context,
id string, id string,
version string,
) (*domain.PromptInspection, error) { ) (*domain.PromptInspection, error) {
repository.lookupID = id repository.lookupID = id
repository.lookupVersion = version
inspection := repository.inspection inspection := repository.inspection
inspection.Inputs = append([]domain.PromptInput(nil), inspection.Inputs...) inspection.Inputs = append([]domain.PromptInput(nil), inspection.Inputs...)
return &inspection, nil return &inspection, nil
@@ -71,21 +65,17 @@ func (repository *requirementPromptMetadataRepository) GetPromptInspection(
func (repository *requirementInspectionPromptRepository) GetPromptSnapshot( func (repository *requirementInspectionPromptRepository) GetPromptSnapshot(
_ context.Context, _ context.Context,
id string, id string,
version string,
) (*domain.PromptDefinition, string, error) { ) (*domain.PromptDefinition, string, error) {
repository.lookupCalls++ repository.lookupCalls++
repository.lookupID = id repository.lookupID = id
repository.lookupVersion = version
return repository.definition, "captured-hash", repository.lookupErr return repository.definition, "captured-hash", repository.lookupErr
} }
func (repository *requirementInspectionPromptRepository) GetRequirementSnapshot( func (repository *requirementInspectionPromptRepository) GetRequirementSnapshot(
id string, id string,
version string,
) (prompt.RequirementSnapshot, bool) { ) (prompt.RequirementSnapshot, bool) {
repository.snapshotCalls++ repository.snapshotCalls++
repository.snapshotID = id repository.snapshotID = id
repository.snapshotVersion = version
return repository.snapshot, repository.snapshotFound return repository.snapshot, repository.snapshotFound
} }
@@ -111,16 +101,16 @@ func TestRunnerInspectRequirementsUsesCapturedSelectionWithoutExecutionWork(t *t
}} }}
runner := &Runner{promptDefs: repository, profiles: profiles} runner := &Runner{promptDefs: repository, profiles: profiles}
inspection, err := runner.InspectRequirements(context.Background(), RequirementInspectionRequest{ inspection, err := runner.InspectRequirements(context.Background(), RequirementInspectionRequest{
PromptID: " requested ", PromptVersion: " exact-version ", ProfileID: "partial-profile", PromptID: " requested ", ProfileID: "partial-profile",
Vars: map[string]string{"caller": ""}, SessionID: " direct-session ", Vars: map[string]string{"caller": ""}, SessionID: " direct-session ",
}) })
if err != nil { if err != nil {
t.Fatalf("inspect requirements: %v", err) t.Fatalf("inspect requirements: %v", err)
} }
if repository.lookupCalls != 1 || repository.lookupID != " requested " || repository.lookupVersion != " exact-version " || if repository.lookupCalls != 1 || repository.lookupID != " requested " ||
repository.snapshotCalls != 1 || repository.snapshotID != definition.ID || repository.snapshotVersion != definition.Version { repository.snapshotCalls != 1 || repository.snapshotID != definition.ID {
t.Fatalf("selection lookup=(%d,%q,%q), snapshot=(%d,%q,%q)", repository.lookupCalls, repository.lookupID, t.Fatalf("selection lookup=(%d,%q), snapshot=(%d,%q)", repository.lookupCalls, repository.lookupID,
repository.lookupVersion, repository.snapshotCalls, repository.snapshotID, repository.snapshotVersion) repository.snapshotCalls, repository.snapshotID)
} }
if profiles.calls != 1 || profiles.id != "partial-profile" || inspection.PromptID != definition.ID || if profiles.calls != 1 || profiles.id != "partial-profile" || inspection.PromptID != definition.ID ||
inspection.PromptVersion != definition.Version || inspection.ProfileID != "partial-profile" || !inspection.Complete || inspection.PromptVersion != definition.Version || inspection.ProfileID != "partial-profile" || !inspection.Complete ||
@@ -158,19 +148,18 @@ func TestRunnerInspectRequirementsUsesPromptMetadataAndExactRequirementSnapshot(
} }
inspection, err := (&Runner{promptDefs: promptRepository, profiles: profiles}).InspectRequirements( inspection, err := (&Runner{promptDefs: promptRepository, profiles: profiles}).InspectRequirements(
context.Background(), RequirementInspectionRequest{ context.Background(), RequirementInspectionRequest{
PromptID: " requested-id ", PromptVersion: " requested-version ", PromptID: " requested-id ",
Vars: map[string]string{"caller": "Ada"}, SessionID: " direct-session ", Vars: map[string]string{"caller": "Ada"}, SessionID: " direct-session ",
}, },
) )
if err != nil || promptRepository.lookupID != " requested-id " || promptRepository.lookupVersion != " requested-version " || if err != nil || promptRepository.lookupID != " requested-id " ||
baseRepository.snapshotID != "captured-id" || baseRepository.snapshotVersion != "captured-version" || baseRepository.snapshotID != "captured-id" ||
profiles.identityID != "default-profile" || inspection.PromptID != "captured-id" || profiles.identityID != "default-profile" || inspection.PromptID != "captured-id" ||
inspection.PromptVersion != "captured-version" || inspection.ProfileID != "default-profile" || inspection.PromptVersion != "captured-version" || inspection.ProfileID != "default-profile" ||
!inspection.Complete || len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "message-input" || !inspection.Complete || len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "message-input" ||
len(inspection.Variables) != 1 || inspection.Variables[0].Name != "caller" || !inspection.Variables[0].Supplied { len(inspection.Variables) != 1 || inspection.Variables[0].Name != "caller" || !inspection.Variables[0].Supplied {
t.Fatalf("metadata-based requirement inspection=(%+v, %v), prompt lookup=(%q,%q), snapshot=(%q,%q), profile=%q", t.Fatalf("metadata-based requirement inspection=(%+v, %v), prompt lookup=%q, snapshot=%q, profile=%q",
inspection, err, promptRepository.lookupID, promptRepository.lookupVersion, inspection, err, promptRepository.lookupID, baseRepository.snapshotID, profiles.identityID)
baseRepository.snapshotID, baseRepository.snapshotVersion, profiles.identityID)
} }
} }
@@ -322,12 +311,12 @@ func TestRunnerInspectRequirementsClassifiesSelectionAndSnapshotFailures(t *test
} }
}) })
t.Run("ambiguous prompt selection", func(t *testing.T) { t.Run("prompt lookup failure", func(t *testing.T) {
ambiguous := errors.New("ambiguous omitted version") lookupFailure := errors.New("prompt lookup failed")
repository := &requirementInspectionPromptRepository{lookupErr: ambiguous} repository := &requirementInspectionPromptRepository{lookupErr: lookupFailure}
_, err := (&Runner{promptDefs: repository}).InspectRequirements(context.Background(), RequirementInspectionRequest{PromptID: "selected"}) _, err := (&Runner{promptDefs: repository}).InspectRequirements(context.Background(), RequirementInspectionRequest{PromptID: "selected"})
if !errors.Is(err, ErrPromptLoad) || !errors.Is(err, ambiguous) || repository.lookupVersion != "" { if !errors.Is(err, ErrPromptLoad) || !errors.Is(err, lookupFailure) {
t.Fatalf("ambiguous selection error=%v version=%q", err, repository.lookupVersion) t.Fatalf("prompt lookup error=%v", err)
} }
}) })

View File

@@ -315,7 +315,7 @@ func (r *Runner) resolvePreparation(
return nil, fmt.Errorf("%w: session_id: %v", ErrInvalidRequest, err) return nil, fmt.Errorf("%w: session_id: %v", ErrInvalidRequest, err)
} }
promptSelection, err := r.resolvePromptDefinition(ctx, req.PromptID, req.PromptVersion) promptSelection, err := r.resolvePromptDefinition(ctx, req.PromptID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -517,22 +517,15 @@ func structuredOutputSpec(def *domain.PromptDefinition, schemaDocument any) *dom
return &domain.StructuredOutputSpec{ return &domain.StructuredOutputSpec{
Type: domain.StructuredOutputJSONSchema, Type: domain.StructuredOutputJSONSchema,
JSONSchema: &domain.StructuredOutputJSONSpec{ JSONSchema: &domain.StructuredOutputJSONSpec{
Name: deriveStructuredSchemaName(def.ID, def.Version), Name: deriveStructuredSchemaName(def.ID),
Strict: true, Strict: true,
Schema: schemaDocument, Schema: schemaDocument,
}, },
} }
} }
func deriveStructuredSchemaName(promptID string, promptVersion string) string { func deriveStructuredSchemaName(promptID string) string {
raw := strings.TrimSpace(promptID) raw := strings.TrimSpace(promptID)
if v := strings.TrimSpace(promptVersion); v != "" {
if raw == "" {
raw = v
} else {
raw = raw + "_" + v
}
}
var b strings.Builder var b strings.Builder
for _, r := range raw { for _, r := range raw {

View File

@@ -24,10 +24,9 @@ import (
) )
type fakePromptRepo struct { type fakePromptRepo struct {
def *domain.PromptDefinition def *domain.PromptDefinition
err error err error
lastID string lastID string
lastVersion string
} }
type fakeExecutionProfileRepo struct { type fakeExecutionProfileRepo struct {
@@ -43,7 +42,6 @@ type staticPromptRepo struct {
func (r staticPromptRepo) GetPromptDefinition( func (r staticPromptRepo) GetPromptDefinition(
context.Context, context.Context,
string, string,
string,
) (*domain.PromptDefinition, error) { ) (*domain.PromptDefinition, error) {
return r.def, nil return r.def, nil
} }
@@ -94,9 +92,8 @@ func (f *fakeExecutionProfileRepo) GetProfile(ctx context.Context, id string) (*
return nil, errors.New("profile not found") return nil, errors.New("profile not found")
} }
func (f *fakePromptRepo) GetPromptDefinition(ctx context.Context, id string, version string) (*domain.PromptDefinition, error) { func (f *fakePromptRepo) GetPromptDefinition(ctx context.Context, id string) (*domain.PromptDefinition, error) {
f.lastID = id f.lastID = id
f.lastVersion = version
if f.err != nil { if f.err != nil {
return nil, f.err return nil, f.err
} }
@@ -358,9 +355,8 @@ func TestRunnerPrepareWithExplicitProfileSelection(t *testing.T) {
runner := NewRunner(promptRepo, execRepo, reader, renderer, llmClient, nil, nil) runner := NewRunner(promptRepo, execRepo, reader, renderer, llmClient, nil, nil)
prepared, err := runner.Prepare(context.Background(), domain.RunRequest{ prepared, err := runner.Prepare(context.Background(), domain.RunRequest{
PromptID: "p", PromptID: "p",
PromptVersion: "1", ProfileID: "exec",
ProfileID: "exec",
Inputs: map[string]domain.ArtifactRef{ Inputs: map[string]domain.ArtifactRef{
"transcript": {Type: domain.ArtifactRefFile, URI: "a://t"}, "transcript": {Type: domain.ArtifactRefFile, URI: "a://t"},
"glossary": {Type: domain.ArtifactRefFile, URI: "a://g"}, "glossary": {Type: domain.ArtifactRefFile, URI: "a://g"},
@@ -956,8 +952,8 @@ func TestRunnerPrepareJSONSchemaBuildsStructuredOutputSpec(t *testing.T) {
if prepared.StructuredOutput.JSONSchema == nil { if prepared.StructuredOutput.JSONSchema == nil {
t.Fatal("expected structured output json_schema payload") t.Fatal("expected structured output json_schema payload")
} }
if prepared.StructuredOutput.JSONSchema.Name != "p_1" { if prepared.StructuredOutput.JSONSchema.Name != "p" {
t.Fatalf("expected derived schema name p_1, got %q", prepared.StructuredOutput.JSONSchema.Name) t.Fatalf("expected derived schema name p, got %q", prepared.StructuredOutput.JSONSchema.Name)
} }
if prepared.StructuredOutput.JSONSchema.Strict != true { if prepared.StructuredOutput.JSONSchema.Strict != true {
t.Fatalf("expected strict=true, got %v", prepared.StructuredOutput.JSONSchema.Strict) t.Fatalf("expected strict=true, got %v", prepared.StructuredOutput.JSONSchema.Strict)
@@ -1022,28 +1018,25 @@ func TestRunnerRunJSONSchemaSchemaLoadFailureFailsBeforeLLM(t *testing.T) {
func TestDeriveStructuredSchemaName(t *testing.T) { func TestDeriveStructuredSchemaName(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
id string id string
version string want string
want string
}{ }{
{ {
name: "sanitizes punctuation and keeps dashes", name: "sanitizes punctuation and keeps dashes",
id: "prompt.id/alpha", id: "prompt.id/alpha",
version: "1.0.0-beta", want: "prompt_id_alpha",
want: "prompt_id_alpha_1_0_0-beta",
}, },
{ {
name: "fallback when empty", name: "fallback when empty",
id: "", id: "",
version: "", want: "promptkit_schema",
want: "promptkit_schema",
}, },
} }
for _, tc := range tests { for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
got := deriveStructuredSchemaName(tc.id, tc.version) got := deriveStructuredSchemaName(tc.id)
if got != tc.want { if got != tc.want {
t.Fatalf("expected %q, got %q", tc.want, got) t.Fatalf("expected %q, got %q", tc.want, got)
} }
@@ -1253,9 +1246,8 @@ func TestRunnerRunSuccessful(t *testing.T) {
runner := NewRunner(promptRepo, execRepo, reader, renderer, llmClient, nil, nil) runner := NewRunner(promptRepo, execRepo, reader, renderer, llmClient, nil, nil)
res, err := runner.Run(context.Background(), domain.RunRequest{ res, err := runner.Run(context.Background(), domain.RunRequest{
PromptID: "p", PromptID: "p",
PromptVersion: "1", ProfileID: "exec",
ProfileID: "exec",
Inputs: map[string]domain.ArtifactRef{ Inputs: map[string]domain.ArtifactRef{
"transcript": {Type: domain.ArtifactRefFile, URI: "a://t"}, "transcript": {Type: domain.ArtifactRefFile, URI: "a://t"},
"glossary": {Type: domain.ArtifactRefFile, URI: "a://g"}, "glossary": {Type: domain.ArtifactRefFile, URI: "a://g"},
@@ -2325,8 +2317,8 @@ func TestRunnerRepairStateMachine(t *testing.T) {
t.Fatalf("initial structured output = %+v, want present %v", initialRequest.StructuredOutput, tc.structured) t.Fatalf("initial structured output = %+v, want present %v", initialRequest.StructuredOutput, tc.structured)
} }
if tc.structured && (initialRequest.StructuredOutput.JSONSchema == nil || if tc.structured && (initialRequest.StructuredOutput.JSONSchema == nil ||
initialRequest.StructuredOutput.JSONSchema.Name != "p_1") { initialRequest.StructuredOutput.JSONSchema.Name != "p") {
t.Fatalf("initial JSON Schema metadata = %+v, want derived schema name p_1", initialRequest.StructuredOutput) t.Fatalf("initial JSON Schema metadata = %+v, want derived schema name p", initialRequest.StructuredOutput)
} }
for index, req := range repairer.reqs { for index, req := range repairer.reqs {

View File

@@ -35,13 +35,9 @@ func (r *Runner) resolveSmokeTestTarget(ctx context.Context, req domain.SmokeTes
return nil, fmt.Errorf("%w: %w", ErrInvalidRequest, err) return nil, fmt.Errorf("%w: %w", ErrInvalidRequest, err)
} }
if req.PromptID == "" && req.PromptVersion != "" {
return nil, fmt.Errorf("%w: prompt version requires a prompt id", ErrInvalidRequest)
}
var promptInspection *domain.PromptInspection var promptInspection *domain.PromptInspection
if req.PromptID != "" { if req.PromptID != "" {
promptInspection, err = r.resolvePromptInspection(ctx, req.PromptID, req.PromptVersion) promptInspection, err = r.resolvePromptInspection(ctx, req.PromptID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -88,7 +88,7 @@ func TestResolveSmokeTestTargetSelectionAndRouting(t *testing.T) {
{ {
name: "prompt default", name: "prompt default",
request: domain.SmokeTestRequest{ request: domain.SmokeTestRequest{
Kind: domain.SmokeTestTextInference, PromptID: "selection.prompt", PromptVersion: "v1", Kind: domain.SmokeTestTextInference, PromptID: "selection.prompt",
}, },
profileID: "prompt-profile", profileID: "prompt-profile",
backendID: "beta", backendID: "beta",
@@ -100,7 +100,7 @@ func TestResolveSmokeTestTargetSelectionAndRouting(t *testing.T) {
{ {
name: "explicit profile overrides prompt default", name: "explicit profile overrides prompt default",
request: domain.SmokeTestRequest{ request: domain.SmokeTestRequest{
Kind: domain.SmokeTestTextInference, PromptID: "selection.prompt", PromptVersion: "v1", Kind: domain.SmokeTestTextInference, PromptID: "selection.prompt",
ProfileID: "alpha-profile", BackendID: "beta", ProfileID: "alpha-profile", BackendID: "beta",
}, },
profileID: "alpha-profile", profileID: "alpha-profile",
@@ -138,7 +138,7 @@ func TestResolveSmokeTestTargetSelectionAndRouting(t *testing.T) {
{ {
name: "prompt profile supplies backend-only target over backend hint", name: "prompt profile supplies backend-only target over backend hint",
request: domain.SmokeTestRequest{ request: domain.SmokeTestRequest{
Kind: domain.SmokeTestReachability, PromptID: "selection.prompt", PromptVersion: "v1", BackendID: "alpha", Kind: domain.SmokeTestReachability, PromptID: "selection.prompt", BackendID: "alpha",
}, },
profileID: "prompt-profile", profileID: "prompt-profile",
backendID: "beta", backendID: "beta",
@@ -150,7 +150,7 @@ func TestResolveSmokeTestTargetSelectionAndRouting(t *testing.T) {
{ {
name: "backend-only probe uses backend when prompt has no profile default", name: "backend-only probe uses backend when prompt has no profile default",
request: domain.SmokeTestRequest{ request: domain.SmokeTestRequest{
Kind: domain.SmokeTestReachability, PromptID: "backend.prompt", PromptVersion: "v1", BackendID: "delta", Kind: domain.SmokeTestReachability, PromptID: "backend.prompt", BackendID: "delta",
}, },
backendID: "delta", backendID: "delta",
selectionSource: smokeSelectionSourceBackend, selectionSource: smokeSelectionSourceBackend,
@@ -171,7 +171,7 @@ func TestResolveSmokeTestTargetSelectionAndRouting(t *testing.T) {
selected.target.EffectiveSettings.Model != test.wantModel { selected.target.EffectiveSettings.Model != test.wantModel {
t.Fatalf("unexpected target: %+v", selected.target) t.Fatalf("unexpected target: %+v", selected.target)
} }
if test.request.PromptID != "" && (selected.target.PromptID != test.request.PromptID || selected.target.PromptVersion != test.request.PromptVersion) { if test.request.PromptID != "" && selected.target.PromptID != test.request.PromptID {
t.Fatalf("prompt identity was not retained: %+v", selected.target) t.Fatalf("prompt identity was not retained: %+v", selected.target)
} }
}) })
@@ -276,13 +276,6 @@ func TestResolveSmokeTestTargetSelectionAndSettingsErrors(t *testing.T) {
}, },
wantErr: ErrInvalidRequest, wantErr: ErrInvalidRequest,
}, },
{
name: "version cannot be supplied without prompt",
request: domain.SmokeTestRequest{
Kind: domain.SmokeTestTextInference, PromptVersion: " ", BackendID: "epsilon",
},
wantErr: ErrInvalidRequest,
},
{ {
name: "dangling backend default fails only when selected", name: "dangling backend default fails only when selected",
request: domain.SmokeTestRequest{Kind: domain.SmokeTestTextInference, BackendID: "delta"}, request: domain.SmokeTestRequest{Kind: domain.SmokeTestTextInference, BackendID: "delta"},

View File

@@ -5,9 +5,9 @@ import (
"fmt" "fmt"
) )
// PromptIdentity identifies one prompt ID and exact version in the effective // PromptIdentity identifies one prompt ID and its optional descriptive version
// configuration captured when an Engine is constructed. It has no stable JSON // label in the effective configuration captured when an Engine is constructed.
// representation. // It has no stable JSON representation.
type PromptIdentity struct { type PromptIdentity struct {
ID string ID string
Version string Version string
@@ -36,9 +36,9 @@ func (e *Engine) ListProfiles(ctx context.Context) ([]string, error) {
return profiles, nil return profiles, nil
} }
// ListPrompts returns every prompt ID and exact version in the effective // ListPrompts returns every prompt ID in the effective configuration captured
// configuration captured when the engine was constructed, sorted lexically by // when the engine was constructed, sorted lexically by ID. Each identity may
// ID and then version. Versions are not interpreted or prioritized. The // include its descriptive version label. The
// returned slice is caller-owned and safe to mutate while the engine is used // returned slice is caller-owned and safe to mutate while the engine is used
// concurrently. Empty inventories succeed. // concurrently. Empty inventories succeed.
// //

View File

@@ -53,7 +53,7 @@ func TestListIdentitiesBasicContract(t *testing.T) {
if err != nil || profile == nil || profile.ProfileID != "configured-profile" { if err != nil || profile == nil || profile.ProfileID != "configured-profile" {
t.Fatalf("listed profile could not be inspected: (%+v, %v)", profile, err) t.Fatalf("listed profile could not be inspected: (%+v, %v)", profile, err)
} }
prompt, err := engine.InspectPrompt(context.Background(), prompts[0].ID, prompts[0].Version) prompt, err := engine.InspectPrompt(context.Background(), prompts[0].ID)
if err != nil || prompt == nil || prompt.PromptID != prompts[0].ID || prompt.PromptVersion != prompts[0].Version { if err != nil || prompt == nil || prompt.PromptID != prompts[0].ID || prompt.PromptVersion != prompts[0].Version {
t.Fatalf("listed prompt could not be inspected: (%+v, %v)", prompt, err) t.Fatalf("listed prompt could not be inspected: (%+v, %v)", prompt, err)
} }
@@ -275,7 +275,7 @@ func TestListIdentitiesAreOwnedAndSafeForConcurrentUse(t *testing.T) {
results <- fmt.Errorf("inspect profile: %w", err) results <- fmt.Errorf("inspect profile: %w", err)
return return
} }
if _, err := engine.InspectPrompt(context.Background(), "prompt", "v1"); err != nil { if _, err := engine.InspectPrompt(context.Background(), "prompt"); err != nil {
results <- fmt.Errorf("inspect prompt: %w", err) results <- fmt.Errorf("inspect prompt: %w", err)
return return
} }

View File

@@ -12,9 +12,6 @@ import (
type RequirementInspectionRequest struct { type RequirementInspectionRequest struct {
// PromptID is the exact, case-sensitive configured prompt ID. // PromptID is the exact, case-sensitive configured prompt ID.
PromptID string PromptID string
// PromptVersion selects an exact version. An empty value uses the prompt
// selection rule that requires exactly one configured version.
PromptVersion string
// ProfileID selects the exact effective profile identity. If empty, the // ProfileID selects the exact effective profile identity. If empty, the
// prompt's default profile is used; the profile need not be ready for // prompt's default profile is used; the profile need not be ready for
// execution. // execution.
@@ -131,7 +128,7 @@ type RequirementIssue struct {
type RequirementInspection struct { type RequirementInspection struct {
// PromptID is the selected exact prompt ID. // PromptID is the selected exact prompt ID.
PromptID string PromptID string
// PromptVersion is the selected exact prompt version. // PromptVersion is the selected definition's descriptive version label.
PromptVersion string PromptVersion string
// ProfileID is the selected profile identity; it does not indicate // ProfileID is the selected profile identity; it does not indicate
// execution readiness. // execution readiness.
@@ -189,7 +186,7 @@ func (e *Engine) InspectRequirements(
return nil, ErrInvalidConfig return nil, ErrInvalidConfig
} }
inspection, err := e.internal.InspectRequirements(ctx, usecase.RequirementInspectionRequest{ inspection, err := e.internal.InspectRequirements(ctx, usecase.RequirementInspectionRequest{
PromptID: req.PromptID, PromptVersion: req.PromptVersion, ProfileID: req.ProfileID, PromptID: req.PromptID, ProfileID: req.ProfileID,
Vars: copyStringMap(req.Vars), SessionID: req.SessionID, Vars: copyStringMap(req.Vars), SessionID: req.SessionID,
}) })
if err != nil { if err != nil {

View File

@@ -184,12 +184,12 @@ func TestInspectPromptHashMatchesPreparationForCapturedConfiguration(t *testing.
engine := requirementsEngine(t, prompts, map[string]any{ engine := requirementsEngine(t, prompts, map[string]any{
"profile": map[string]any{"endpoint": "https://example.test/v1", "model": "inspection-model"}, "profile": map[string]any{"endpoint": "https://example.test/v1", "model": "inspection-model"},
}) })
inspection, err := engine.InspectPrompt(context.Background(), "hash-equality", "v1") inspection, err := engine.InspectPrompt(context.Background(), "hash-equality")
if err != nil { if err != nil {
t.Fatalf("inspect captured prompt: %v", err) t.Fatalf("inspect captured prompt: %v", err)
} }
prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{ prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{
PromptID: "hash-equality", PromptVersion: "v1", Vars: map[string]string{"name": "Ada"}, PromptID: "hash-equality", Vars: map[string]string{"name": "Ada"},
}) })
if err != nil { if err != nil {
t.Fatalf("prepare captured prompt: %v", err) t.Fatalf("prepare captured prompt: %v", err)

View File

@@ -38,9 +38,6 @@ type SmokeTestRequest struct {
// PromptID selects the prompt's default profile when ProfileID is empty. The // PromptID selects the prompt's default profile when ProfileID is empty. The
// prompt's messages, inputs, templates, and output contract are not executed. // prompt's messages, inputs, templates, and output contract are not executed.
PromptID string PromptID string
// PromptVersion selects a version of PromptID using ordinary prompt-version
// selection semantics.
PromptVersion string
// ProfileID explicitly selects a profile and takes precedence over a prompt's // ProfileID explicitly selects a profile and takes precedence over a prompt's
// default profile and a backend's configured diagnostic default. // default profile and a backend's configured diagnostic default.
ProfileID string ProfileID string
@@ -68,11 +65,10 @@ func (r SmokeTestRequest) redactedString() string {
retries = fmt.Sprintf("%d", *r.TransientRetries) retries = fmt.Sprintf("%d", *r.TransientRetries)
} }
return fmt.Sprintf( return fmt.Sprintf(
"promptkit.SmokeTestRequest{Kind:%q BackendIDSet:%t PromptIDSet:%t PromptVersionSet:%t ProfileIDSet:%t APIKeySet:%t TransientRetries:%s}", "promptkit.SmokeTestRequest{Kind:%q BackendIDSet:%t PromptIDSet:%t ProfileIDSet:%t APIKeySet:%t TransientRetries:%s}",
r.Kind, r.Kind,
r.BackendID != "", r.BackendID != "",
r.PromptID != "", r.PromptID != "",
r.PromptVersion != "",
r.ProfileID != "", r.ProfileID != "",
r.APIKey != "", r.APIKey != "",
retries, retries,
@@ -119,7 +115,8 @@ type SmokeTestTarget struct {
BackendID string BackendID string
// ProfileID is the selected profile, if any. // ProfileID is the selected profile, if any.
ProfileID string ProfileID string
// PromptID and PromptVersion identify the prompt used for selection, if any. // PromptID identifies the selected prompt; PromptVersion carries its
// descriptive version label, if any.
PromptID string PromptID string
PromptVersion string PromptVersion string
// DefaultSourceBackendID identifies the backend whose configured default // DefaultSourceBackendID identifies the backend whose configured default
@@ -228,7 +225,7 @@ const (
// configuration. // configuration.
// //
// Selection prefers an explicit ProfileID, then the profile selected by // Selection prefers an explicit ProfileID, then the profile selected by
// PromptID and PromptVersion, then a configured backend default profile when // PromptID, then a configured backend default profile when
// inference requires one. For backend-only probes, a selected profile supplies // inference requires one. For backend-only probes, a selected profile supplies
// its backend metadata; otherwise BackendID selects a backend directly without // its backend metadata; otherwise BackendID selects a backend directly without
// resolving its default inference profile. A selected profile determines its // resolving its default inference profile. A selected profile determines its
@@ -286,7 +283,6 @@ func toDomainSmokeTestRequest(request SmokeTestRequest) (domain.SmokeTestRequest
Kind: domain.SmokeTestKind(request.Kind), Kind: domain.SmokeTestKind(request.Kind),
BackendID: request.BackendID, BackendID: request.BackendID,
PromptID: request.PromptID, PromptID: request.PromptID,
PromptVersion: request.PromptVersion,
ProfileID: request.ProfileID, ProfileID: request.ProfileID,
Execution: execution, Execution: execution,
APIKey: request.APIKey, APIKey: request.APIKey,

View File

@@ -279,7 +279,7 @@ func TestSmokeTestUsesPrivateInferenceWithoutApplicationResourcesOrListingChange
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
inspectionBefore, err := engine.InspectPrompt(context.Background(), "application-prompt", "") inspectionBefore, err := engine.InspectPrompt(context.Background(), "application-prompt")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -330,7 +330,7 @@ func TestSmokeTestUsesPrivateInferenceWithoutApplicationResourcesOrListingChange
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
inspectionAfter, err := engine.InspectPrompt(context.Background(), "application-prompt", "") inspectionAfter, err := engine.InspectPrompt(context.Background(), "application-prompt")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -87,9 +87,6 @@ const (
type RunRequest struct { type RunRequest struct {
// PromptID is the required non-empty prompt identifier. // PromptID is the required non-empty prompt identifier.
PromptID string PromptID string
// PromptVersion optionally selects one version of PromptID. When empty, the
// prompt source must contain exactly one matching version.
PromptVersion string
// ProfileID selects an execution profile. When empty, the prompt's default // ProfileID selects an execution profile. When empty, the prompt's default
// profile is used; if both are empty, the error matches ErrProfileRequired // profile is used; if both are empty, the error matches ErrProfileRequired
// and ErrInvalidRequest. // and ErrInvalidRequest.
@@ -162,7 +159,7 @@ type RunRequest struct {
type PreparedRun struct { type PreparedRun struct {
// PromptID is the selected prompt identifier. // PromptID is the selected prompt identifier.
PromptID string `json:"prompt_id"` PromptID string `json:"prompt_id"`
// PromptVersion is the selected prompt version. // PromptVersion is the selected definition's descriptive version label.
PromptVersion string `json:"prompt_version,omitempty"` PromptVersion string `json:"prompt_version,omitempty"`
// PromptHash is an opaque equality value for the selected definition. // PromptHash is an opaque equality value for the selected definition.
PromptHash string `json:"prompt_hash,omitempty"` PromptHash string `json:"prompt_hash,omitempty"`
@@ -220,7 +217,7 @@ type RunResult struct {
Validation ValidationResult `json:"validation"` Validation ValidationResult `json:"validation"`
// PromptID is the selected prompt identifier. // PromptID is the selected prompt identifier.
PromptID string `json:"prompt_id"` PromptID string `json:"prompt_id"`
// PromptVersion is the selected prompt version. // PromptVersion is the selected definition's descriptive version label.
PromptVersion string `json:"prompt_version,omitempty"` PromptVersion string `json:"prompt_version,omitempty"`
// PromptHash is the same opaque definition equality value exposed by // PromptHash is the same opaque definition equality value exposed by
// PreparedRun. // PreparedRun.
@@ -403,7 +400,7 @@ type PromptInputDefinition struct {
type PromptInspection struct { type PromptInspection struct {
// PromptID is the normalized ID of the selected prompt definition. // PromptID is the normalized ID of the selected prompt definition.
PromptID string PromptID string
// PromptVersion is the normalized version of the selected prompt definition. // PromptVersion is the selected definition's descriptive version label.
PromptVersion string PromptVersion string
// PromptHash is the opaque equality value for the selected definition. // PromptHash is the opaque equality value for the selected definition.
PromptHash string PromptHash string