Review LLM concurrency refactor
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -467,6 +468,57 @@ func TestRunProcessLLMConcurrencyFlagsOverrideEnvironment(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunProcessAcceptsLLMConcurrencyEnvironmentVariables(t *testing.T) {
|
||||
processModuleFactory = fakeModuleFactory{modules: map[string]contracts.TranscriptModule{
|
||||
"m": fakeModule{
|
||||
key: "m",
|
||||
policy: proposals.ReplacementPolicyRequireUnique,
|
||||
validators: []contracts.Validator{
|
||||
fakeValidator{name: "capture-config", validateF: func(req contracts.ValidationRequest) (validators.Result, error) {
|
||||
if req.Config == nil {
|
||||
t.Fatal("expected config in validation request")
|
||||
}
|
||||
if req.Config.TotalLLMConcurrency != 5 {
|
||||
t.Fatalf("expected env total llm concurrency 5, got %d", req.Config.TotalLLMConcurrency)
|
||||
}
|
||||
if req.Config.ProposalLLMConcurrency != 3 {
|
||||
t.Fatalf("expected env proposal llm concurrency 3, got %d", req.Config.ProposalLLMConcurrency)
|
||||
}
|
||||
if req.Config.ValidationLLMConcurrency == nil || *req.Config.ValidationLLMConcurrency != 2 {
|
||||
t.Fatalf("expected env validation llm concurrency 2, got %#v", req.Config.ValidationLLMConcurrency)
|
||||
}
|
||||
return validators.Result{ValidatorName: "capture-config", Decisions: nil}, nil
|
||||
}},
|
||||
},
|
||||
proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) {
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
}}
|
||||
t.Cleanup(func() { processModuleFactory = nil })
|
||||
t.Setenv("AUDITA_TOTAL_LLM_CONCURRENCY", "5")
|
||||
t.Setenv("AUDITA_PROPOSAL_LLM_CONCURRENCY", "3")
|
||||
t.Setenv("AUDITA_VALIDATION_LLM_CONCURRENCY", "2")
|
||||
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
transcriptPath := writeFile(t, "transcript.json", `[
|
||||
{"id":1,"speaker":"Alice","start":0.0,"end":1.0,"text":"Hello"}
|
||||
]`)
|
||||
|
||||
exitCode := Run([]string{
|
||||
"process",
|
||||
transcriptPath,
|
||||
"--glossary",
|
||||
fixturePath("tiny_glossary.yaml"),
|
||||
"--modules",
|
||||
"m",
|
||||
}, &stdout, &stderr)
|
||||
if exitCode != 0 {
|
||||
t.Fatalf("expected success, got %d stderr=%q", exitCode, stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestComposeSchedulersEnforcesStricterSubcap(t *testing.T) {
|
||||
global, err := llm.NewScheduler(3)
|
||||
if err != nil {
|
||||
@@ -483,6 +535,7 @@ func TestComposeSchedulersEnforcesStricterSubcap(t *testing.T) {
|
||||
|
||||
var inFlight int32
|
||||
var maxInFlight int32
|
||||
release := make(chan struct{})
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 6; i++ {
|
||||
wg.Add(1)
|
||||
@@ -496,7 +549,7 @@ func TestComposeSchedulersEnforcesStricterSubcap(t *testing.T) {
|
||||
break
|
||||
}
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
<-release
|
||||
atomic.AddInt32(&inFlight, -1)
|
||||
return nil
|
||||
})
|
||||
@@ -505,6 +558,8 @@ func TestComposeSchedulersEnforcesStricterSubcap(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
}
|
||||
waitForAtomicAtLeast(t, &maxInFlight, 1)
|
||||
close(release)
|
||||
wg.Wait()
|
||||
|
||||
if maxInFlight > 1 {
|
||||
@@ -530,6 +585,7 @@ func TestComposedProposalAndValidationSchedulersShareGlobalTotalCap(t *testing.T
|
||||
|
||||
var inFlight int32
|
||||
var maxInFlight int32
|
||||
release := make(chan struct{})
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 12; i++ {
|
||||
wg.Add(1)
|
||||
@@ -547,7 +603,7 @@ func TestComposedProposalAndValidationSchedulersShareGlobalTotalCap(t *testing.T
|
||||
break
|
||||
}
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
<-release
|
||||
atomic.AddInt32(&inFlight, -1)
|
||||
return nil
|
||||
})
|
||||
@@ -556,6 +612,8 @@ func TestComposedProposalAndValidationSchedulersShareGlobalTotalCap(t *testing.T
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
waitForAtomicAtLeast(t, &maxInFlight, 2)
|
||||
close(release)
|
||||
wg.Wait()
|
||||
|
||||
if maxInFlight > 2 {
|
||||
@@ -566,6 +624,18 @@ func TestComposedProposalAndValidationSchedulersShareGlobalTotalCap(t *testing.T
|
||||
}
|
||||
}
|
||||
|
||||
func waitForAtomicAtLeast(t *testing.T, value *int32, want int32) {
|
||||
t.Helper()
|
||||
deadline := time.Now().Add(300 * time.Millisecond)
|
||||
for time.Now().Before(deadline) {
|
||||
if atomic.LoadInt32(value) >= want {
|
||||
return
|
||||
}
|
||||
runtime.Gosched()
|
||||
}
|
||||
t.Fatalf("timed out waiting for value >= %d (got %d)", want, atomic.LoadInt32(value))
|
||||
}
|
||||
|
||||
func TestRunProcessReportJSONSuccessIncludesNormalizationSummary(t *testing.T) {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
Reference in New Issue
Block a user