Implement runtime parameter completion fixes
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
This commit is contained in:
@@ -92,9 +92,13 @@ func (r *Runner) Run(ctx context.Context, req domain.RunRequest) (*domain.RunRes
|
||||
genResp, err := r.llm.Generate(ctx, domain.GenerateRequest{
|
||||
Prompt: domain.RenderedPrompt{SessionID: prepared.SessionID, Messages: prepared.Messages},
|
||||
Target: prepared.EffectiveModelParams,
|
||||
TargetPresence: prepared.TargetPresence,
|
||||
StructuredOutput: prepared.StructuredOutput,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, llm.ErrInvalidRequest) {
|
||||
return nil, fmt.Errorf("%w: %w", ErrInvalidRequest, err)
|
||||
}
|
||||
return nil, fmt.Errorf("%w: %w", ErrLLMGenerate, err)
|
||||
}
|
||||
|
||||
@@ -187,7 +191,7 @@ func (r *Runner) Prepare(ctx context.Context, req domain.RunRequest) (*domain.Pr
|
||||
return nil, fmt.Errorf("%w: %w", ErrProfileLoad, err)
|
||||
}
|
||||
|
||||
effectiveModel, err := resolveExecutionTarget(execProfile, req.Execution)
|
||||
effectiveModel, targetPresence, err := resolveExecutionTarget(execProfile, req.Execution)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", ErrInvalidRequest, err)
|
||||
}
|
||||
@@ -233,6 +237,7 @@ func (r *Runner) Prepare(ctx context.Context, req domain.RunRequest) (*domain.Pr
|
||||
PromptHash: promptDefinitionHash,
|
||||
SelectedProfileID: selectedProfileID,
|
||||
EffectiveModelParams: effectiveModel,
|
||||
TargetPresence: targetPresence,
|
||||
OutputContract: effectiveContract,
|
||||
StructuredOutput: structuredOutput,
|
||||
InputHashes: inputHashes,
|
||||
@@ -363,8 +368,9 @@ func mergeExecutionTarget(base domain.ExecutionTarget, override domain.Execution
|
||||
return out
|
||||
}
|
||||
|
||||
func mergeExecutionTargetOverride(base domain.ExecutionTarget, override domain.ExecutionTargetOverride) (domain.ExecutionTarget, error) {
|
||||
func mergeExecutionTargetOverride(base domain.ExecutionTarget, override domain.ExecutionTargetOverride) (domain.ExecutionTarget, domain.ExecutionTargetPresence, error) {
|
||||
out := base
|
||||
var presence domain.ExecutionTargetPresence
|
||||
if override.Endpoint != "" {
|
||||
out.Endpoint = override.Endpoint
|
||||
}
|
||||
@@ -373,27 +379,31 @@ func mergeExecutionTargetOverride(base domain.ExecutionTarget, override domain.E
|
||||
}
|
||||
if override.Temperature != nil {
|
||||
if *override.Temperature < 0 || *override.Temperature > 2 {
|
||||
return domain.ExecutionTarget{}, errors.New("temperature must be between 0 and 2")
|
||||
return domain.ExecutionTarget{}, domain.ExecutionTargetPresence{}, errors.New("temperature must be between 0 and 2")
|
||||
}
|
||||
out.Temperature = *override.Temperature
|
||||
presence.Temperature = true
|
||||
}
|
||||
if override.MaxTokens != nil {
|
||||
if *override.MaxTokens < 0 {
|
||||
return domain.ExecutionTarget{}, errors.New("max_tokens must be greater than or equal to 0")
|
||||
return domain.ExecutionTarget{}, domain.ExecutionTargetPresence{}, errors.New("max_tokens must be greater than or equal to 0")
|
||||
}
|
||||
out.MaxTokens = *override.MaxTokens
|
||||
presence.MaxTokens = true
|
||||
}
|
||||
if override.TopP != nil {
|
||||
if *override.TopP < 0 || *override.TopP > 1 {
|
||||
return domain.ExecutionTarget{}, errors.New("top_p must be between 0 and 1")
|
||||
return domain.ExecutionTarget{}, domain.ExecutionTargetPresence{}, errors.New("top_p must be between 0 and 1")
|
||||
}
|
||||
out.TopP = *override.TopP
|
||||
presence.TopP = true
|
||||
}
|
||||
if override.TimeoutSeconds != nil {
|
||||
if *override.TimeoutSeconds < 0 {
|
||||
return domain.ExecutionTarget{}, errors.New("timeout_seconds must be greater than or equal to 0")
|
||||
return domain.ExecutionTarget{}, domain.ExecutionTargetPresence{}, errors.New("timeout_seconds must be greater than or equal to 0")
|
||||
}
|
||||
out.TimeoutSeconds = *override.TimeoutSeconds
|
||||
presence.TimeoutSeconds = true
|
||||
}
|
||||
if strings.TrimSpace(override.ServiceTier) != "" {
|
||||
out.ServiceTier = override.ServiceTier
|
||||
@@ -407,20 +417,21 @@ func mergeExecutionTargetOverride(base domain.ExecutionTarget, override domain.E
|
||||
if len(override.ExtraParams) > 0 {
|
||||
out.ExtraParams = copyExtraParams(override.ExtraParams)
|
||||
}
|
||||
return out, nil
|
||||
return out, presence, nil
|
||||
}
|
||||
|
||||
func resolveExecutionTarget(profileValue *domain.ExecutionProfile, override *domain.ExecutionTargetOverride) (domain.ExecutionTarget, error) {
|
||||
func resolveExecutionTarget(profileValue *domain.ExecutionProfile, override *domain.ExecutionTargetOverride) (domain.ExecutionTarget, domain.ExecutionTargetPresence, error) {
|
||||
out := defaults.ExecutionTargetDefault()
|
||||
out = mergeExecutionTarget(out, executionProfileToTarget(profileValue))
|
||||
var presence domain.ExecutionTargetPresence
|
||||
if override != nil {
|
||||
var err error
|
||||
out, err = mergeExecutionTargetOverride(out, *override)
|
||||
out, presence, err = mergeExecutionTargetOverride(out, *override)
|
||||
if err != nil {
|
||||
return domain.ExecutionTarget{}, err
|
||||
return domain.ExecutionTarget{}, domain.ExecutionTargetPresence{}, err
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
return out, presence, nil
|
||||
}
|
||||
|
||||
func validateAPIKeyEnv(apiKeyEnv string) error {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"gitea.maximumdirect.net/eric/scriptorium/internal/defaults"
|
||||
"gitea.maximumdirect.net/eric/scriptorium/internal/domain"
|
||||
"gitea.maximumdirect.net/eric/scriptorium/internal/llm"
|
||||
"gitea.maximumdirect.net/eric/scriptorium/internal/profile"
|
||||
"gitea.maximumdirect.net/eric/scriptorium/internal/prompt"
|
||||
"gitea.maximumdirect.net/eric/scriptorium/internal/promptdef"
|
||||
@@ -302,6 +303,7 @@ func TestRunnerPrepareRequestNumericOverridePresence(t *testing.T) {
|
||||
wantMaxTokens int
|
||||
wantTopP float64
|
||||
wantTimeoutSecs int
|
||||
wantPresence domain.ExecutionTargetPresence
|
||||
}{
|
||||
{
|
||||
name: "omitted preserves profile values",
|
||||
@@ -318,6 +320,7 @@ func TestRunnerPrepareRequestNumericOverridePresence(t *testing.T) {
|
||||
wantMaxTokens: 321,
|
||||
wantTopP: 0.8,
|
||||
wantTimeoutSecs: 45,
|
||||
wantPresence: domain.ExecutionTargetPresence{Temperature: true},
|
||||
},
|
||||
{
|
||||
name: "explicit zero max tokens",
|
||||
@@ -326,6 +329,7 @@ func TestRunnerPrepareRequestNumericOverridePresence(t *testing.T) {
|
||||
wantMaxTokens: 0,
|
||||
wantTopP: 0.8,
|
||||
wantTimeoutSecs: 45,
|
||||
wantPresence: domain.ExecutionTargetPresence{MaxTokens: true},
|
||||
},
|
||||
{
|
||||
name: "explicit zero top p",
|
||||
@@ -334,6 +338,7 @@ func TestRunnerPrepareRequestNumericOverridePresence(t *testing.T) {
|
||||
wantMaxTokens: 321,
|
||||
wantTopP: 0,
|
||||
wantTimeoutSecs: 45,
|
||||
wantPresence: domain.ExecutionTargetPresence{TopP: true},
|
||||
},
|
||||
{
|
||||
name: "explicit zero timeout",
|
||||
@@ -342,6 +347,7 @@ func TestRunnerPrepareRequestNumericOverridePresence(t *testing.T) {
|
||||
wantMaxTokens: 321,
|
||||
wantTopP: 0.8,
|
||||
wantTimeoutSecs: 0,
|
||||
wantPresence: domain.ExecutionTargetPresence{TimeoutSeconds: true},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -382,6 +388,9 @@ func TestRunnerPrepareRequestNumericOverridePresence(t *testing.T) {
|
||||
got.TimeoutSeconds != tc.wantTimeoutSecs {
|
||||
t.Fatalf("unexpected effective numeric settings: %+v", got)
|
||||
}
|
||||
if prepared.TargetPresence != tc.wantPresence {
|
||||
t.Fatalf("unexpected target presence: got %+v want %+v", prepared.TargetPresence, tc.wantPresence)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -857,6 +866,9 @@ func TestRunnerRunSuccessful(t *testing.T) {
|
||||
if llmClient.lastReq.Target.TimeoutSeconds != 90 {
|
||||
t.Fatalf("expected timeout propagation, got %d", llmClient.lastReq.Target.TimeoutSeconds)
|
||||
}
|
||||
if !llmClient.lastReq.TargetPresence.Temperature || !llmClient.lastReq.TargetPresence.TimeoutSeconds {
|
||||
t.Fatalf("expected numeric override presence to be sent to llm, got %+v", llmClient.lastReq.TargetPresence)
|
||||
}
|
||||
if llmClient.lastReq.Prompt.SessionID != "session-123" {
|
||||
t.Fatalf("expected session id to be sent to llm, got %q", llmClient.lastReq.Prompt.SessionID)
|
||||
}
|
||||
@@ -1305,6 +1317,28 @@ func TestRunnerRunLLMFailure(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunnerRunLLMInvalidRequestMapsToUsecaseInvalidRequest(t *testing.T) {
|
||||
runner := NewRunner(
|
||||
&fakePromptRepo{def: promptDef(domain.FormatText, domain.ValidationNone, 0)},
|
||||
&fakeExecutionProfileRepo{profiles: map[string]*domain.ExecutionProfile{"exec": defaultExecutionProfile()}},
|
||||
defaultArtifactReader(),
|
||||
defaultRenderer(),
|
||||
&fakeLLM{err: llm.ErrInvalidRequest},
|
||||
nil,
|
||||
)
|
||||
_, err := runner.Run(context.Background(), domain.RunRequest{
|
||||
PromptID: "p",
|
||||
ProfileID: "exec",
|
||||
Inputs: singleInputRef(),
|
||||
})
|
||||
if !errors.Is(err, ErrInvalidRequest) {
|
||||
t.Fatalf("expected ErrInvalidRequest, got %v", err)
|
||||
}
|
||||
if errors.Is(err, ErrLLMGenerate) {
|
||||
t.Fatalf("did not expect ErrLLMGenerate, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunnerRunValidationStillWorks(t *testing.T) {
|
||||
validator := &fakeValidator{result: domain.ValidationResult{Status: domain.ValidationFailed, Mode: domain.ValidationBasic, Errors: []string{"bad"}, IsValid: false}}
|
||||
runner := NewRunner(
|
||||
@@ -1478,10 +1512,13 @@ func TestResolveExecutionTargetProfileValuesPopulateAllSupportedFields(t *testin
|
||||
},
|
||||
}
|
||||
|
||||
target, err := resolveExecutionTarget(profileValue, nil)
|
||||
target, presence, err := resolveExecutionTarget(profileValue, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if presence != (domain.ExecutionTargetPresence{}) {
|
||||
t.Fatalf("expected no request override presence, got %+v", presence)
|
||||
}
|
||||
if target.Endpoint != profileValue.Endpoint ||
|
||||
target.Model != profileValue.Model ||
|
||||
target.Temperature != profileValue.Temperature ||
|
||||
@@ -1529,10 +1566,13 @@ func TestResolveExecutionTargetRuntimeOverridesBeatProfileForAllOverrideableFiel
|
||||
},
|
||||
}
|
||||
|
||||
target, err := resolveExecutionTarget(profileValue, override)
|
||||
target, presence, err := resolveExecutionTarget(profileValue, override)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if presence != (domain.ExecutionTargetPresence{Temperature: true, MaxTokens: true, TopP: true, TimeoutSeconds: true}) {
|
||||
t.Fatalf("unexpected override presence: %+v", presence)
|
||||
}
|
||||
if target.Endpoint != override.Endpoint ||
|
||||
target.Model != override.Model ||
|
||||
target.Temperature != *override.Temperature ||
|
||||
|
||||
Reference in New Issue
Block a user