114 lines
3.5 KiB
Go
114 lines
3.5 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"gitea.maximumdirect.net/eric/audita/internal/framework/contracts"
|
|
"gitea.maximumdirect.net/eric/audita/internal/framework/proposal_generation"
|
|
"gitea.maximumdirect.net/eric/audita/internal/framework/validators"
|
|
)
|
|
|
|
const (
|
|
subprocessTestLLMModeEnv = "AUDITA_SUBPROCESS_TEST_LLM_MODE"
|
|
subprocessTestRunTimeoutMSEnv = "AUDITA_SUBPROCESS_TEST_RUN_TIMEOUT_MS"
|
|
)
|
|
|
|
// ConfigureSubprocessTestHooksFromEnv enables deterministic test-only hooks for
|
|
// subprocess integration tests that run through the Go test binary helper path.
|
|
func ConfigureSubprocessTestHooksFromEnv() {
|
|
mode := strings.TrimSpace(os.Getenv(subprocessTestLLMModeEnv))
|
|
timeoutMSRaw := strings.TrimSpace(os.Getenv(subprocessTestRunTimeoutMSEnv))
|
|
// Only activate in explicit subprocess test mode.
|
|
if mode == "" && timeoutMSRaw == "" {
|
|
return
|
|
}
|
|
|
|
if mode != "" {
|
|
client := &subprocessTestLLMClient{mode: mode}
|
|
processProposalLLMClient = client
|
|
processValidationLLMClient = client
|
|
}
|
|
|
|
if timeoutMSRaw == "" {
|
|
return
|
|
}
|
|
timeoutMS, err := strconv.Atoi(timeoutMSRaw)
|
|
if err != nil || timeoutMS <= 0 {
|
|
return
|
|
}
|
|
processRunnerContext = func() (context.Context, context.CancelFunc) {
|
|
return context.WithTimeout(context.Background(), time.Duration(timeoutMS)*time.Millisecond)
|
|
}
|
|
}
|
|
|
|
type subprocessTestLLMClient struct {
|
|
mode string
|
|
mu sync.Mutex
|
|
proposals int
|
|
}
|
|
|
|
func (c *subprocessTestLLMClient) CompleteStructured(ctx context.Context, req contracts.StructuredCompletionRequest, out any) (contracts.StructuredCompletionResponse, error) {
|
|
_ = req
|
|
|
|
switch c.mode {
|
|
case "backend_error":
|
|
return contracts.StructuredCompletionResponse{}, errors.New("synthetic backend failure")
|
|
case "block_until_cancel":
|
|
<-ctx.Done()
|
|
return contracts.StructuredCompletionResponse{}, ctx.Err()
|
|
case "malformed_structured":
|
|
switch target := out.(type) {
|
|
case *proposal_generation.StructuredCorrectionSet:
|
|
*target = proposal_generation.StructuredCorrectionSet{
|
|
Corrections: []proposal_generation.StructuredCorrectionProposal{
|
|
{TargetSegmentID: 0, OriginalText: "x", CorrectedText: "y", Confidence: 0.99},
|
|
},
|
|
}
|
|
case *validators.LLMValidationResponse:
|
|
*target = validators.LLMValidationResponse{
|
|
Validations: []validators.LLMValidationDecision{
|
|
{CorrectionIndex: 999, Approved: true, Confidence: 0.9, Reason: "bad index"},
|
|
},
|
|
}
|
|
}
|
|
case "mid_pipeline_fail":
|
|
switch target := out.(type) {
|
|
case *proposal_generation.StructuredCorrectionSet:
|
|
c.mu.Lock()
|
|
c.proposals++
|
|
proposalCall := c.proposals
|
|
c.mu.Unlock()
|
|
|
|
if proposalCall >= 3 {
|
|
*target = proposal_generation.StructuredCorrectionSet{
|
|
Corrections: []proposal_generation.StructuredCorrectionProposal{
|
|
{TargetSegmentID: 0, OriginalText: "x", CorrectedText: "y", Confidence: 0.99},
|
|
},
|
|
}
|
|
} else {
|
|
*target = proposal_generation.StructuredCorrectionSet{
|
|
Corrections: []proposal_generation.StructuredCorrectionProposal{
|
|
{TargetSegmentID: 1, OriginalText: "Segment", CorrectedText: "Segment", Confidence: 0.99},
|
|
},
|
|
}
|
|
}
|
|
case *validators.LLMValidationResponse:
|
|
*target = validators.LLMValidationResponse{Validations: nil}
|
|
}
|
|
default:
|
|
switch target := out.(type) {
|
|
case *proposal_generation.StructuredCorrectionSet:
|
|
*target = proposal_generation.StructuredCorrectionSet{Corrections: nil}
|
|
case *validators.LLMValidationResponse:
|
|
*target = validators.LLMValidationResponse{Validations: nil}
|
|
}
|
|
}
|
|
return contracts.StructuredCompletionResponse{}, nil
|
|
}
|