Carry optional prompt labels through results

This commit is contained in:
2026-10-17 05:54:32 +00:00
parent 8cbb7c0daf
commit ec3245c97e
40 changed files with 363 additions and 121 deletions

View File

@@ -76,7 +76,7 @@ func (r *Runner) resolvePromptInspection(ctx context.Context, promptID string) (
definition := selection.definition
return &domain.PromptInspection{
PromptID: definition.ID,
PromptVersion: definition.Version,
PromptVersion: copyStringPointer(definition.Version),
PromptHash: selection.hash,
DefaultProfileID: definition.DefaultProfile,
Inputs: append([]domain.PromptInput(nil), definition.Inputs...),

View File

@@ -52,7 +52,7 @@ func (r *promptMetadataRepository) GetPromptInspection(
func TestRunnerInspectPromptResolvesOneDefinitionWithoutExecutionCollaborators(t *testing.T) {
definition := &domain.PromptDefinition{
ID: "normalized.prompt",
Version: "1.2.3",
Version: stringPtr("1.2.3"),
DefaultProfile: "not-resolved",
Inputs: []domain.PromptInput{
{Name: "document", Required: true, ContentType: "text/plain", Description: "Source document."},
@@ -79,8 +79,8 @@ func TestRunnerInspectPromptResolvesOneDefinitionWithoutExecutionCollaborators(t
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 ||
if inspection.PromptID != definition.ID || inspection.PromptVersion == nil || definition.Version == nil ||
*inspection.PromptVersion != *definition.Version ||
inspection.PromptHash != wantHash ||
inspection.DefaultProfileID != definition.DefaultProfile ||
!reflect.DeepEqual(inspection.Inputs, definition.Inputs) ||
@@ -100,7 +100,7 @@ func TestRunnerInspectPromptResolvesOneDefinitionWithoutExecutionCollaborators(t
func TestRunnerInspectPromptUsesOwnedMetadataWhenRepositorySupportsIt(t *testing.T) {
repository := &promptMetadataRepository{inspection: domain.PromptInspection{
PromptID: "captured.prompt", PromptVersion: "v2", PromptHash: "cached-prompt-hash",
PromptID: "captured.prompt", PromptVersion: stringPtr("v2"), 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},
@@ -108,7 +108,7 @@ func TestRunnerInspectPromptUsesOwnedMetadataWhenRepositorySupportsIt(t *testing
runner := &Runner{promptDefs: repository}
inspection, err := runner.InspectPrompt(context.Background(), " requested-id ")
if err != nil || repository.lookupID != " requested-id " ||
inspection.PromptID != "captured.prompt" || inspection.PromptVersion != "v2" ||
inspection.PromptID != "captured.prompt" || inspection.PromptVersion == nil || *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", inspection, err, repository.lookupID)
@@ -124,11 +124,11 @@ func TestRunnerInspectPromptUsesOwnedMetadataWhenRepositorySupportsIt(t *testing
func TestRunnerInspectPromptUsesCapturedHashWhenMetadataCapabilityIsAbsent(t *testing.T) {
repository := &requirementInspectionPromptRepository{
definition: &domain.PromptDefinition{
ID: "captured.prompt", Version: "v1", Inputs: []domain.PromptInput{{Name: "document"}},
ID: "captured.prompt", Version: stringPtr("v1"), Inputs: []domain.PromptInput{{Name: "document"}},
},
}
inspection, err := (&Runner{promptDefs: repository}).InspectPrompt(context.Background(), " requested-id ")
if err != nil || inspection.PromptID != "captured.prompt" || inspection.PromptVersion != "v1" ||
if err != nil || inspection.PromptID != "captured.prompt" || inspection.PromptVersion == nil || *inspection.PromptVersion != "v1" ||
inspection.PromptHash != "captured-hash" || inspection.Inputs[0].Name != "document" {
t.Fatalf("captured-hash fallback inspection=(%+v, %v)", inspection, err)
}

View File

@@ -69,7 +69,7 @@ func (r *Runner) InspectRequirements(
if !ok {
return nil, fmt.Errorf("%w: captured requirement snapshot is unavailable", ErrPromptLoad)
}
definition := &domain.PromptDefinition{ID: selection.PromptID, Version: selection.PromptVersion}
definition := &domain.PromptDefinition{ID: selection.PromptID, Version: copyStringPointer(selection.PromptVersion)}
inspection, err := prompt.AnalyzeRequirements(ctx, definition, snapshot, req.Vars, directSessionID != "")
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrPromptLoad, err)

View File

@@ -88,7 +88,7 @@ func TestRunnerInspectRequirementsUsesCapturedSelectionWithoutExecutionWork(t *t
if err != nil {
t.Fatal(err)
}
definition := &domain.PromptDefinition{ID: "selected", Version: "v2", DefaultProfile: "default-profile"}
definition := &domain.PromptDefinition{ID: "selected", Version: stringPtr("v2"), DefaultProfile: "default-profile"}
repository := &requirementInspectionPromptRepository{
definition: definition,
snapshotFound: true,
@@ -113,7 +113,7 @@ func TestRunnerInspectRequirementsUsesCapturedSelectionWithoutExecutionWork(t *t
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 ||
inspection.PromptVersion == nil || *inspection.PromptVersion != *definition.Version || inspection.ProfileID != "partial-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("unexpected requirement inspection: %+v", inspection)
@@ -138,7 +138,7 @@ func TestRunnerInspectRequirementsUsesPromptMetadataAndExactRequirementSnapshot(
promptRepository := &requirementPromptMetadataRepository{
requirementInspectionPromptRepository: baseRepository,
inspection: domain.PromptInspection{
PromptID: "captured-id", PromptVersion: "captured-version", PromptHash: "captured-hash",
PromptID: "captured-id", PromptVersion: stringPtr("captured-version"), PromptHash: "captured-hash",
DefaultProfileID: "default-profile",
},
}
@@ -155,7 +155,7 @@ func TestRunnerInspectRequirementsUsesPromptMetadataAndExactRequirementSnapshot(
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.PromptVersion == nil || *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, snapshot=%q, profile=%q",
@@ -168,7 +168,7 @@ func TestRunnerInspectRequirementsUsesProfileIdentityCapabilityOrFallback(t *tes
if err != nil {
t.Fatal(err)
}
definition := &domain.PromptDefinition{ID: "selected", Version: "v1"}
definition := &domain.PromptDefinition{ID: "selected", Version: stringPtr("v1")}
makePromptRepository := func() *requirementInspectionPromptRepository {
return &requirementInspectionPromptRepository{
definition: definition, snapshotFound: true,
@@ -234,7 +234,7 @@ func TestRunnerInspectRequirementsUsesPromptDefaultAndIncludesSessionWhenNotBypa
if err != nil {
t.Fatal(err)
}
definition := &domain.PromptDefinition{ID: "selected", Version: "v1", DefaultProfile: "default-profile"}
definition := &domain.PromptDefinition{ID: "selected", Version: stringPtr("v1"), DefaultProfile: "default-profile"}
repository := &requirementInspectionPromptRepository{
definition: definition, snapshotFound: true,
snapshot: prompt.RequirementSnapshot{Templates: prompt.CompiledTemplates{Messages: []*template.Template{message}, Session: session}},
@@ -260,7 +260,7 @@ func TestRunnerInspectRequirementsReturnsIncompleteDiscoveryAsSuccess(t *testing
t.Fatal(err)
}
repository := &requirementInspectionPromptRepository{
definition: &domain.PromptDefinition{ID: "selected", Version: "v1", DefaultProfile: "profile"},
definition: &domain.PromptDefinition{ID: "selected", Version: stringPtr("v1"), DefaultProfile: "profile"},
snapshotFound: true,
snapshot: prompt.RequirementSnapshot{Templates: prompt.CompiledTemplates{Messages: []*template.Template{compiled}}},
}

View File

@@ -29,7 +29,7 @@ func TestPreparationCompletesResolvedTargetThroughRequestOverrides(t *testing.T)
Temperature: 0, ExtraParams: map[string]any{"configured": true},
}, TargetPresence: domain.ExecutionTargetPresence{Temperature: true}}
runner := &Runner{
promptDefs: staticPromptRepo{def: &domain.PromptDefinition{ID: "prompt", Version: "v1", Validation: domain.OutputContract{
promptDefs: staticPromptRepo{def: &domain.PromptDefinition{ID: "prompt", Version: stringPtr("v1"), Validation: domain.OutputContract{
Format: domain.FormatText, ValidationMode: domain.ValidationNone,
}}},
profiles: staticExecutionProfileRepo{profile: profile}, renderer: staticRenderer{},
@@ -63,7 +63,7 @@ func TestResolvedTargetCredentialAvailability(t *testing.T) {
Endpoint: "https://provider.example/v1", Model: "model", APIKeyRequired: true,
}}
runner := &Runner{
promptDefs: staticPromptRepo{def: &domain.PromptDefinition{ID: "prompt", Version: "v1", Validation: domain.OutputContract{
promptDefs: staticPromptRepo{def: &domain.PromptDefinition{ID: "prompt", Version: stringPtr("v1"), Validation: domain.OutputContract{
Format: domain.FormatText, ValidationMode: domain.ValidationNone,
}}},
profiles: staticExecutionProfileRepo{profile: profile}, renderer: staticRenderer{},

View File

@@ -252,7 +252,7 @@ func (r *Runner) executePreparedRun(
RawOutput: genResp.Content,
Validation: validationResult,
PromptID: prepared.PromptID,
PromptVersion: prepared.PromptVersion,
PromptVersion: copyStringPointer(prepared.PromptVersion),
PromptHash: prepared.PromptHash,
SessionID: prepared.SessionID,
RenderedPromptHash: prepared.RenderedPromptHash,
@@ -321,7 +321,7 @@ func (r *Runner) resolvePreparation(
}
def := promptSelection.definition
if recorder := observation.FromContext(ctx); recorder != nil {
recorder.UpdateSelection(observation.Selection{PromptID: def.ID, PromptVersion: def.Version})
recorder.UpdateSelection(observation.Selection{PromptID: def.ID, PromptVersion: copyStringPointer(def.Version)})
}
promptDefinitionHash := promptSelection.hash
effectiveContract, err := resolveOutputContract(def, req.Validation)
@@ -342,7 +342,7 @@ func (r *Runner) resolvePreparation(
return nil, err
}
if recorder := observation.FromContext(ctx); recorder != nil {
recorder.UpdateSelection(observation.Selection{PromptID: def.ID, PromptVersion: def.Version, ProfileID: selection.id})
recorder.UpdateSelection(observation.Selection{PromptID: def.ID, PromptVersion: copyStringPointer(def.Version), ProfileID: selection.id})
}
effectiveModel, targetPresence := resolveExecutionTarget(selection.profile, req.Execution)
@@ -355,7 +355,7 @@ func (r *Runner) resolvePreparation(
return nil, fmt.Errorf("%w: %w", ErrInvalidRequest, err)
}
if recorder := observation.FromContext(ctx); recorder != nil {
recorder.UpdateSelection(observation.Selection{PromptID: def.ID, PromptVersion: def.Version,
recorder.UpdateSelection(observation.Selection{PromptID: def.ID, PromptVersion: copyStringPointer(def.Version),
ProfileID: selection.id, BackendID: effectiveModel.BackendID, Model: effectiveModel.Model})
}
@@ -481,7 +481,7 @@ func (r *Runner) completePreparationWithStructuredOutput(
effectiveModel.APIKey = ""
return &domain.PreparedRun{
PromptID: state.definition.ID,
PromptVersion: state.definition.Version,
PromptVersion: copyStringPointer(state.definition.Version),
PromptHash: state.promptDefinitionHash,
SelectedProfileID: state.selectedProfileID,
SelectedBackendID: state.effectiveModel.BackendID,
@@ -713,3 +713,11 @@ func buildOutputArtifact(content string, format domain.OutputFormat) domain.Arti
func hashPromptDefinition(def *domain.PromptDefinition) (string, error) {
return promptdef.HashDefinition(def)
}
func copyStringPointer(value *string) *string {
if value == nil {
return nil
}
copy := *value
return &copy
}

View File

@@ -366,7 +366,7 @@ func TestRunnerPrepareWithExplicitProfileSelection(t *testing.T) {
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if prepared.PromptID != "p" || prepared.PromptVersion != "1" {
if prepared.PromptID != "p" || prepared.PromptVersion == nil || *prepared.PromptVersion != "1" {
t.Fatalf("unexpected prepared prompt metadata: %+v", prepared)
}
if prepared.SelectedProfileID != "exec" {
@@ -1257,7 +1257,7 @@ func TestRunnerRunSuccessful(t *testing.T) {
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if res.PromptID != "p" || res.PromptVersion != "1" {
if res.PromptID != "p" || res.PromptVersion == nil || *res.PromptVersion != "1" {
t.Fatalf("unexpected prompt metadata: %+v", res)
}
if res.SelectedProfileID != "exec" {
@@ -2504,7 +2504,7 @@ func TestBuildOutputArtifactDefaults(t *testing.T) {
func promptDef(format domain.OutputFormat, mode domain.ValidationMode, attempts int) *domain.PromptDefinition {
return &domain.PromptDefinition{
ID: "p",
Version: "1",
Version: stringPtr("1"),
DefaultProfile: "exec",
Inputs: []domain.PromptInput{{Name: "transcript", Required: true}},
Templates: []domain.PromptMessageTemplate{{Role: "user", Content: "x"}},