Refactor: split prompt definition from execution settings and migrate run contracts to prompt_* + execution_target
This commit is contained in:
@@ -21,7 +21,7 @@ import (
|
||||
|
||||
var (
|
||||
ErrInvalidRequest = errors.New("invalid run request")
|
||||
ErrProfileLoad = errors.New("failed to load profile")
|
||||
ErrProfileLoad = errors.New("failed to load prompt definition")
|
||||
ErrArtifactLoad = errors.New("failed to load artifact")
|
||||
ErrPromptRender = errors.New("failed to render prompt")
|
||||
ErrLLMGenerate = errors.New("failed to generate output")
|
||||
@@ -67,8 +67,8 @@ func NewRunnerWithRepairer(
|
||||
}
|
||||
|
||||
func (r *Runner) Run(ctx context.Context, req domain.RunRequest) (*domain.RunResult, error) {
|
||||
if strings.TrimSpace(req.ProfileID) == "" {
|
||||
return nil, fmt.Errorf("%w: profile id is required", ErrInvalidRequest)
|
||||
if strings.TrimSpace(req.PromptID) == "" {
|
||||
return nil, fmt.Errorf("%w: prompt id is required", ErrInvalidRequest)
|
||||
}
|
||||
|
||||
runID, err := newRunID()
|
||||
@@ -78,17 +78,32 @@ func (r *Runner) Run(ctx context.Context, req domain.RunRequest) (*domain.RunRes
|
||||
|
||||
start := time.Now().UTC()
|
||||
|
||||
prof, err := r.profiles.GetProfile(ctx, req.ProfileID, req.ProfileVersion)
|
||||
def, err := r.profiles.GetPromptDefinition(ctx, req.PromptID, req.PromptVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", ErrProfileLoad, err)
|
||||
}
|
||||
profileHash, err := hashProfile(prof)
|
||||
promptDefinitionHash, err := hashPromptDefinition(def)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: failed to hash profile: %v", ErrProfileLoad, err)
|
||||
return nil, fmt.Errorf("%w: failed to hash prompt definition: %v", ErrProfileLoad, err)
|
||||
}
|
||||
|
||||
effectiveModel := mergeModelTarget(prof.ModelDefaults, req.Model)
|
||||
effectiveContract := resolveOutputContract(prof, req.Validation)
|
||||
selectedProfileID := strings.TrimSpace(req.ProfileID)
|
||||
if selectedProfileID == "" {
|
||||
selectedProfileID = strings.TrimSpace(def.DefaultProfile)
|
||||
}
|
||||
if selectedProfileID == "" {
|
||||
return nil, fmt.Errorf("%w: profile id is required either in request or prompt default_profile", ErrInvalidRequest)
|
||||
}
|
||||
if req.Execution == nil {
|
||||
return nil, fmt.Errorf("%w: execution override is required until execution profile loading is implemented", ErrInvalidRequest)
|
||||
}
|
||||
effectiveModel := mergeExecutionTarget(domain.ExecutionTarget{}, req.Execution)
|
||||
if strings.TrimSpace(effectiveModel.Endpoint) == "" {
|
||||
return nil, fmt.Errorf("%w: execution endpoint is required", ErrInvalidRequest)
|
||||
}
|
||||
if strings.TrimSpace(effectiveModel.Model) == "" {
|
||||
return nil, fmt.Errorf("%w: execution model is required", ErrInvalidRequest)
|
||||
}
|
||||
effectiveContract := resolveOutputContract(def, req.Validation)
|
||||
|
||||
resolvedInputs := make(map[string]*domain.Artifact, len(req.Inputs))
|
||||
inputHashes := make(map[string]string, len(req.Inputs))
|
||||
@@ -104,12 +119,12 @@ func (r *Runner) Run(ctx context.Context, req domain.RunRequest) (*domain.RunRes
|
||||
inputHashes[name] = art.Hash
|
||||
}
|
||||
|
||||
renderedPrompt, err := r.renderer.Render(ctx, prof, resolvedInputs, req.Vars)
|
||||
renderedPrompt, err := r.renderer.Render(ctx, def, resolvedInputs, req.Vars)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", ErrPromptRender, err)
|
||||
}
|
||||
|
||||
promptHash := hashRenderedPrompt(*renderedPrompt)
|
||||
renderedPromptHash := hashRenderedPrompt(*renderedPrompt)
|
||||
|
||||
genResp, err := r.llm.Generate(ctx, domain.GenerateRequest{
|
||||
Prompt: *renderedPrompt,
|
||||
@@ -158,22 +173,23 @@ func (r *Runner) Run(ctx context.Context, req domain.RunRequest) (*domain.RunRes
|
||||
end := time.Now().UTC()
|
||||
|
||||
return &domain.RunResult{
|
||||
RunID: runID,
|
||||
Artifact: outputArtifact,
|
||||
RawOutput: genResp.Content,
|
||||
Validation: validationResult,
|
||||
ProfileID: prof.ID,
|
||||
ProfileVersion: prof.Version,
|
||||
ProfileHash: profileHash,
|
||||
ModelName: effectiveModel.Model,
|
||||
Endpoint: effectiveModel.Endpoint,
|
||||
ModelParams: effectiveModel,
|
||||
InputHashes: inputHashes,
|
||||
PromptHash: promptHash,
|
||||
Usage: genResp.Usage,
|
||||
StartTime: start,
|
||||
EndTime: end,
|
||||
Duration: end.Sub(start),
|
||||
RunID: runID,
|
||||
Artifact: outputArtifact,
|
||||
RawOutput: genResp.Content,
|
||||
Validation: validationResult,
|
||||
PromptID: def.ID,
|
||||
PromptVersion: def.Version,
|
||||
PromptHash: promptDefinitionHash,
|
||||
RenderedPromptHash: renderedPromptHash,
|
||||
SelectedProfileID: selectedProfileID,
|
||||
ModelName: effectiveModel.Model,
|
||||
Endpoint: effectiveModel.Endpoint,
|
||||
EffectiveModelParams: effectiveModel,
|
||||
InputHashes: inputHashes,
|
||||
Usage: genResp.Usage,
|
||||
StartTime: start,
|
||||
EndTime: end,
|
||||
Duration: end.Sub(start),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -209,7 +225,7 @@ func (r *Runner) shouldAttemptRepair(contract domain.OutputContract, validationR
|
||||
return contract.ValidationMode == domain.ValidationJSON || contract.ValidationMode == domain.ValidationJSONSchema
|
||||
}
|
||||
|
||||
func mergeModelTarget(base domain.ModelTarget, override *domain.ModelTarget) domain.ModelTarget {
|
||||
func mergeExecutionTarget(base domain.ExecutionTarget, override *domain.ExecutionTarget) domain.ExecutionTarget {
|
||||
if override == nil {
|
||||
return base
|
||||
}
|
||||
@@ -233,13 +249,26 @@ func mergeModelTarget(base domain.ModelTarget, override *domain.ModelTarget) dom
|
||||
if override.TimeoutSeconds != 0 {
|
||||
out.TimeoutSeconds = override.TimeoutSeconds
|
||||
}
|
||||
if strings.TrimSpace(override.ReasoningEffort) != "" {
|
||||
out.ReasoningEffort = override.ReasoningEffort
|
||||
}
|
||||
if strings.TrimSpace(override.APIKeyEnv) != "" {
|
||||
out.APIKeyEnv = override.APIKeyEnv
|
||||
}
|
||||
if len(override.ExtraParams) > 0 {
|
||||
cp := make(map[string]string, len(override.ExtraParams))
|
||||
for k, v := range override.ExtraParams {
|
||||
cp[k] = v
|
||||
}
|
||||
out.ExtraParams = cp
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func resolveOutputContract(prof *domain.PromptProfile, override *domain.OutputContract) domain.OutputContract {
|
||||
contract := prof.Validation
|
||||
func resolveOutputContract(def *domain.PromptDefinition, override *domain.OutputContract) domain.OutputContract {
|
||||
contract := def.Validation
|
||||
if contract.Format == "" {
|
||||
contract.Format = prof.OutputFormat
|
||||
contract.Format = def.OutputFormat
|
||||
}
|
||||
if override != nil {
|
||||
contract = *override
|
||||
@@ -283,8 +312,8 @@ func buildOutputArtifact(content string, format domain.OutputFormat) domain.Arti
|
||||
}
|
||||
}
|
||||
|
||||
func hashProfile(prof *domain.PromptProfile) (string, error) {
|
||||
b, err := json.Marshal(prof)
|
||||
func hashPromptDefinition(def *domain.PromptDefinition) (string, error) {
|
||||
b, err := json.Marshal(def)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user