diff --git a/assembled_workflow_benchmark_test.go b/assembled_workflow_benchmark_test.go index b54c963..27901c9 100644 --- a/assembled_workflow_benchmark_test.go +++ b/assembled_workflow_benchmark_test.go @@ -114,7 +114,7 @@ func newAssembledWorkflowFixture(b *testing.B, name string) assembledWorkflowFix b.Fatal(err) } request := promptkit.RunRequest{ - PromptID: "workflow", PromptVersion: "v1", Vars: map[string]string{"name": "Ada"}, + PromptID: "workflow", Vars: map[string]string{"name": "Ada"}, } return assembledWorkflowFixture{ engine: engine, request: request, expectedMessage: expectedMessage, response: response, diff --git a/convert.go b/convert.go index eaad194..3d7cb70 100644 --- a/convert.go +++ b/convert.go @@ -25,7 +25,6 @@ func toDomainRunRequest(req RunRequest) (domain.RunRequest, error) { } return domain.RunRequest{ PromptID: req.PromptID, - PromptVersion: req.PromptVersion, ProfileID: req.ProfileID, SessionID: req.SessionID, APIKey: req.APIKey, diff --git a/docs/consumers/pkg-promptkit.md b/docs/consumers/pkg-promptkit.md index 7f3c4f5..955cb49 100644 --- a/docs/consumers/pkg-promptkit.md +++ b/docs/consumers/pkg-promptkit.md @@ -141,7 +141,7 @@ if err != nil { } if len(prompts) > 0 { selected := prompts[0] - inspection, err := engine.InspectPrompt(ctx, selected.ID, selected.Version) + inspection, err := engine.InspectPrompt(ctx, selected.ID) if err != nil { return err } @@ -286,7 +286,7 @@ declared inputs and output workflow without creating placeholder inputs or resolving a profile: ```go -inspection, err := engine.InspectPrompt(ctx, "meeting.summary", "") +inspection, err := engine.InspectPrompt(ctx, "meeting.summary") if err != nil { return err } diff --git a/docs/internal/runner.md b/docs/internal/runner.md index 9af9e23..ff7bbf6 100644 --- a/docs/internal/runner.md +++ b/docs/internal/runner.md @@ -46,8 +46,8 @@ admission, or model collaborators. The root exact contract. `InspectRequirements` uses the same metadata selection to resolve the -request-profile/default identity, then obtains the exact captured requirement -snapshot by the selected prompt ID and version. The analyzer receives only +request-profile/default identity, then obtains the captured requirement +snapshot by the selected prompt ID. The analyzer receives only those identity fields rather than an execution definition. It analyzes the snapshot with the supplied caller variables and normalized direct-session bypass; it does not resolve target readiness or enter the preparation diff --git a/docs/internal/sources.md b/docs/internal/sources.md index 7bfd3c6..315c03d 100644 --- a/docs/internal/sources.md +++ b/docs/internal/sources.md @@ -46,7 +46,7 @@ real loader and final-value handoff. ## 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 origin and resource context, and checks static message/session template syntax 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 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` returns declared metadata without rendering or resolving profile, input, or 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) cover resource origins, context containment, capture, and selection. -The configuration adapter shares exact ID/version selection across its -internal prompt lookups. The execution snapshot lookup returns an owned prompt +The configuration adapter shares ID-only selection across its internal prompt +lookups. The execution snapshot lookup returns an owned prompt definition and cached hash; metadata inspection returns owned input metadata without copying execution templates. Profile selection likewise has an identity-only lookup for consumers that do not need the execution target, diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 4d1361c..05e3aea 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -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 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 Scope: presence-aware metadata across internal values and the facade. diff --git a/engine.go b/engine.go index a0dcff6..7410b96 100644 --- a/engine.go +++ b/engine.go @@ -24,9 +24,8 @@ var ( // ErrInvalidRequest identifies a request whose required values, overrides, // credentials, or effective settings are invalid. ErrInvalidRequest = errors.New("invalid run request") - // ErrPromptNotFound identifies a requested prompt ID or version that is not - // present in the selected prompt source. It does not also match - // ErrPromptLoad. + // ErrPromptNotFound identifies a requested prompt ID that is not present in + // the captured configuration. It does not also match ErrPromptLoad. ErrPromptNotFound = errors.New("prompt not found") // ErrProfileNotFound identifies a selected profile ID that is absent from // 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 } -// InspectPrompt resolves one explicit prompt definition without selecting a -// profile or starting execution work. +// InspectPrompt resolves one prompt ID without selecting a profile or starting +// execution work. // -// InspectPrompt requires a nonblank promptID. It passes nonblank promptID and -// promptVersion values unchanged to the engine's ordinary, case-sensitive -// 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. +// InspectPrompt requires a nonblank promptID and performs an exact, +// case-sensitive lookup in the captured configuration snapshot. // // A successful result proves that the selected definition and any referenced // 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. // // A nil Engine returns an error matching ErrInvalidConfig. A blank prompt ID -// matches ErrInvalidRequest. An absent exact ID or version matches -// ErrPromptNotFound and not ErrPromptLoad. An ambiguous omitted version matches -// ErrPromptLoad. Invalid definitions and resource failures reject construction -// instead. Cancellation during lookup preserves the context error. InspectPrompt -// returns no partial result on error. +// matches ErrInvalidRequest. An absent exact ID matches ErrPromptNotFound and +// not ErrPromptLoad. Invalid definitions and resource failures reject +// construction instead. Cancellation during lookup preserves the context +// error. InspectPrompt returns no partial result on error. func (e *Engine) InspectPrompt( ctx context.Context, promptID string, - promptVersion string, ) (*PromptInspection, error) { if e == nil || e.internal == nil { 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 { return nil, mapPublicError(err) } diff --git a/formatting.go b/formatting.go index e9958ab..60e797e 100644 --- a/formatting.go +++ b/formatting.go @@ -18,9 +18,8 @@ func (r RunRequest) GoString() string { func (r RunRequest) redactedString() string { 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.PromptVersion, r.ProfileID, r.APIKey != "", len(r.Inputs), diff --git a/internal/configuration/benchmark_test.go b/internal/configuration/benchmark_test.go index 71f193a..0d17660 100644 --- a/internal/configuration/benchmark_test.go +++ b/internal/configuration/benchmark_test.go @@ -227,7 +227,7 @@ func BenchmarkInspectionSelection(b *testing.B) { b.Fatal(err) } 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 == "" || 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) @@ -237,7 +237,7 @@ func BenchmarkInspectionSelection(b *testing.B) { len(profile.ResolvedTarget.ExtraParams["provider_options"].(map[string]any)) != scenario.entries { 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" || len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "transcript" { 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.ReportAllocs() for b.Loop() { - definition, hash, err := result.GetPromptSnapshot(ctx, "inspection", "1") + definition, hash, err := result.GetPromptSnapshot(ctx, "inspection") if err != nil { b.Fatal(err) } @@ -262,7 +262,7 @@ func BenchmarkInspectionSelection(b *testing.B) { b.Run("prompt_inspection/"+scenario.name, func(b *testing.B) { b.ReportAllocs() for b.Loop() { - inspection, err := result.GetPromptInspection(ctx, "inspection", "1") + inspection, err := result.GetPromptInspection(ctx, "inspection") if err != nil { b.Fatal(err) } diff --git a/internal/configuration/prompts.go b/internal/configuration/prompts.go index ef190c9..cc7382c 100644 --- a/internal/configuration/prompts.go +++ b/internal/configuration/prompts.go @@ -286,7 +286,7 @@ func (state *loaded) readContent(fieldPath configkit.Path, name string, lookupOr 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) == "" { return nil, errPromptNotFound } @@ -322,7 +322,7 @@ func promptLookupError(err error) 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 { 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 -// construction-time hash. The version argument is retained temporarily at the -// adapter boundary and does not participate in lookup. -func (state *loaded) GetPromptSnapshot(ctx context.Context, id, version string) (*domain.PromptDefinition, string, error) { +// construction-time hash. +func (state *loaded) GetPromptSnapshot(ctx context.Context, id string) (*domain.PromptDefinition, string, error) { if err := ctx.Err(); err != nil { return nil, "", err } - entry, err := state.lookupPromptEntry(id, version) + entry, err := state.lookupPromptEntry(id) if err != nil { 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 // 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 { return nil, err } - entry, err := state.lookupPromptEntry(id, version) + entry, err := state.lookupPromptEntry(id) if err != nil { 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. -func (state *loaded) GetPromptDefinition(ctx context.Context, id, version string) (*domain.PromptDefinition, error) { - definition, _, err := state.GetPromptSnapshot(ctx, id, version) +func (state *loaded) GetPromptDefinition(ctx context.Context, id string) (*domain.PromptDefinition, error) { + definition, _, err := state.GetPromptSnapshot(ctx, id) return definition, err } // 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 { 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 // one prompt ID. Returned metadata and slices are independently // 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 { return prompt.RequirementSnapshot{}, false } diff --git a/internal/configuration/prompts_test.go b/internal/configuration/prompts_test.go index 0cd5a64..2ded508 100644 --- a/internal/configuration/prompts_test.go +++ b/internal/configuration/prompts_test.go @@ -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 { 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 { 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 { t.Fatal(err) } owned.Inputs[0].Name = "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" { t.Fatalf("inspection mutation escaped into captured state: inspection=%+v err=%v", again, err) } 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) { 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) { t.Errorf("snapshot lookup %q=(%+v, %v), want not found", id, definition, err) } } ctx, cancel := context.WithCancel(context.Background()) 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) } } @@ -269,7 +269,7 @@ func TestRequirementSnapshotCapturesMetadataPresenceAndNormalizedValues(t *testi 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 { 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[3].Name = "caller mutation" 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 { t.Fatalf("snapshot mutation escaped into captured state: %+v, %t", again, ok) } diff --git a/internal/domain/domain.go b/internal/domain/domain.go index 686b439..1cc5796 100644 --- a/internal/domain/domain.go +++ b/internal/domain/domain.go @@ -61,7 +61,6 @@ type CacheControl struct { // RunRequest represents a request to generate a single artifact. type RunRequest struct { PromptID string - PromptVersion string ProfileID string SessionID string APIKey string `json:"-" yaml:"-"` @@ -135,7 +134,7 @@ type Artifact struct { Hash string } -// PromptIdentity identifies one configured prompt version. +// PromptIdentity identifies one configured prompt and its descriptive version label. type PromptIdentity struct { ID string Version string diff --git a/internal/domain/smoke_diagnostics.go b/internal/domain/smoke_diagnostics.go index d1fa129..6ee608d 100644 --- a/internal/domain/smoke_diagnostics.go +++ b/internal/domain/smoke_diagnostics.go @@ -39,7 +39,6 @@ type SmokeTestRequest struct { Kind SmokeTestKind BackendID string PromptID string - PromptVersion string ProfileID string Execution *ExecutionTargetOverride APIKey string `json:"-" yaml:"-"` diff --git a/internal/engine/engine.go b/internal/engine/engine.go index d1d56da..3f91f1f 100644 --- a/internal/engine/engine.go +++ b/internal/engine/engine.go @@ -187,8 +187,8 @@ func fileSource(name string) (fs.FS, string, string, error) { return os.DirFS(directory), filepath.ToSlash(base), directory, nil } -func (e *Engine) InspectPrompt(ctx context.Context, id, version string) (*domain.PromptInspection, error) { - return e.runner.InspectPrompt(ctx, id, version) +func (e *Engine) InspectPrompt(ctx context.Context, id string) (*domain.PromptInspection, error) { + return e.runner.InspectPrompt(ctx, id) } func (e *Engine) InspectRequirements( ctx context.Context, diff --git a/internal/prompt/compiled_renderer_benchmark_test.go b/internal/prompt/compiled_renderer_benchmark_test.go index 85df1b1..0ea2605 100644 --- a/internal/prompt/compiled_renderer_benchmark_test.go +++ b/internal/prompt/compiled_renderer_benchmark_test.go @@ -19,8 +19,7 @@ var ( ) type rendererBenchmarkIdentity struct { - id string - version string + id string } type rendererBenchmarkLookup struct { @@ -28,8 +27,8 @@ type rendererBenchmarkLookup struct { templates CompiledTemplates } -func (lookup rendererBenchmarkLookup) GetCompiledTemplates(id, version string) (CompiledTemplates, bool) { - if id != lookup.identity.id || version != lookup.identity.version { +func (lookup rendererBenchmarkLookup) GetCompiledTemplates(id string) (CompiledTemplates, bool) { + if id != lookup.identity.id { return CompiledTemplates{}, false } return lookup.templates, true @@ -148,7 +147,7 @@ func BenchmarkRetainedRenderedOutput(b *testing.B) { } compiled := CompiledTemplates{Messages: []*template.Template{mustCompileRendererBenchmarkTemplate(b, content)}} renderer := NewCompiledRenderer(rendererBenchmarkLookup{ - identity: rendererBenchmarkIdentity{id: definition.ID, version: definition.Version}, + identity: rendererBenchmarkIdentity{id: definition.ID}, templates: compiled, }) inputs := map[string]*domain.Artifact{"document": {Name: "document", Body: body}} @@ -228,7 +227,7 @@ func newCompiledRenderingBenchmarkCase( return compiledRenderingBenchmarkCase{ name: name, renderer: NewCompiledRenderer(rendererBenchmarkLookup{ - identity: rendererBenchmarkIdentity{id: definition.ID, version: definition.Version}, + identity: rendererBenchmarkIdentity{id: definition.ID}, templates: compiled, }), definition: definition, inputs: inputs, vars: vars, ctx: context.Background(), diff --git a/internal/prompt/go_renderer.go b/internal/prompt/go_renderer.go index 2296a14..99e8568 100644 --- a/internal/prompt/go_renderer.go +++ b/internal/prompt/go_renderer.go @@ -59,7 +59,7 @@ func (r *goRenderer) Render(ctx context.Context, definition *domain.PromptDefini var compiled CompiledTemplates if r.templates != nil { 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) { return nil, fmt.Errorf("%w: captured templates are unavailable", ErrRenderFailure) } diff --git a/internal/prompt/renderer_test.go b/internal/prompt/renderer_test.go index 928e1b5..5d24169 100644 --- a/internal/prompt/renderer_test.go +++ b/internal/prompt/renderer_test.go @@ -518,8 +518,7 @@ func TestCompiledRendererKeepsConcurrentRequestsIndependent(t *testing.T) { t.Fatalf("compile message template: %v", err) } renderer := NewCompiledRenderer(compiledRendererTestLookup{ - id: promptID, - version: version, + id: promptID, templates: CompiledTemplates{ Session: sessionTemplate, Messages: []*template.Template{messageTemplate}, @@ -562,12 +561,12 @@ func TestCompiledRendererKeepsConcurrentRequestsIndependent(t *testing.T) { } type compiledRendererTestLookup struct { - id, version string - templates CompiledTemplates + id string + templates CompiledTemplates } -func (lookup compiledRendererTestLookup) GetCompiledTemplates(id, version string) (CompiledTemplates, bool) { - if id != lookup.id || version != lookup.version { +func (lookup compiledRendererTestLookup) GetCompiledTemplates(id string) (CompiledTemplates, bool) { + if id != lookup.id { return CompiledTemplates{}, false } return lookup.templates, true diff --git a/internal/prompt/template.go b/internal/prompt/template.go index 82422b2..b32e87a 100644 --- a/internal/prompt/template.go +++ b/internal/prompt/template.go @@ -27,7 +27,7 @@ type RequirementSnapshot struct { // TemplateLookup provides parsed templates for a captured prompt identity. type TemplateLookup interface { - GetCompiledTemplates(id, version string) (CompiledTemplates, bool) + GetCompiledTemplates(id string) (CompiledTemplates, bool) } // CompileTemplate checks static syntax with the same grammar and function diff --git a/internal/promptdef/repository.go b/internal/promptdef/repository.go index 67a4aa1..4686387 100644 --- a/internal/promptdef/repository.go +++ b/internal/promptdef/repository.go @@ -12,5 +12,5 @@ var ErrPromptDefinitionNotFound = errors.New("prompt definition not found") // Repository selects prompt definitions from a captured index. type Repository interface { - GetPromptDefinition(ctx context.Context, id string, version string) (*domain.PromptDefinition, error) + GetPromptDefinition(ctx context.Context, id string) (*domain.PromptDefinition, error) } diff --git a/internal/usecase/prepared_execution_test.go b/internal/usecase/prepared_execution_test.go index 1a8199d..c6f345b 100644 --- a/internal/usecase/prepared_execution_test.go +++ b/internal/usecase/prepared_execution_test.go @@ -680,7 +680,7 @@ func TestRunnerPreparedExecutionDetachesTransferredSnapshotSources(t *testing.T) reader, renderer, llmClient, validator, nil, ) request := domain.RunRequest{ - PromptID: "p", PromptVersion: "1", ProfileID: "exec", + PromptID: "p", ProfileID: "exec", Inputs: map[string]domain.ArtifactRef{ "transcript": {Type: domain.ArtifactRefFile, URI: "source"}, }, diff --git a/internal/usecase/prepared_snapshot_benchmark_test.go b/internal/usecase/prepared_snapshot_benchmark_test.go index de8fbb7..4820ab6 100644 --- a/internal/usecase/prepared_snapshot_benchmark_test.go +++ b/internal/usecase/prepared_snapshot_benchmark_test.go @@ -119,7 +119,7 @@ func newPreparedSnapshotBenchmarkRunner( nil, ) request := domain.RunRequest{ - PromptID: "p", PromptVersion: "1", ProfileID: "exec", + PromptID: "p", ProfileID: "exec", Inputs: singleInputRef(), Vars: map[string]string{"name": "Ada"}, AppendedMessages: []domain.RenderedMessage{{ Role: domain.RoleAssistant, Content: "appended message", diff --git a/internal/usecase/prompt_inspection.go b/internal/usecase/prompt_inspection.go index f985a8b..ee5d959 100644 --- a/internal/usecase/prompt_inspection.go +++ b/internal/usecase/prompt_inspection.go @@ -14,14 +14,10 @@ type resolvedPromptDefinition struct { } type promptInspectionRepository interface { - GetPromptInspection(context.Context, string, string) (*domain.PromptInspection, error) + GetPromptInspection(context.Context, string) (*domain.PromptInspection, error) } -func (r *Runner) resolvePromptDefinition( - ctx context.Context, - promptID string, - promptVersion string, -) (*resolvedPromptDefinition, error) { +func (r *Runner) resolvePromptDefinition(ctx context.Context, promptID string) (*resolvedPromptDefinition, error) { if strings.TrimSpace(promptID) == "" { return nil, fmt.Errorf("%w: prompt id is required", ErrInvalidRequest) } @@ -33,11 +29,11 @@ func (r *Runner) resolvePromptDefinition( var hash string var err error if indexed, ok := r.promptDefs.(interface { - GetPromptSnapshot(context.Context, string, string) (*domain.PromptDefinition, string, error) + GetPromptSnapshot(context.Context, string) (*domain.PromptDefinition, string, error) }); ok { - definition, hash, err = indexed.GetPromptSnapshot(ctx, promptID, promptVersion) + definition, hash, err = indexed.GetPromptSnapshot(ctx, promptID) } else { - definition, err = r.promptDefs.GetPromptDefinition(ctx, promptID, promptVersion) + definition, err = r.promptDefs.GetPromptDefinition(ctx, promptID) } if err != nil { return nil, fmt.Errorf("%w: %w", ErrPromptLoad, err) @@ -55,11 +51,7 @@ func (r *Runner) resolvePromptDefinition( return &resolvedPromptDefinition{definition: definition, hash: hash}, nil } -func (r *Runner) resolvePromptInspection( - ctx context.Context, - promptID string, - promptVersion string, -) (*domain.PromptInspection, error) { +func (r *Runner) resolvePromptInspection(ctx context.Context, promptID string) (*domain.PromptInspection, error) { if strings.TrimSpace(promptID) == "" { 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) } if indexed, ok := r.promptDefs.(promptInspectionRepository); ok { - inspection, err := indexed.GetPromptInspection(ctx, promptID, promptVersion) + inspection, err := indexed.GetPromptInspection(ctx, promptID) if err != nil { return nil, fmt.Errorf("%w: %w", ErrPromptLoad, err) } @@ -77,7 +69,7 @@ func (r *Runner) resolvePromptInspection( return inspection, nil } - selection, err := r.resolvePromptDefinition(ctx, promptID, promptVersion) + selection, err := r.resolvePromptDefinition(ctx, promptID) if err != nil { return nil, err } @@ -93,11 +85,7 @@ func (r *Runner) resolvePromptInspection( } // InspectPrompt resolves one explicit prompt without execution work. -func (r *Runner) InspectPrompt( - ctx context.Context, - promptID string, - promptVersion string, -) (*domain.PromptInspection, error) { +func (r *Runner) InspectPrompt(ctx context.Context, promptID string) (*domain.PromptInspection, error) { if strings.TrimSpace(promptID) == "" { return nil, fmt.Errorf("%w: prompt id is required", ErrInvalidRequest) } @@ -107,5 +95,5 @@ func (r *Runner) InspectPrompt( default: } - return r.resolvePromptInspection(ctx, promptID, promptVersion) + return r.resolvePromptInspection(ctx, promptID) } diff --git a/internal/usecase/prompt_inspection_test.go b/internal/usecase/prompt_inspection_test.go index e87c3c8..83042b3 100644 --- a/internal/usecase/prompt_inspection_test.go +++ b/internal/usecase/prompt_inspection_test.go @@ -15,41 +15,35 @@ type inspectionPromptRepository struct { err error calls int id string - version string } type promptMetadataRepository struct { inspection domain.PromptInspection inspectionErr error lookupID string - lookupVersion string } func (r *inspectionPromptRepository) GetPromptDefinition( _ context.Context, id string, - version string, ) (*domain.PromptDefinition, error) { r.calls++ r.id = id - r.version = version if r.err != nil { return nil, r.err } 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") } func (r *promptMetadataRepository) GetPromptInspection( _ context.Context, id string, - version string, ) (*domain.PromptInspection, error) { r.lookupID = id - r.lookupVersion = version inspection := r.inspection inspection.Inputs = append([]domain.PromptInput(nil), inspection.Inputs...) return &inspection, r.inspectionErr @@ -74,7 +68,7 @@ func TestRunnerInspectPromptResolvesOneDefinitionWithoutExecutionCollaborators(t repository := &inspectionPromptRepository{definition: definition} runner := &Runner{promptDefs: repository} - inspection, err := runner.InspectPrompt(context.Background(), " prompt-id ", " version ") + inspection, err := runner.InspectPrompt(context.Background(), " prompt-id ") if err != nil { t.Fatalf("inspect prompt: %v", err) } @@ -82,8 +76,8 @@ func TestRunnerInspectPromptResolvesOneDefinitionWithoutExecutionCollaborators(t if err != nil { t.Fatalf("hash prompt definition: %v", err) } - if repository.calls != 1 || repository.id != " prompt-id " || repository.version != " version " { - t.Fatalf("prompt lookup=(calls=%d id=%q version=%q), want one unchanged lookup", repository.calls, repository.id, repository.version) + if repository.calls != 1 || repository.id != " prompt-id " { + t.Fatalf("prompt lookup=(calls=%d id=%q), want one unchanged ID lookup", repository.calls, repository.id) } if inspection.PromptID != definition.ID || inspection.PromptVersion != definition.Version || @@ -95,7 +89,7 @@ func TestRunnerInspectPromptResolvesOneDefinitionWithoutExecutionCollaborators(t } 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 { t.Fatalf("inspect prompt again: %v", err) } @@ -112,16 +106,16 @@ func TestRunnerInspectPromptUsesOwnedMetadataWhenRepositorySupportsIt(t *testing OutputContract: domain.OutputContract{Format: domain.FormatJSON, ValidationMode: domain.ValidationJSON}, }} runner := &Runner{promptDefs: repository} - inspection, err := runner.InspectPrompt(context.Background(), " requested-id ", " requested-version ") - if err != nil || repository.lookupID != " requested-id " || repository.lookupVersion != " requested-version " || + inspection, err := runner.InspectPrompt(context.Background(), " requested-id ") + if err != nil || repository.lookupID != " requested-id " || inspection.PromptID != "captured.prompt" || inspection.PromptVersion != "v2" || inspection.PromptHash != "cached-prompt-hash" || inspection.DefaultProfileID != "partial-profile" || !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.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 != "" { 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"}}, }, } - 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" || inspection.PromptHash != "captured-hash" || inspection.Inputs[0].Name != "document" { t.Fatalf("captured-hash fallback inspection=(%+v, %v)", inspection, err) @@ -143,7 +137,7 @@ func TestRunnerInspectPromptUsesCapturedHashWhenMetadataCapabilityIsAbsent(t *te func TestRunnerInspectPromptClassifiesFailuresWithoutRepositoryWorkAfterCancellation(t *testing.T) { t.Run("blank ID", func(t *testing.T) { 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 { 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()) cancel() 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 { 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) { 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) { t.Fatalf("missing prompt error=%v, want prompt load and not-found identities", err) } @@ -179,7 +173,7 @@ func TestRunnerInspectPromptClassifiesFailuresWithoutRepositoryWorkAfterCancella } for _, tc := range cases { 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) { t.Fatalf("inspection error=%v, want prompt load", err) } @@ -202,11 +196,11 @@ func TestRunnerPrepareUsesThePromptInspectionSelectionAndHash(t *testing.T) { nil, ) - inspection, err := runner.InspectPrompt(context.Background(), definition.ID, definition.Version) + inspection, err := runner.InspectPrompt(context.Background(), definition.ID) if err != nil { 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 { t.Fatalf("prepare prompt: %v", err) } diff --git a/internal/usecase/requirements_inspection.go b/internal/usecase/requirements_inspection.go index c243ac5..32e81a0 100644 --- a/internal/usecase/requirements_inspection.go +++ b/internal/usecase/requirements_inspection.go @@ -12,15 +12,14 @@ import ( // RequirementInspectionRequest selects a captured prompt and supplies the // context used for dependency classification. type RequirementInspectionRequest struct { - PromptID string - PromptVersion string - ProfileID string - Vars map[string]string - SessionID string + PromptID string + ProfileID string + Vars map[string]string + SessionID string } type requirementSnapshotRepository interface { - GetRequirementSnapshot(id, version string) (prompt.RequirementSnapshot, bool) + GetRequirementSnapshot(id string) (prompt.RequirementSnapshot, bool) } // InspectRequirements discovers prompt inputs and caller variables without @@ -40,7 +39,7 @@ func (r *Runner) InspectRequirements( 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 { return nil, err } @@ -66,7 +65,7 @@ func (r *Runner) InspectRequirements( if !ok { 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 { return nil, fmt.Errorf("%w: captured requirement snapshot is unavailable", ErrPromptLoad) } diff --git a/internal/usecase/requirements_inspection_test.go b/internal/usecase/requirements_inspection_test.go index 0a8ffa4..c9299e6 100644 --- a/internal/usecase/requirements_inspection_test.go +++ b/internal/usecase/requirements_inspection_test.go @@ -12,23 +12,20 @@ import ( ) type requirementInspectionPromptRepository struct { - definition *domain.PromptDefinition - snapshot prompt.RequirementSnapshot - lookupErr error - snapshotFound bool - lookupCalls int - snapshotCalls int - lookupID string - lookupVersion string - snapshotID string - snapshotVersion string + definition *domain.PromptDefinition + snapshot prompt.RequirementSnapshot + lookupErr error + snapshotFound bool + lookupCalls int + snapshotCalls int + lookupID string + snapshotID string } type requirementPromptMetadataRepository struct { *requirementInspectionPromptRepository - inspection domain.PromptInspection - lookupID string - lookupVersion string + inspection domain.PromptInspection + lookupID string } type inspectionProfileIdentityRepository struct { @@ -51,7 +48,6 @@ func (repository *inspectionProfileIdentityRepository) GetProfileIdentity( func (repository *requirementInspectionPromptRepository) GetPromptDefinition( context.Context, string, - string, ) (*domain.PromptDefinition, error) { return repository.definition, repository.lookupErr } @@ -59,10 +55,8 @@ func (repository *requirementInspectionPromptRepository) GetPromptDefinition( func (repository *requirementPromptMetadataRepository) GetPromptInspection( _ context.Context, id string, - version string, ) (*domain.PromptInspection, error) { repository.lookupID = id - repository.lookupVersion = version inspection := repository.inspection inspection.Inputs = append([]domain.PromptInput(nil), inspection.Inputs...) return &inspection, nil @@ -71,21 +65,17 @@ func (repository *requirementPromptMetadataRepository) GetPromptInspection( func (repository *requirementInspectionPromptRepository) GetPromptSnapshot( _ context.Context, id string, - version string, ) (*domain.PromptDefinition, string, error) { repository.lookupCalls++ repository.lookupID = id - repository.lookupVersion = version return repository.definition, "captured-hash", repository.lookupErr } func (repository *requirementInspectionPromptRepository) GetRequirementSnapshot( id string, - version string, ) (prompt.RequirementSnapshot, bool) { repository.snapshotCalls++ repository.snapshotID = id - repository.snapshotVersion = version return repository.snapshot, repository.snapshotFound } @@ -111,16 +101,16 @@ func TestRunnerInspectRequirementsUsesCapturedSelectionWithoutExecutionWork(t *t }} runner := &Runner{promptDefs: repository, profiles: profiles} 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 ", }) if err != nil { t.Fatalf("inspect requirements: %v", err) } - if repository.lookupCalls != 1 || repository.lookupID != " requested " || repository.lookupVersion != " exact-version " || - repository.snapshotCalls != 1 || repository.snapshotID != definition.ID || repository.snapshotVersion != definition.Version { - t.Fatalf("selection lookup=(%d,%q,%q), snapshot=(%d,%q,%q)", repository.lookupCalls, repository.lookupID, - repository.lookupVersion, repository.snapshotCalls, repository.snapshotID, repository.snapshotVersion) + if repository.lookupCalls != 1 || repository.lookupID != " requested " || + repository.snapshotCalls != 1 || repository.snapshotID != definition.ID { + t.Fatalf("selection lookup=(%d,%q), snapshot=(%d,%q)", repository.lookupCalls, repository.lookupID, + repository.snapshotCalls, repository.snapshotID) } if profiles.calls != 1 || profiles.id != "partial-profile" || inspection.PromptID != definition.ID || inspection.PromptVersion != definition.Version || inspection.ProfileID != "partial-profile" || !inspection.Complete || @@ -158,19 +148,18 @@ func TestRunnerInspectRequirementsUsesPromptMetadataAndExactRequirementSnapshot( } inspection, err := (&Runner{promptDefs: promptRepository, profiles: profiles}).InspectRequirements( context.Background(), RequirementInspectionRequest{ - PromptID: " requested-id ", PromptVersion: " requested-version ", - Vars: map[string]string{"caller": "Ada"}, SessionID: " direct-session ", + PromptID: " requested-id ", + Vars: map[string]string{"caller": "Ada"}, SessionID: " direct-session ", }, ) - if err != nil || promptRepository.lookupID != " requested-id " || promptRepository.lookupVersion != " requested-version " || - baseRepository.snapshotID != "captured-id" || baseRepository.snapshotVersion != "captured-version" || + if err != nil || promptRepository.lookupID != " requested-id " || + baseRepository.snapshotID != "captured-id" || profiles.identityID != "default-profile" || inspection.PromptID != "captured-id" || inspection.PromptVersion != "captured-version" || inspection.ProfileID != "default-profile" || !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 { - t.Fatalf("metadata-based requirement inspection=(%+v, %v), prompt lookup=(%q,%q), snapshot=(%q,%q), profile=%q", - inspection, err, promptRepository.lookupID, promptRepository.lookupVersion, - baseRepository.snapshotID, baseRepository.snapshotVersion, profiles.identityID) + t.Fatalf("metadata-based requirement inspection=(%+v, %v), prompt lookup=%q, snapshot=%q, profile=%q", + inspection, err, promptRepository.lookupID, baseRepository.snapshotID, profiles.identityID) } } @@ -322,12 +311,12 @@ func TestRunnerInspectRequirementsClassifiesSelectionAndSnapshotFailures(t *test } }) - t.Run("ambiguous prompt selection", func(t *testing.T) { - ambiguous := errors.New("ambiguous omitted version") - repository := &requirementInspectionPromptRepository{lookupErr: ambiguous} + t.Run("prompt lookup failure", func(t *testing.T) { + lookupFailure := errors.New("prompt lookup failed") + repository := &requirementInspectionPromptRepository{lookupErr: lookupFailure} _, err := (&Runner{promptDefs: repository}).InspectRequirements(context.Background(), RequirementInspectionRequest{PromptID: "selected"}) - if !errors.Is(err, ErrPromptLoad) || !errors.Is(err, ambiguous) || repository.lookupVersion != "" { - t.Fatalf("ambiguous selection error=%v version=%q", err, repository.lookupVersion) + if !errors.Is(err, ErrPromptLoad) || !errors.Is(err, lookupFailure) { + t.Fatalf("prompt lookup error=%v", err) } }) diff --git a/internal/usecase/runner.go b/internal/usecase/runner.go index 387d3c3..9316660 100644 --- a/internal/usecase/runner.go +++ b/internal/usecase/runner.go @@ -315,7 +315,7 @@ func (r *Runner) resolvePreparation( 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 { return nil, err } @@ -517,22 +517,15 @@ func structuredOutputSpec(def *domain.PromptDefinition, schemaDocument any) *dom return &domain.StructuredOutputSpec{ Type: domain.StructuredOutputJSONSchema, JSONSchema: &domain.StructuredOutputJSONSpec{ - Name: deriveStructuredSchemaName(def.ID, def.Version), + Name: deriveStructuredSchemaName(def.ID), Strict: true, Schema: schemaDocument, }, } } -func deriveStructuredSchemaName(promptID string, promptVersion string) string { +func deriveStructuredSchemaName(promptID string) string { raw := strings.TrimSpace(promptID) - if v := strings.TrimSpace(promptVersion); v != "" { - if raw == "" { - raw = v - } else { - raw = raw + "_" + v - } - } var b strings.Builder for _, r := range raw { diff --git a/internal/usecase/runner_test.go b/internal/usecase/runner_test.go index 7bb4540..9a4fb6c 100644 --- a/internal/usecase/runner_test.go +++ b/internal/usecase/runner_test.go @@ -24,10 +24,9 @@ import ( ) type fakePromptRepo struct { - def *domain.PromptDefinition - err error - lastID string - lastVersion string + def *domain.PromptDefinition + err error + lastID string } type fakeExecutionProfileRepo struct { @@ -43,7 +42,6 @@ type staticPromptRepo struct { func (r staticPromptRepo) GetPromptDefinition( context.Context, string, - string, ) (*domain.PromptDefinition, error) { return r.def, nil } @@ -94,9 +92,8 @@ func (f *fakeExecutionProfileRepo) GetProfile(ctx context.Context, id string) (* 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.lastVersion = version if f.err != nil { return nil, f.err } @@ -358,9 +355,8 @@ func TestRunnerPrepareWithExplicitProfileSelection(t *testing.T) { runner := NewRunner(promptRepo, execRepo, reader, renderer, llmClient, nil, nil) prepared, err := runner.Prepare(context.Background(), domain.RunRequest{ - PromptID: "p", - PromptVersion: "1", - ProfileID: "exec", + PromptID: "p", + ProfileID: "exec", Inputs: map[string]domain.ArtifactRef{ "transcript": {Type: domain.ArtifactRefFile, URI: "a://t"}, "glossary": {Type: domain.ArtifactRefFile, URI: "a://g"}, @@ -956,8 +952,8 @@ func TestRunnerPrepareJSONSchemaBuildsStructuredOutputSpec(t *testing.T) { if prepared.StructuredOutput.JSONSchema == nil { t.Fatal("expected structured output json_schema payload") } - if prepared.StructuredOutput.JSONSchema.Name != "p_1" { - t.Fatalf("expected derived schema name p_1, got %q", prepared.StructuredOutput.JSONSchema.Name) + if prepared.StructuredOutput.JSONSchema.Name != "p" { + t.Fatalf("expected derived schema name p, got %q", prepared.StructuredOutput.JSONSchema.Name) } if prepared.StructuredOutput.JSONSchema.Strict != true { 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) { tests := []struct { - name string - id string - version string - want string + name string + id string + want string }{ { - name: "sanitizes punctuation and keeps dashes", - id: "prompt.id/alpha", - version: "1.0.0-beta", - want: "prompt_id_alpha_1_0_0-beta", + name: "sanitizes punctuation and keeps dashes", + id: "prompt.id/alpha", + want: "prompt_id_alpha", }, { - name: "fallback when empty", - id: "", - version: "", - want: "promptkit_schema", + name: "fallback when empty", + id: "", + want: "promptkit_schema", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - got := deriveStructuredSchemaName(tc.id, tc.version) + got := deriveStructuredSchemaName(tc.id) if got != tc.want { 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) res, err := runner.Run(context.Background(), domain.RunRequest{ - PromptID: "p", - PromptVersion: "1", - ProfileID: "exec", + PromptID: "p", + ProfileID: "exec", Inputs: map[string]domain.ArtifactRef{ "transcript": {Type: domain.ArtifactRefFile, URI: "a://t"}, "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) } if tc.structured && (initialRequest.StructuredOutput.JSONSchema == nil || - initialRequest.StructuredOutput.JSONSchema.Name != "p_1") { - t.Fatalf("initial JSON Schema metadata = %+v, want derived schema name p_1", initialRequest.StructuredOutput) + initialRequest.StructuredOutput.JSONSchema.Name != "p") { + t.Fatalf("initial JSON Schema metadata = %+v, want derived schema name p", initialRequest.StructuredOutput) } for index, req := range repairer.reqs { diff --git a/internal/usecase/smoke_selection.go b/internal/usecase/smoke_selection.go index cfc99c4..5b5706b 100644 --- a/internal/usecase/smoke_selection.go +++ b/internal/usecase/smoke_selection.go @@ -35,13 +35,9 @@ func (r *Runner) resolveSmokeTestTarget(ctx context.Context, req domain.SmokeTes 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 if req.PromptID != "" { - promptInspection, err = r.resolvePromptInspection(ctx, req.PromptID, req.PromptVersion) + promptInspection, err = r.resolvePromptInspection(ctx, req.PromptID) if err != nil { return nil, err } diff --git a/internal/usecase/smoke_selection_test.go b/internal/usecase/smoke_selection_test.go index 6a1b39c..d0c939b 100644 --- a/internal/usecase/smoke_selection_test.go +++ b/internal/usecase/smoke_selection_test.go @@ -88,7 +88,7 @@ func TestResolveSmokeTestTargetSelectionAndRouting(t *testing.T) { { name: "prompt default", request: domain.SmokeTestRequest{ - Kind: domain.SmokeTestTextInference, PromptID: "selection.prompt", PromptVersion: "v1", + Kind: domain.SmokeTestTextInference, PromptID: "selection.prompt", }, profileID: "prompt-profile", backendID: "beta", @@ -100,7 +100,7 @@ func TestResolveSmokeTestTargetSelectionAndRouting(t *testing.T) { { name: "explicit profile overrides prompt default", 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", @@ -138,7 +138,7 @@ func TestResolveSmokeTestTargetSelectionAndRouting(t *testing.T) { { name: "prompt profile supplies backend-only target over backend hint", request: domain.SmokeTestRequest{ - Kind: domain.SmokeTestReachability, PromptID: "selection.prompt", PromptVersion: "v1", BackendID: "alpha", + Kind: domain.SmokeTestReachability, PromptID: "selection.prompt", BackendID: "alpha", }, profileID: "prompt-profile", backendID: "beta", @@ -150,7 +150,7 @@ func TestResolveSmokeTestTargetSelectionAndRouting(t *testing.T) { { name: "backend-only probe uses backend when prompt has no profile default", request: domain.SmokeTestRequest{ - Kind: domain.SmokeTestReachability, PromptID: "backend.prompt", PromptVersion: "v1", BackendID: "delta", + Kind: domain.SmokeTestReachability, PromptID: "backend.prompt", BackendID: "delta", }, backendID: "delta", selectionSource: smokeSelectionSourceBackend, @@ -171,7 +171,7 @@ func TestResolveSmokeTestTargetSelectionAndRouting(t *testing.T) { selected.target.EffectiveSettings.Model != test.wantModel { 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) } }) @@ -276,13 +276,6 @@ func TestResolveSmokeTestTargetSelectionAndSettingsErrors(t *testing.T) { }, 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", request: domain.SmokeTestRequest{Kind: domain.SmokeTestTextInference, BackendID: "delta"}, diff --git a/listing.go b/listing.go index a38115f..ed83faf 100644 --- a/listing.go +++ b/listing.go @@ -5,9 +5,9 @@ import ( "fmt" ) -// PromptIdentity identifies one prompt ID and exact version in the effective -// configuration captured when an Engine is constructed. It has no stable JSON -// representation. +// PromptIdentity identifies one prompt ID and its optional descriptive version +// label in the effective configuration captured when an Engine is constructed. +// It has no stable JSON representation. type PromptIdentity struct { ID string Version string @@ -36,9 +36,9 @@ func (e *Engine) ListProfiles(ctx context.Context) ([]string, error) { return profiles, nil } -// ListPrompts returns every prompt ID and exact version in the effective -// configuration captured when the engine was constructed, sorted lexically by -// ID and then version. Versions are not interpreted or prioritized. The +// ListPrompts returns every prompt ID in the effective configuration captured +// when the engine was constructed, sorted lexically by ID. Each identity may +// include its descriptive version label. The // returned slice is caller-owned and safe to mutate while the engine is used // concurrently. Empty inventories succeed. // diff --git a/listing_contract_test.go b/listing_contract_test.go index 588d74b..4a8be7b 100644 --- a/listing_contract_test.go +++ b/listing_contract_test.go @@ -53,7 +53,7 @@ func TestListIdentitiesBasicContract(t *testing.T) { if err != nil || profile == nil || profile.ProfileID != "configured-profile" { 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 { 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) 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) return } diff --git a/requirements.go b/requirements.go index aa4487d..f27e9d5 100644 --- a/requirements.go +++ b/requirements.go @@ -12,9 +12,6 @@ import ( type RequirementInspectionRequest struct { // PromptID is the exact, case-sensitive configured prompt ID. 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 // prompt's default profile is used; the profile need not be ready for // execution. @@ -131,7 +128,7 @@ type RequirementIssue struct { type RequirementInspection struct { // PromptID is the selected exact prompt ID. PromptID string - // PromptVersion is the selected exact prompt version. + // PromptVersion is the selected definition's descriptive version label. PromptVersion string // ProfileID is the selected profile identity; it does not indicate // execution readiness. @@ -189,7 +186,7 @@ func (e *Engine) InspectRequirements( return nil, ErrInvalidConfig } 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, }) if err != nil { diff --git a/requirements_inspection_contract_test.go b/requirements_inspection_contract_test.go index 97a5f88..79f287e 100644 --- a/requirements_inspection_contract_test.go +++ b/requirements_inspection_contract_test.go @@ -184,12 +184,12 @@ func TestInspectPromptHashMatchesPreparationForCapturedConfiguration(t *testing. engine := requirementsEngine(t, prompts, map[string]any{ "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 { t.Fatalf("inspect captured prompt: %v", err) } 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 { t.Fatalf("prepare captured prompt: %v", err) diff --git a/smoke_diagnostics.go b/smoke_diagnostics.go index 980eec7..770ff81 100644 --- a/smoke_diagnostics.go +++ b/smoke_diagnostics.go @@ -38,9 +38,6 @@ type SmokeTestRequest struct { // PromptID selects the prompt's default profile when ProfileID is empty. The // prompt's messages, inputs, templates, and output contract are not executed. 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 // default profile and a backend's configured diagnostic default. ProfileID string @@ -68,11 +65,10 @@ func (r SmokeTestRequest) redactedString() string { retries = fmt.Sprintf("%d", *r.TransientRetries) } 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.BackendID != "", r.PromptID != "", - r.PromptVersion != "", r.ProfileID != "", r.APIKey != "", retries, @@ -119,7 +115,8 @@ type SmokeTestTarget struct { BackendID string // ProfileID is the selected profile, if any. 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 PromptVersion string // DefaultSourceBackendID identifies the backend whose configured default @@ -228,7 +225,7 @@ const ( // configuration. // // 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 // its backend metadata; otherwise BackendID selects a backend directly without // 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), BackendID: request.BackendID, PromptID: request.PromptID, - PromptVersion: request.PromptVersion, ProfileID: request.ProfileID, Execution: execution, APIKey: request.APIKey, diff --git a/smoke_integration_contract_test.go b/smoke_integration_contract_test.go index f1350d4..061e03b 100644 --- a/smoke_integration_contract_test.go +++ b/smoke_integration_contract_test.go @@ -279,7 +279,7 @@ func TestSmokeTestUsesPrivateInferenceWithoutApplicationResourcesOrListingChange if err != nil { t.Fatal(err) } - inspectionBefore, err := engine.InspectPrompt(context.Background(), "application-prompt", "") + inspectionBefore, err := engine.InspectPrompt(context.Background(), "application-prompt") if err != nil { t.Fatal(err) } @@ -330,7 +330,7 @@ func TestSmokeTestUsesPrivateInferenceWithoutApplicationResourcesOrListingChange if err != nil { t.Fatal(err) } - inspectionAfter, err := engine.InspectPrompt(context.Background(), "application-prompt", "") + inspectionAfter, err := engine.InspectPrompt(context.Background(), "application-prompt") if err != nil { t.Fatal(err) } diff --git a/types.go b/types.go index e6f2cf2..07ca25e 100644 --- a/types.go +++ b/types.go @@ -87,9 +87,6 @@ const ( type RunRequest struct { // PromptID is the required non-empty prompt identifier. 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 // profile is used; if both are empty, the error matches ErrProfileRequired // and ErrInvalidRequest. @@ -162,7 +159,7 @@ type RunRequest struct { type PreparedRun struct { // PromptID is the selected prompt identifier. 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"` // PromptHash is an opaque equality value for the selected definition. PromptHash string `json:"prompt_hash,omitempty"` @@ -220,7 +217,7 @@ type RunResult struct { Validation ValidationResult `json:"validation"` // PromptID is the selected prompt identifier. 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"` // PromptHash is the same opaque definition equality value exposed by // PreparedRun. @@ -403,7 +400,7 @@ type PromptInputDefinition struct { type PromptInspection struct { // PromptID is the normalized ID of the selected prompt definition. PromptID string - // PromptVersion is the normalized version of the selected prompt definition. + // PromptVersion is the selected definition's descriptive version label. PromptVersion string // PromptHash is the opaque equality value for the selected definition. PromptHash string