Reconcile prompt execution provenance
This commit is contained in:
@@ -3,6 +3,7 @@ package app
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/generatedtext"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/promptdebug"
|
||||
@@ -49,10 +50,22 @@ func executePreparedProfile(ctx context.Context, req profileExecutionRequest) (p
|
||||
if req.Executor == nil {
|
||||
return outcome, nil, &profileExecutionError{operation: "execute prompt", err: promptexec.NewError(promptexec.InvalidConfiguration, "prompt executor is required", nil)}
|
||||
}
|
||||
if err := validatePreparedExecutionRequest(req); err != nil {
|
||||
return outcome, nil, &profileExecutionError{operation: "validate prompt provenance", err: err}
|
||||
}
|
||||
|
||||
callbackFailed := false
|
||||
preparationCallback := func(preparation promptexec.Preparation, debug *promptexec.PreparationDebug) error {
|
||||
outcome.ProfileID, outcome.BackendID, outcome.ModelName = preparation.ProfileID, preparation.BackendID, preparation.ModelName
|
||||
preparationCount := 0
|
||||
var preparation promptexec.Preparation
|
||||
preparationCallback := func(value promptexec.Preparation, debug *promptexec.PreparationDebug) error {
|
||||
preparationCount++
|
||||
if preparationCount != 1 {
|
||||
return promptProvenanceError()
|
||||
}
|
||||
if err := validatePreparationProvenance(req, value); err != nil {
|
||||
return err
|
||||
}
|
||||
preparation = clonePreparation(value)
|
||||
if req.DebugWriter == nil || !req.DebugWriter.Enabled() {
|
||||
return nil
|
||||
}
|
||||
@@ -60,7 +73,7 @@ func executePreparedProfile(ctx context.Context, req profileExecutionRequest) (p
|
||||
callbackFailed = true
|
||||
return promptDebugWriteError(fmt.Errorf("prompt debug reference is required"))
|
||||
}
|
||||
path, err := req.DebugWriter.WritePreparation(*req.DebugRef, preparation, debug)
|
||||
path, err := req.DebugWriter.WritePreparation(*req.DebugRef, value, debug)
|
||||
if err != nil {
|
||||
callbackFailed = true
|
||||
return promptDebugWriteError(err)
|
||||
@@ -86,7 +99,12 @@ func executePreparedProfile(ctx context.Context, req profileExecutionRequest) (p
|
||||
if execution == nil {
|
||||
return outcome, nil, &profileExecutionError{operation: "execute prompt", err: promptexec.NewError(promptexec.Generation, "prompt executor returned no execution", nil)}
|
||||
}
|
||||
|
||||
if preparationCount != 1 {
|
||||
return outcome, nil, &profileExecutionError{operation: "validate prompt provenance", err: promptProvenanceError()}
|
||||
}
|
||||
if err := validateExecutionProvenance(req, preparation, *execution); err != nil {
|
||||
return outcome, nil, &profileExecutionError{operation: "validate prompt provenance", err: err}
|
||||
}
|
||||
outcome.ValidationStatus = execution.Validation.Status
|
||||
if err := generatedtext.ValidateRawOutput(execution.RawOutput); err != nil {
|
||||
return outcome, nil, &profileExecutionError{operation: "validate generated text", err: err}
|
||||
@@ -129,3 +147,54 @@ func executePreparedProfile(ctx context.Context, req profileExecutionRequest) (p
|
||||
}
|
||||
return outcome, rendered, nil
|
||||
}
|
||||
|
||||
func validatePreparedExecutionRequest(req profileExecutionRequest) error {
|
||||
definition := req.Prepared.resolved.Definition
|
||||
if definition.PromptID != req.Prompt.PromptID || definition.PromptVersion != req.Prompt.PromptVersion ||
|
||||
definition.GeneratedTextSchemaID != req.Prepared.handler.SchemaID() {
|
||||
return promptProvenanceError()
|
||||
}
|
||||
if req.Prompt.ProfileID != "" && (req.Prompt.ProfileID != req.Profile.ProfileID || req.Prompt.BackendID != req.Profile.BackendID || req.Prompt.ModelName != req.Profile.ModelName) {
|
||||
return promptProvenanceError()
|
||||
}
|
||||
if req.Prompt.PromptHash == "" || req.Profile.ProfileID == "" || req.Profile.BackendID == "" || req.Profile.ModelName == "" {
|
||||
return promptProvenanceError()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validatePreparationProvenance(req profileExecutionRequest, preparation promptexec.Preparation) error {
|
||||
definition := req.Prepared.resolved.Definition
|
||||
if preparation.PromptID != req.Prompt.PromptID || preparation.PromptVersion != req.Prompt.PromptVersion || preparation.PromptHash != req.Prompt.PromptHash ||
|
||||
preparation.ProfileID != req.Profile.ProfileID || preparation.BackendID != req.Profile.BackendID || preparation.ModelName != req.Profile.ModelName ||
|
||||
!validPromptOutput(definition, preparation.Output) {
|
||||
return promptProvenanceError()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateExecutionProvenance(req profileExecutionRequest, preparation promptexec.Preparation, execution promptexec.Execution) error {
|
||||
definition := req.Prepared.resolved.Definition
|
||||
if execution.PromptID != preparation.PromptID || execution.PromptVersion != preparation.PromptVersion || execution.PromptHash != preparation.PromptHash ||
|
||||
execution.RenderedPromptHash != preparation.RenderedPromptHash || !reflect.DeepEqual(execution.InputHashes, preparation.InputHashes) ||
|
||||
execution.ProfileID != preparation.ProfileID || execution.BackendID != preparation.BackendID || execution.ModelName != preparation.ModelName ||
|
||||
execution.Validation.Mode != "json_schema" || execution.Validation.SchemaPath != definition.GeneratedTextSchemaID+".generated_text.schema.json" {
|
||||
return promptProvenanceError()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func promptProvenanceError() error {
|
||||
return promptexec.NewError(promptexec.InvalidConfiguration, "prompt execution provenance is inconsistent", nil)
|
||||
}
|
||||
|
||||
func clonePreparation(value promptexec.Preparation) promptexec.Preparation {
|
||||
if value.InputHashes != nil {
|
||||
inputHashes := make(map[string]string, len(value.InputHashes))
|
||||
for name, hash := range value.InputHashes {
|
||||
inputHashes[name] = hash
|
||||
}
|
||||
value.InputHashes = inputHashes
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user