Skip to content

Commit eb252fa

Browse files
committed
test(bdd): retry transient LLM eligibility
Signed-off-by: Mike Camp <mcamp@nvidia.com>
1 parent 522610b commit eb252fa

2 files changed

Lines changed: 81 additions & 2 deletions

File tree

tests/bdd/steps/nvcf_cli_steps.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@ import (
2121
"context"
2222
"fmt"
2323
"strings"
24+
"time"
2425

2526
"github.com/cucumber/godog"
2627

2728
"nvcf-bdd/dsl"
2829
)
2930

31+
var modelInvocationRetryInterval = time.Second
32+
3033
func registerNVCFCLISteps(ctx *godog.ScenarioContext, sc *ScenarioContext) {
3134
ctx.Step(`^I use NVCF CLI config "([^"]*)"$`, sc.iUseNVCFCLIConfig)
3235
ctx.Step(`^I successfully create function "([^"]*)" from image "([^"]*)" with CLI options:$`, sc.iSuccessfullyCreateFunction)
@@ -101,13 +104,39 @@ func (sc *ScenarioContext) iSuccessfullyInvokeModel(
101104
timeout string,
102105
doc *godog.DocString,
103106
) error {
104-
return sc.runNVCFCLI(ctx,
107+
args := []string{
105108
"function", "invoke",
106109
"--inference-url", inferenceURL,
107110
"--model-name", model,
108111
"--request-body", doc.Content,
109112
"--timeout", timeout,
110-
)
113+
}
114+
retryFor, retryTimeoutErr := time.ParseDuration(timeout + "s")
115+
deadline := time.Now().Add(retryFor)
116+
117+
for {
118+
err := sc.runNVCFCLI(ctx, args...)
119+
if err == nil {
120+
return nil
121+
}
122+
if retryTimeoutErr != nil || retryFor <= 0 ||
123+
!strings.Contains(combinedOutput(sc.LastResult), "no_eligible_candidates") {
124+
return err
125+
}
126+
127+
remaining := time.Until(deadline)
128+
if remaining <= 0 {
129+
return err
130+
}
131+
waitFor := min(modelInvocationRetryInterval, remaining)
132+
timer := time.NewTimer(waitFor)
133+
select {
134+
case <-ctx.Done():
135+
timer.Stop()
136+
return ctx.Err()
137+
case <-timer.C:
138+
}
139+
}
111140
}
112141

113142
func (sc *ScenarioContext) iSuccessfullyUndeploySelectedFunction(ctx context.Context) error {

tests/bdd/steps/nvcf_cli_steps_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"errors"
2323
"strings"
2424
"testing"
25+
"time"
2526

2627
"github.com/cucumber/godog"
2728

@@ -119,6 +120,55 @@ func TestNVCFCLIInvocationAdaptersExposeAllArguments(t *testing.T) {
119120
}
120121
}
121122

123+
func TestNVCFCLIModelInvocationRetriesNoEligibleCandidates(t *testing.T) {
124+
sc, fake := newScenarioContext(t)
125+
t.Setenv("NVCF_CLI", "nvcf-cli")
126+
sc.NVCFCLIConfig = "config.yaml"
127+
fake.runResults = []harness.Result{
128+
{ExitCode: 1, Stderr: `API error 404: {"code":"no_eligible_candidates"}`},
129+
{ExitCode: 0, Stdout: `{"object":"chat.completion"}`},
130+
}
131+
132+
previousInterval := modelInvocationRetryInterval
133+
modelInvocationRetryInterval = time.Nanosecond
134+
t.Cleanup(func() { modelInvocationRetryInterval = previousInterval })
135+
136+
err := sc.iSuccessfullyInvokeModel(
137+
context.Background(),
138+
"model/name",
139+
"/v1/chat/completions",
140+
"1",
141+
&godog.DocString{Content: `{"messages":[]}`},
142+
)
143+
if err != nil {
144+
t.Fatalf("invoke model: %v", err)
145+
}
146+
if len(fake.runs) != 2 {
147+
t.Fatalf("runs = %d, want 2", len(fake.runs))
148+
}
149+
}
150+
151+
func TestNVCFCLIModelInvocationDoesNotRetryOtherErrors(t *testing.T) {
152+
sc, fake := newScenarioContext(t)
153+
t.Setenv("NVCF_CLI", "nvcf-cli")
154+
sc.NVCFCLIConfig = "config.yaml"
155+
fake.result = harness.Result{ExitCode: 1, Stderr: "API error 401: unauthorized"}
156+
157+
err := sc.iSuccessfullyInvokeModel(
158+
context.Background(),
159+
"model/name",
160+
"/v1/chat/completions",
161+
"1",
162+
&godog.DocString{Content: `{"messages":[]}`},
163+
)
164+
if err == nil || !strings.Contains(err.Error(), "exit code = 1, want 0") {
165+
t.Fatalf("error = %v, want exit-zero assertion failure", err)
166+
}
167+
if len(fake.runs) != 1 {
168+
t.Fatalf("runs = %d, want 1", len(fake.runs))
169+
}
170+
}
171+
122172
func TestNVCFCLISuccessStepRequiresExitZero(t *testing.T) {
123173
sc, fake := newScenarioContext(t)
124174
t.Setenv("NVCF_CLI", "nvcf-cli")

0 commit comments

Comments
 (0)