diff --git a/bundled_composition_contract_test.go b/bundled_composition_contract_test.go index 659a524..8b47b04 100644 --- a/bundled_composition_contract_test.go +++ b/bundled_composition_contract_test.go @@ -19,7 +19,7 @@ func TestBundledCompositionReplacementAndInheritance(t *testing.T) { t.Fatal(err) } underlayProfile, err := underlayEngine.InspectProfile(context.Background(), "gpt-sol-latest") - if err != nil || underlayProfile.EffectiveModelParams.Model != "underlay-model" || underlayProfile.EffectiveModelParams.BackendID != "" { + if err != nil || underlayProfile.Model == nil || *underlayProfile.Model != "underlay-model" || underlayProfile.Backend != nil { t.Fatalf("underlay replacement = %+v, %v", underlayProfile, err) } main := configkit.Input{Source: configkit.Mapping("caller main", map[string]any{ @@ -38,29 +38,29 @@ func TestBundledCompositionReplacementAndInheritance(t *testing.T) { if err != nil { t.Fatal(err) } - inspect := func(id string) promptkit.ExecutionTarget { + inspect := func(id string) *promptkit.ProfileInspection { t.Helper() profile, err := engine.InspectProfile(context.Background(), id) if err != nil { t.Fatal(err) } - return profile.EffectiveModelParams + return profile } - if target := inspect("gpt-sol-latest"); target.Model != "main-model" || target.BackendID != "" { + if target := inspect("gpt-sol-latest"); target.Model == nil || *target.Model != "main-model" || target.Backend != nil { t.Fatalf("replacement = %+v", target) } - if target := inspect("custom"); target.Model != "custom-model" || target.BackendID != "openrouter" || target.Endpoint != "https://local.invalid/v1" { + if target := inspect("custom"); target.Model == nil || *target.Model != "custom-model" || target.Backend == nil || *target.Backend != "openrouter" || target.Endpoint != nil { t.Fatalf("inherited = %+v", target) } - if target := inspect("gpt-luna-latest"); target.Endpoint != "https://local.invalid/v1" || target.APIKeyEnv != "" { - t.Fatalf("backend replacement = %+v", target) + if backend, err := engine.InspectBackend(context.Background(), "openrouter"); err != nil || backend.Endpoint != "https://local.invalid/v1" || backend.APIKeyEnv != nil { + t.Fatalf("backend replacement = %+v, %v", backend, err) } - if target := inspect("openrouter/profile"); target.BackendID != "openrouter/local" || target.Model != "collection-model" { + if target := inspect("openrouter/profile"); target.Backend == nil || *target.Backend != "openrouter/local" || target.Model == nil || *target.Model != "collection-model" { t.Fatalf("collection = %+v", target) } target := inspect("auto-low") wantPlugins := map[string]any{"plugins": []any{map[string]any{"id": "auto-router", "cost_tier": "low"}}} - if target.BackendID != "other" || target.Model != "other-model" || !reflect.DeepEqual(target.ExtraParams, wantPlugins) { + if target.Backend == nil || *target.Backend != "other" || target.Model == nil || *target.Model != "other-model" || target.ExtraParams == nil || !reflect.DeepEqual(*target.ExtraParams, wantPlugins) { t.Errorf("inherited tier = %+v", target) } } diff --git a/bundled_openrouter_contract_test.go b/bundled_openrouter_contract_test.go index ed64c04..2ee9df4 100644 --- a/bundled_openrouter_contract_test.go +++ b/bundled_openrouter_contract_test.go @@ -59,7 +59,7 @@ func TestBundledOpenRouterOptionsAndInventory(t *testing.T) { t.Fatalf("bundle inventory: %v %v", profiles, backends) } profile, err := engine.InspectProfile(context.Background(), "auto") - if err != nil || profile.EffectiveModelParams.BackendID != "openrouter" { + if err != nil || profile.Backend == nil || *profile.Backend != "openrouter" { t.Fatalf("bundle profile = %+v, %v", profile, err) } view, err := engine.InspectConfiguration() diff --git a/configuration_sources_test.go b/configuration_sources_test.go index da476fd..5350d5b 100644 --- a/configuration_sources_test.go +++ b/configuration_sources_test.go @@ -58,7 +58,7 @@ func TestCollectionInputLoadsNativeImportsAndLaterOverride(t *testing.T) { t.Fatalf("construct engine with native collection import: %v", err) } inspection, err := engine.InspectPrompt(context.Background(), "docs/greeting") - if err != nil || inspection.PromptID != "docs/greeting" || len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "overridden" { + if err != nil || inspection.PromptID != "docs/greeting" || inspection.Inputs == nil || len(*inspection.Inputs) != 1 || (*inspection.Inputs)[0].Name != "overridden" { t.Fatalf("inspect imported and overlaid prompt = (%+v, %v)", inspection, err) } } @@ -85,7 +85,7 @@ func TestCollectionInputLoadsEmbeddedImportsAndLaterOverride(t *testing.T) { t.Fatalf("construct engine with embedded collection import: %v", err) } inspection, err := engine.InspectPrompt(context.Background(), "docs/greeting") - if err != nil || inspection.PromptID != "docs/greeting" || len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "overridden" { + if err != nil || inspection.PromptID != "docs/greeting" || inspection.Inputs == nil || len(*inspection.Inputs) != 1 || (*inspection.Inputs)[0].Name != "overridden" { t.Fatalf("inspect imported and overlaid prompt = (%+v, %v)", inspection, err) } } diff --git a/construction_contract_test.go b/construction_contract_test.go index bf73475..74e7095 100644 --- a/construction_contract_test.go +++ b/construction_contract_test.go @@ -134,7 +134,7 @@ func TestFormerCatalogBackendIDsWorkWhenConfigured(t *testing.T) { "rakestrawhome-profile": rakestrawhomeID, } { inspection, err := engine.InspectProfile(context.Background(), profileID) - if err != nil || inspection.EffectiveModelParams.BackendID != backendID { + if err != nil || inspection.Backend == nil || *inspection.Backend != backendID { t.Fatalf("inspect %q = (%+v, %v), want backend %q", profileID, inspection, err, backendID) } } @@ -242,8 +242,9 @@ func TestPublicConstructionUsesProgrammaticBackendAndProfileSources(t *testing.T if err != nil { t.Fatalf("inspect configured profile: %v", err) } - if inspection.EffectiveModelParams.Endpoint != endpoint { - t.Fatalf("effective endpoint = %q, want %q", inspection.EffectiveModelParams.Endpoint, endpoint) + backendInspection, err := engine.InspectBackend(context.Background(), backendID) + if err != nil || inspection.Endpoint != nil || backendInspection.Endpoint != endpoint { + t.Fatalf("profile/backend = %+v / %+v, %v", inspection, backendInspection, err) } } diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 4923c35..00731a8 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -376,6 +376,10 @@ cancellation remains detectable through `errors.Is`. ## Stage 6: Retire Superseded Inspection Paths And Migrate Existing Tests +**Completed:** Removed superseded runner inspection wrappers, migrated public +consumers and the preparation example, and retained target and requirement +coverage at their owning seams. The full ordinary `go test ./...` suite passes. + **Scope:** Existing consumers and test ownership, without losing execution or requirement coverage. diff --git a/examples/go-library/prepare/main.go b/examples/go-library/prepare/main.go index fe2b979..7fbb5de 100644 --- a/examples/go-library/prepare/main.go +++ b/examples/go-library/prepare/main.go @@ -110,10 +110,10 @@ func run(ctx context.Context, exampleDir string, out io.Writer) error { if err != nil { return err } - if profile.EffectiveModelParams.TimeoutSeconds != 30 { + if profile.TimeoutSeconds == nil || *profile.TimeoutSeconds != 30 { return fmt.Errorf( - "later profile override was not applied: timeout is %d seconds", - profile.EffectiveModelParams.TimeoutSeconds, + "later profile override was not applied: timeout is %v", + profile.TimeoutSeconds, ) } namespaces, err := scoped.ListNamespaces(ctx) @@ -156,7 +156,7 @@ func run(ctx context.Context, exampleDir string, out io.Writer) error { SelectedProfile: prepared.SelectedProfileID, Model: prepared.EffectiveModelParams.Model, MessageCount: len(prepared.Messages), - ProfileTimeout: profile.EffectiveModelParams.TimeoutSeconds, + ProfileTimeout: *profile.TimeoutSeconds, Namespaces: namespaces, Collections: collectionSummaries, ScopedPrompts: scopedPromptIDs, diff --git a/internal/domain/domain.go b/internal/domain/domain.go index 95b21d3..17e09a2 100644 --- a/internal/domain/domain.go +++ b/internal/domain/domain.go @@ -239,13 +239,6 @@ type ExecutionTarget struct { ExtraParams map[string]any `yaml:"extra_params" json:"extra_params"` } -// ProfileInspection is the resolved result of exact profile inspection. -type ProfileInspection struct { - ProfileID string - EffectiveModelParams ExecutionTarget - APIKeyRequired bool -} - // OutputContract defines the requirements for the output artifact. type OutputContract struct { Format OutputFormat `yaml:"format"` diff --git a/internal/usecase/profile_fixture_test.go b/internal/usecase/profile_fixture_test.go new file mode 100644 index 0000000..ae14e49 --- /dev/null +++ b/internal/usecase/profile_fixture_test.go @@ -0,0 +1,38 @@ +package usecase + +import ( + "context" + + "go.maximumdirect.net/promptkit/internal/domain" + "go.maximumdirect.net/promptkit/internal/jsonvalue" +) + +type inspectionProfileRepository struct { + profile *domain.ExecutionProfile + err error + calls int + id string +} + +func (r *inspectionProfileRepository) GetProfile(_ context.Context, id string) (*domain.ExecutionProfile, error) { + r.calls++ + r.id = id + if r.err != nil { + return nil, r.err + } + if r.profile == nil { + return nil, nil + } + profile := *r.profile + if profile.ResolvedTarget != nil { + target := *profile.ResolvedTarget + target.ExtraParams = jsonvalue.CloneTrusted(target.ExtraParams).(map[string]any) + profile.ResolvedTarget = &target + } + return &profile, nil +} + +func (r *inspectionProfileRepository) GetProfileIdentity(ctx context.Context, id string) (string, error) { + _, err := r.GetProfile(ctx, id) + return id, err +} diff --git a/internal/usecase/profile_inspection.go b/internal/usecase/profile_inspection.go index 1907053..e587bb6 100644 --- a/internal/usecase/profile_inspection.go +++ b/internal/usecase/profile_inspection.go @@ -85,30 +85,3 @@ func normalizeResolvedExecutionSettings(target domain.ExecutionTarget) (domain.E } return target, nil } - -// InspectProfile resolves one explicit profile without prompt or execution work. -func (r *Runner) InspectProfile( - ctx context.Context, - profileID string, -) (*domain.ProfileInspection, error) { - if strings.TrimSpace(profileID) == "" { - return nil, fmt.Errorf("%w: profile id is required", ErrInvalidRequest) - } - select { - case <-ctx.Done(): - return nil, &OperationError{Category: ErrProfileLoad, Cause: ctx.Err()} - default: - } - - selection, err := r.resolveProfileSelection(ctx, profileID) - if err != nil { - return nil, err - } - target, _ := resolveExecutionTarget(selection.profile, nil) - target.APIKey = "" - return &domain.ProfileInspection{ - ProfileID: selection.id, - EffectiveModelParams: target, - APIKeyRequired: target.APIKeyRequired, - }, nil -} diff --git a/internal/usecase/profile_inspection_test.go b/internal/usecase/profile_inspection_test.go deleted file mode 100644 index 9f8134b..0000000 --- a/internal/usecase/profile_inspection_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package usecase - -import ( - "context" - "errors" - "reflect" - "testing" - - "go.maximumdirect.net/promptkit/internal/domain" - "go.maximumdirect.net/promptkit/internal/jsonvalue" - "go.maximumdirect.net/promptkit/internal/profile" -) - -type inspectionProfileRepository struct { - profile *domain.ExecutionProfile - err error - calls int - id string -} - -func (r *inspectionProfileRepository) GetProfile(_ context.Context, id string) (*domain.ExecutionProfile, error) { - r.calls++ - r.id = id - if r.err != nil { - return nil, r.err - } - if r.profile == nil { - return nil, nil - } - profile := *r.profile - if profile.ResolvedTarget != nil { - target := *profile.ResolvedTarget - target.ExtraParams = jsonvalue.CloneTrusted(target.ExtraParams).(map[string]any) - profile.ResolvedTarget = &target - } - return &profile, nil -} - -func (r *inspectionProfileRepository) GetProfileIdentity(ctx context.Context, id string) (string, error) { - _, err := r.GetProfile(ctx, id) - return id, err -} - -func TestRunnerInspectProfileUsesExactSnapshotAndCopiesTarget(t *testing.T) { - configured := domain.ExecutionTarget{ - BackendID: "backend", Endpoint: "https://backend.example/v1", Model: "model", - Temperature: 0, APIKeyEnv: "OPTIONAL_KEY", ExtraParams: map[string]any{"nested": map[string]any{"value": "original"}}, - } - profiles := &inspectionProfileRepository{profile: &domain.ExecutionProfile{ - ID: " literal ", ResolvedTarget: &configured, - TargetPresence: domain.ExecutionTargetPresence{Temperature: true}, - }} - runner := &Runner{profiles: profiles} - first, err := runner.InspectProfile(context.Background(), " literal ") - if err != nil { - t.Fatalf("inspect exact ID: %v", err) - } - if profiles.calls != 1 || profiles.id != " literal " || first.ProfileID != profiles.id || !reflect.DeepEqual(first.EffectiveModelParams, configured) { - t.Fatalf("inspection=%+v lookup=(%d,%q), want exact snapshot", first, profiles.calls, profiles.id) - } - first.EffectiveModelParams.ExtraParams["nested"].(map[string]any)["value"] = "changed" - second, err := runner.InspectProfile(context.Background(), " literal ") - if err != nil || second.EffectiveModelParams.ExtraParams["nested"].(map[string]any)["value"] != "original" { - t.Fatalf("inspection mutated snapshot: (%+v, %v)", second, err) - } -} - -func TestRunnerInspectProfileDoesNotNeedExecutionCollaboratorsOrCredentials(t *testing.T) { - t.Setenv("PROMPTKIT_INSPECTION_TEST_KEY", "") - profiles := &inspectionProfileRepository{profile: &domain.ExecutionProfile{ - ID: "endpoint-only", ResolvedTarget: &domain.ExecutionTarget{ - Endpoint: "https://profile.example/v1", Model: "profile-model", APIKeyEnv: "PROMPTKIT_INSPECTION_TEST_KEY", - }, - }} - inspection, err := (&Runner{profiles: profiles}).InspectProfile(context.Background(), "endpoint-only") - if err != nil || inspection.EffectiveModelParams.APIKeyEnv != "PROMPTKIT_INSPECTION_TEST_KEY" || inspection.APIKeyRequired { - t.Fatalf("unexpected endpoint-only inspection: (%+v, %v)", inspection, err) - } -} - -func TestRunnerInspectProfileClassifiesLookupFailures(t *testing.T) { - profiles := &inspectionProfileRepository{} - runner := &Runner{profiles: profiles} - if _, err := runner.InspectProfile(context.Background(), " \t "); !errors.Is(err, ErrInvalidRequest) || profiles.calls != 0 { - t.Fatalf("blank inspection=(%v, calls=%d)", err, profiles.calls) - } - ctx, cancel := context.WithCancel(context.Background()) - cancel() - if _, err := runner.InspectProfile(ctx, "profile"); !errors.Is(err, ErrProfileLoad) || !errors.Is(err, context.Canceled) || profiles.calls != 0 { - t.Fatalf("canceled inspection=(%v, calls=%d)", err, profiles.calls) - } - profiles.err = profile.ErrProfileNotFound - if _, err := runner.InspectProfile(context.Background(), "missing"); !errors.Is(err, ErrProfileLoad) || !errors.Is(err, profile.ErrProfileNotFound) { - t.Fatalf("missing profile error=%v", err) - } - for _, invalid := range []*Runner{ - {}, - {profiles: &inspectionProfileRepository{}}, - {profiles: &inspectionProfileRepository{profile: &domain.ExecutionProfile{ID: "profile"}}}, - } { - if _, err := invalid.InspectProfile(context.Background(), "profile"); !errors.Is(err, ErrProfileLoad) { - t.Fatalf("invalid dependency error=%v", err) - } - } -} diff --git a/internal/usecase/prompt_inspection.go b/internal/usecase/prompt_inspection.go index 6019306..147be72 100644 --- a/internal/usecase/prompt_inspection.go +++ b/internal/usecase/prompt_inspection.go @@ -48,17 +48,3 @@ func (r *Runner) resolvePromptInspection(ctx context.Context, promptID string) ( } return inspection, nil } - -// InspectPrompt resolves one explicit prompt without execution work. -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) - } - select { - case <-ctx.Done(): - return nil, &OperationError{Category: ErrPromptLoad, Cause: ctx.Err()} - default: - } - - return r.resolvePromptInspection(ctx, promptID) -} diff --git a/internal/usecase/prompt_inspection_test.go b/internal/usecase/prompt_inspection_test.go deleted file mode 100644 index 5e21b33..0000000 --- a/internal/usecase/prompt_inspection_test.go +++ /dev/null @@ -1,218 +0,0 @@ -package usecase - -import ( - "context" - "errors" - "reflect" - "testing" - - "go.maximumdirect.net/promptkit/internal/domain" - "go.maximumdirect.net/promptkit/internal/prompt" - "go.maximumdirect.net/promptkit/internal/promptdef" -) - -type inspectionPromptRepository struct { - definition *domain.PromptDefinition - err error - calls int - id string -} - -type promptMetadataRepository struct { - inspection domain.PromptInspection - inspectionErr error - lookupID string -} - -func (r *inspectionPromptRepository) GetPromptSnapshot( - _ context.Context, - id string, -) (*domain.PromptDefinition, string, error) { - r.calls++ - r.id = id - if r.err != nil { - return nil, "", r.err - } - return r.definition, "captured-hash", nil -} - -func (r *inspectionPromptRepository) GetPromptInspection(ctx context.Context, id string) (*domain.PromptInspection, error) { - definition, hash, err := r.GetPromptSnapshot(ctx, id) - return fixturePromptInspection(definition, hash), err -} - -func (*inspectionPromptRepository) GetRequirementSnapshot(string) (prompt.RequirementSnapshot, bool) { - return prompt.RequirementSnapshot{}, false -} - -func (r *promptMetadataRepository) GetPromptSnapshot(context.Context, string) (*domain.PromptDefinition, string, error) { - return nil, "", errors.New("full prompt definition should not be requested") -} - -func (*promptMetadataRepository) GetRequirementSnapshot(string) (prompt.RequirementSnapshot, bool) { - return prompt.RequirementSnapshot{}, false -} - -func (r *promptMetadataRepository) GetPromptInspection( - _ context.Context, - id string, -) (*domain.PromptInspection, error) { - r.lookupID = id - inspection := r.inspection - inspection.Inputs = append([]domain.PromptInput(nil), inspection.Inputs...) - return &inspection, r.inspectionErr -} - -func TestRunnerInspectPromptResolvesOneDefinitionWithoutExecutionCollaborators(t *testing.T) { - definition := &domain.PromptDefinition{ - ID: "normalized.prompt", - DefaultProfile: "not-resolved", - Inputs: []domain.PromptInput{ - {Name: "document", Required: true, ContentType: "text/plain", Description: "Source document."}, - {Name: "audience", ContentType: "text/plain", Description: "Intended reader."}, - }, - Validation: domain.OutputContract{ - Format: domain.FormatJSON, - ValidationMode: domain.ValidationJSONSchema, - SchemaPath: "schemas/result.json", - RepairAttempts: 2, - }, - } - repository := &inspectionPromptRepository{definition: definition} - runner := &Runner{promptDefs: repository} - - inspection, err := runner.InspectPrompt(context.Background(), " prompt-id ") - if err != nil { - t.Fatalf("inspect prompt: %v", err) - } - 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.PromptHash != "captured-hash" || - inspection.DefaultProfileID != definition.DefaultProfile || - !reflect.DeepEqual(inspection.Inputs, definition.Inputs) || - inspection.OutputContract != definition.Validation { - t.Fatalf("inspection=%#v, want definition metadata", inspection) - } - - inspection.Inputs[0].Name = "changed" - second, err := runner.InspectPrompt(context.Background(), " prompt-id ") - if err != nil { - t.Fatalf("inspect prompt again: %v", err) - } - if definition.Inputs[0].Name != "document" || second.Inputs[0].Name != "document" { - t.Fatalf("inspection input mutation escaped caller result: definition=%#v next=%#v", definition.Inputs, second.Inputs) - } -} - -func TestRunnerInspectPromptUsesOwnedCapturedMetadata(t *testing.T) { - repository := &promptMetadataRepository{inspection: domain.PromptInspection{ - PromptID: "captured.prompt", PromptHash: "cached-prompt-hash", - DefaultProfileID: "partial-profile", - Inputs: []domain.PromptInput{{Name: "document", Required: true, ContentType: "text/plain", Description: "Captured."}}, - OutputContract: domain.OutputContract{Format: domain.FormatJSON, ValidationMode: domain.ValidationJSON}, - }} - runner := &Runner{promptDefs: repository} - inspection, err := runner.InspectPrompt(context.Background(), " requested-id ") - if err != nil || repository.lookupID != " requested-id " || - inspection.PromptID != "captured.prompt" || - 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", inspection, err, repository.lookupID) - } - inspection.Inputs[0].Name = "caller mutation" - inspection.OutputContract.SchemaPath = "caller mutation" - 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) - } -} - -func TestRunnerInspectPromptUsesCapturedHashAndMetadata(t *testing.T) { - repository := &requirementInspectionPromptRepository{ - definition: &domain.PromptDefinition{ - ID: "captured.prompt", Inputs: []domain.PromptInput{{Name: "document"}}, - }, - } - inspection, err := (&Runner{promptDefs: repository}).InspectPrompt(context.Background(), " requested-id ") - if err != nil || inspection.PromptID != "captured.prompt" || - inspection.PromptHash != "captured-hash" || inspection.Inputs[0].Name != "document" { - t.Fatalf("captured inspection=(%+v, %v)", inspection, err) - } -} - -func TestRunnerInspectPromptClassifiesFailuresWithoutRepositoryWorkAfterCancellation(t *testing.T) { - t.Run("blank ID", func(t *testing.T) { - repository := &inspectionPromptRepository{} - _, 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) - } - }) - - t.Run("canceled context", func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - cancel() - repository := &inspectionPromptRepository{} - _, 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) - } - }) - - t.Run("missing prompt", func(t *testing.T) { - repository := &inspectionPromptRepository{err: promptdef.ErrPromptDefinitionNotFound} - _, 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) - } - }) - - t.Run("defensive prompt dependencies", func(t *testing.T) { - var nilRunner *Runner - cases := []struct { - name string - runner *Runner - }{ - {name: "nil runner", runner: nilRunner}, - {name: "nil repository", runner: &Runner{}}, - {name: "nil definition", runner: &Runner{promptDefs: &inspectionPromptRepository{}}}, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - _, err := tc.runner.InspectPrompt(context.Background(), "prompt") - if !errors.Is(err, ErrPromptLoad) { - t.Fatalf("inspection error=%v, want prompt load", err) - } - }) - } - }) -} - -func TestRunnerPrepareUsesThePromptInspectionSelectionAndHash(t *testing.T) { - definition := promptDef(domain.FormatMarkdown, domain.ValidationBasic, 0) - repository := &fakePromptRepo{def: definition} - runner := NewRunner( - repository, - &fakeExecutionProfileRepo{profiles: map[string]*domain.ExecutionProfile{"exec": defaultExecutionProfile()}}, - - &fakeArtifactReader{}, - &fakeRenderer{rendered: &domain.RenderedPrompt{Messages: []domain.RenderedMessage{{Role: "user", Content: "hello"}}}}, - nil, - nil, - nil, - ) - - 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, ProfileID: "exec"}) - if err != nil { - t.Fatalf("prepare prompt: %v", err) - } - if inspection.PromptHash != prepared.PromptHash { - t.Fatalf("inspection hash=%q, preparation hash=%q", inspection.PromptHash, prepared.PromptHash) - } -} diff --git a/internal/usecase/resolved_targets_test.go b/internal/usecase/resolved_targets_test.go index 94afb25..f61edfd 100644 --- a/internal/usecase/resolved_targets_test.go +++ b/internal/usecase/resolved_targets_test.go @@ -9,20 +9,6 @@ import ( "go.maximumdirect.net/promptkit/internal/domain" ) -func TestInspectProfileAllowsIncompleteResolvedTarget(t *testing.T) { - profile := &domain.ExecutionProfile{ID: "partial", ResolvedTarget: &domain.ExecutionTarget{ - TimeoutSeconds: defaults.ExecutionDefaultTimeoutSeconds, - }} - runner := &Runner{profiles: staticExecutionProfileRepo{profile: profile}} - inspection, err := runner.InspectProfile(context.Background(), "partial") - if err != nil { - t.Fatalf("partial profile inspection failed: %v", err) - } - if inspection.EffectiveModelParams.Endpoint != "" || inspection.EffectiveModelParams.Model != "" { - t.Fatalf("inspection invented a runnable target: %+v", inspection) - } -} - func TestPreparationCompletesResolvedTargetThroughRequestOverrides(t *testing.T) { profile := &domain.ExecutionProfile{ID: "partial", ResolvedTarget: &domain.ExecutionTarget{ TimeoutSeconds: defaults.ExecutionDefaultTimeoutSeconds, diff --git a/listing_contract_test.go b/listing_contract_test.go index f1c329a..d0b2678 100644 --- a/listing_contract_test.go +++ b/listing_contract_test.go @@ -438,11 +438,11 @@ func TestListIdentitiesUseEffectiveLayeredMembership(t *testing.T) { } overlay, err := engine.InspectProfile(context.Background(), "shared/overlay") - if err != nil || overlay == nil || overlay.EffectiveModelParams.Model != "effective-model" { + if err != nil || overlay == nil || overlay.Model == nil || *overlay.Model != "effective-model" { t.Fatalf("overlay profile did not resolve to its effective definition: (%+v, %v)", overlay, err) } child, err := engine.InspectProfile(context.Background(), "shared/child") - if err != nil || child == nil || child.EffectiveModelParams.Model != "base-model" { + if err != nil || child == nil || child.Model == nil || *child.Model != "base-model" { t.Fatalf("inherited child profile did not resolve its effective definition: (%+v, %v)", child, err) } diff --git a/profile_inheritance_contract_test.go b/profile_inheritance_contract_test.go index c9c2106..2337fc1 100644 --- a/profile_inheritance_contract_test.go +++ b/profile_inheritance_contract_test.go @@ -41,9 +41,8 @@ func TestProfileInheritanceConfiguredBaseAliasWorkflow(t *testing.T) { if err != nil { t.Fatalf("inspect alias: %v", err) } - if child.ProfileID != "child" || child.EffectiveModelParams.BackendID != base.EffectiveModelParams.BackendID || - child.EffectiveModelParams.Model != base.EffectiveModelParams.Model || - child.EffectiveModelParams.ReasoningEffort != reasoning || child.EffectiveModelParams.TimeoutSeconds != timeout { + if child.ProfileID != "child" || child.Backend != nil || base.Backend != nil || child.Model == nil || base.Model == nil || *child.Model != *base.Model || + child.ReasoningEffort == nil || *child.ReasoningEffort != reasoning || child.TimeoutSeconds == nil || *child.TimeoutSeconds != timeout { t.Fatalf("alias inspection = %+v, base = %+v", child, base) } prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{PromptID: "prepared"}) @@ -71,7 +70,7 @@ func TestProfileInheritanceYAMLAliasOfConfiguredProfile(t *testing.T) { t.Fatal(err) } inspection, err := engine.InspectProfile(context.Background(), "yaml-alias") - if err != nil || inspection.ProfileID != "yaml-alias" || inspection.EffectiveModelParams.Model == "" { + if err != nil || inspection.ProfileID != "yaml-alias" || inspection.Model == nil || *inspection.Model == "" { t.Fatalf("YAML alias inspection = (%+v, %v)", inspection, err) } } @@ -92,7 +91,7 @@ func TestProfileInheritanceRetainsRequiredCredentialBehavior(t *testing.T) { t.Fatal(err) } inspection, err := engine.InspectProfile(context.Background(), "credential-child") - if err != nil || !inspection.APIKeyRequired || inspection.EffectiveModelParams.APIKeyEnv != "" { + if err != nil || inspection.APIKeyRequired == nil || !*inspection.APIKeyRequired || inspection.APIKeyEnv != nil { t.Fatalf("credential inspection = (%+v, %v)", inspection, err) } } @@ -144,7 +143,7 @@ func TestProfileInheritanceFreezesPreparedExecution(t *testing.T) { t.Fatalf("prepared execution = (%+v, %v), model %q", result, err, client.model) } inspection, err := engine.InspectProfile(context.Background(), "child") - if err != nil || inspection.EffectiveModelParams.Model != "first-model" { + if err != nil || inspection.Model == nil || *inspection.Model != "first-model" { t.Fatalf("existing engine inspection = (%+v, %v)", inspection, err) } } diff --git a/profile_inspection_contract_test.go b/profile_inspection_contract_test.go index 159ada1..4e786f0 100644 --- a/profile_inspection_contract_test.go +++ b/profile_inspection_contract_test.go @@ -39,7 +39,7 @@ func TestInspectProfileUsesLiteralFileKeyForPreparation(t *testing.T) { if err != nil { t.Fatal(err) } - if inspected.ProfileID != " literal " || prepared.SelectedProfileID != inspected.ProfileID || prepared.EffectiveModelParams.Model != inspected.EffectiveModelParams.Model { + if inspected.ProfileID != " literal " || inspected.Model == nil || prepared.SelectedProfileID != inspected.ProfileID || prepared.EffectiveModelParams.Model != *inspected.Model { t.Fatalf("inspection=%+v preparation=%+v", inspected, prepared) } if _, err := engine.InspectProfile(context.Background(), "literal"); !errors.Is(err, promptkit.ErrProfileNotFound) { @@ -61,12 +61,12 @@ func TestInspectProfileIsIndependentAndDoesNotInvokeProvider(t *testing.T) { t.Fatal(err) } first, err := engine.InspectProfile(context.Background(), "profile") - if err != nil || first == nil || !first.APIKeyRequired { + if err != nil || first == nil || first.APIKeyRequired == nil || !*first.APIKeyRequired || first.ExtraParams == nil { t.Fatalf("first inspection=(%+v, %v)", first, err) } - first.EffectiveModelParams.ExtraParams["nested"].(map[string]any)["value"] = "changed" + (*first.ExtraParams)["nested"].(map[string]any)["value"] = "changed" second, err := engine.InspectProfile(context.Background(), "profile") - if err != nil || second == nil || second.EffectiveModelParams.ExtraParams["nested"].(map[string]any)["value"] != "original" || len(client.requests) != 0 { + if err != nil || second == nil || second.ExtraParams == nil || (*second.ExtraParams)["nested"].(map[string]any)["value"] != "original" || len(client.requests) != 0 { t.Fatalf("inspection changed snapshot or invoked provider: (%+v, %v), calls=%d", second, err, len(client.requests)) } } @@ -94,7 +94,7 @@ func TestInspectProfilePublicErrorsAndPartialTarget(t *testing.T) { t.Fatalf("canceled inspection: %v", err) } partial, err := engine.InspectProfile(context.Background(), "profile") - if err != nil || partial == nil || partial.EffectiveModelParams.Endpoint != "" || partial.EffectiveModelParams.Model != "model" { + if err != nil || partial == nil || partial.Endpoint != nil || partial.Model == nil || *partial.Model != "model" { t.Fatalf("partial inspection=(%+v, %v)", partial, err) } } diff --git a/smoke_integration_contract_test.go b/smoke_integration_contract_test.go index 6188b5c..55a0610 100644 --- a/smoke_integration_contract_test.go +++ b/smoke_integration_contract_test.go @@ -422,7 +422,7 @@ func TestSmokeTestUsesPrivateInferenceWithoutApplicationResourcesOrListingChange t.Fatal(err) } if !reflect.DeepEqual(profilesBefore, profilesAfter) || !reflect.DeepEqual(promptsBefore, promptsAfter) || - inspectionBefore.PromptHash != inspectionAfter.PromptHash || inspectionAfter.OutputContract.SchemaPath != schemaMarker { + inspectionBefore.PromptHash != inspectionAfter.PromptHash || inspectionAfter.OutputContract.SchemaPath == nil || *inspectionAfter.OutputContract.SchemaPath != schemaMarker { t.Fatalf("diagnostic changed captured listings or application inspection: profiles %v -> %v, prompts %v -> %v, inspection %+v -> %+v", profilesBefore, profilesAfter, promptsBefore, promptsAfter, inspectionBefore, inspectionAfter) } diff --git a/sources_contract_test.go b/sources_contract_test.go index d376fef..ff69536 100644 --- a/sources_contract_test.go +++ b/sources_contract_test.go @@ -70,7 +70,7 @@ func TestProgrammaticProfileVariantInherits(t *testing.T) { t.Fatal(err) } profile, err := engine.InspectProfile(context.Background(), "child") - if err != nil || profile.EffectiveModelParams.Model != model || profile.EffectiveModelParams.Endpoint != endpoint || profile.EffectiveModelParams.TimeoutSeconds != timeout { + if err != nil || profile.Model == nil || *profile.Model != model || profile.Endpoint == nil || *profile.Endpoint != endpoint || profile.TimeoutSeconds == nil || *profile.TimeoutSeconds != timeout { t.Fatalf("inherited profile = %+v, %v", profile, err) } }