Run ordered processing with independent settings

This commit is contained in:
2026-11-10 17:33:30 +00:00
parent 89de2a7014
commit 7efa07ee9d
21 changed files with 447 additions and 335 deletions

View File

@@ -7,6 +7,7 @@ import (
"gitea.maximumdirect.net/eric/audita/v3/internal/core/chunking"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/glossary"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/modulecatalog"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/outcomes"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/proposals"
@@ -38,6 +39,13 @@ type ModuleRunSpec struct {
ModuleKey string `json:"module_key"`
InstanceName string `json:"instance_name"`
ReplacementPolicy proposals.ReplacementPolicy `json:"replacement_policy"`
Index int `json:"-"`
ID *string `json:"-"`
Settings config.Stage `json:"-"`
Glossary []glossary.Entry `json:"-"`
ProtectedTerms []glossary.Term `json:"-"`
ProposalClient StructuredLLMClient `json:"-"`
ValidationClient StructuredLLMClient `json:"-"`
}
// SectionMetadata captures chunk-level metadata without embedding full segment payloads.
@@ -64,7 +72,7 @@ func SectionMetadataFromSection(section chunking.Section) SectionMetadata {
type ExecutionContext struct {
// ReportDiagnosticError may be called concurrently; it must not affect processing.
ReportDiagnosticError func(error) `json:"-"`
Config *config.Config `json:"-"`
Settings config.Stage `json:"-"`
WorkingTranscript *schema.Transcript `json:"-"`
Glossary []glossary.Entry `json:"-"`
ProtectedTerms []glossary.Term `json:"-"`
@@ -87,32 +95,20 @@ type ProposalResult struct {
// ValidationRequest is the input to validator execution.
type ValidationRequest = validators.Request
// ResolveModuleRunSpecs deterministically resolves instance names from logical keys.
// Repeated keys are suffixed with _<n> (1-based), while singleton keys keep their raw key.
func ResolveModuleRunSpecs(moduleKeys []string) ([]ModuleRunSpec, error) {
totals := make(map[string]int, len(moduleKeys))
for i, key := range moduleKeys {
if key == "" {
return nil, fmt.Errorf("module key at index %d must not be empty", i)
}
totals[key]++
// ResolveModuleRunSpecs derives isolated consumer views from resolved settings.
func ResolveModuleRunSpecs(cfg *config.Config) []ModuleRunSpec {
if cfg == nil {
return nil
}
seen := make(map[string]int, len(totals))
specs := make([]ModuleRunSpec, len(moduleKeys))
for i, key := range moduleKeys {
seen[key]++
name := key
if totals[key] > 1 {
name = fmt.Sprintf("%s_%d", key, seen[key])
}
specs[i] = ModuleRunSpec{
ModuleKey: key,
InstanceName: name,
specs := make([]ModuleRunSpec, len(cfg.Pipeline.Stages))
for i, item := range cfg.Pipeline.Stages {
selected := glossary.Select(cfg.GlossaryCollections, item.Glossaries)
protected := glossary.Select(cfg.GlossaryCollections, item.ProtectedTerms)
var excluded []glossary.Entry
if item.Module == modulecatalog.KeyGlossary {
excluded = selected
}
specs[i] = ModuleRunSpec{ModuleKey: item.Module, InstanceName: fmt.Sprintf("%s_%d", item.Module, i+1), Index: i, ID: item.ID, Settings: item, Glossary: selected, ProtectedTerms: glossary.Vocabulary(protected, excluded)}
}
return specs, nil
return specs
}

View File

@@ -5,9 +5,7 @@ import (
"fmt"
"strings"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/modulecatalog"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/contracts"
glossarymodule "gitea.maximumdirect.net/eric/audita/v3/internal/modules/glossary"
grammarmodule "gitea.maximumdirect.net/eric/audita/v3/internal/modules/grammar"
@@ -32,19 +30,9 @@ func IsKnownModuleKey(key string) bool {
return modulecatalog.IsSupported(key)
}
// Dependencies holds explicit constructor dependencies for module creation.
type Dependencies struct {
Config *config.Config
Glossary *schema.Glossary
ProposalLLMClient contracts.StructuredLLMClient
ValidationLLMClient contracts.StructuredLLMClient
DiagnosticsDir string
}
// ConstructRequest is one module-construction request.
// ConstructRequest identifies the module to construct.
type ConstructRequest struct {
RunSpec contracts.ModuleRunSpec
Dependencies
}
// Constructor builds one module instance from a run spec and explicit deps.
@@ -52,14 +40,12 @@ type Constructor func(ctx context.Context, req ConstructRequest) (contracts.Tran
// Factory resolves configured module specs into module instances.
type Factory struct {
deps Dependencies
constructors map[string]Constructor
}
// NewFactory creates a production module registry.
func NewFactory(deps Dependencies) *Factory {
func NewFactory() *Factory {
factory := &Factory{
deps: deps,
constructors: make(map[string]Constructor, len(modulecatalog.SupportedKeys())),
}
_ = factory.RegisterConstructor(ModuleKeyGlossary, constructGlossaryModule)
@@ -125,16 +111,7 @@ func (f *Factory) ModuleForSpec(spec contracts.ModuleRunSpec) (contracts.Transcr
return nil, fmt.Errorf("internal module registry error: constructor for module %q is not configured", key)
}
module, err := constructor(context.Background(), ConstructRequest{
RunSpec: spec,
Dependencies: Dependencies{
Config: f.deps.Config,
Glossary: f.deps.Glossary,
ProposalLLMClient: f.deps.ProposalLLMClient,
ValidationLLMClient: f.deps.ValidationLLMClient,
DiagnosticsDir: f.deps.DiagnosticsDir,
},
})
module, err := constructor(context.Background(), ConstructRequest{RunSpec: spec})
if err != nil {
return nil, fmt.Errorf("construct module %q: %w", spec.InstanceName, err)
}

View File

@@ -7,11 +7,14 @@ import (
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/diagnostics"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/glossary"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/modulecatalog"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
)
// Input contains already acquired application and transcript inputs.
type Input struct {
InspectProfile func(context.Context, string) error
Config *config.Config
Transcript *schema.SourceTranscript
Output diagnostics.OutputSelection
@@ -45,6 +48,9 @@ func Prepare(ctx context.Context, input Input) (*Prepared, error) {
if input.Transcript == nil {
return nil, fmt.Errorf("transcript is required")
}
if err := CheckReadiness(ctx, input.Config, input.InspectProfile); err != nil {
return nil, err
}
session, err := diagnostics.SelectOutput(input.Output)
if err != nil {
return nil, err
@@ -74,3 +80,38 @@ func Prepare(ctx context.Context, input Input) (*Prepared, error) {
}
return result, nil
}
// CheckReadiness checks all resolved processing selections before output preparation.
func CheckReadiness(ctx context.Context, cfg *config.Config, inspectProfile func(context.Context, string) error) error {
if cfg == nil {
return fmt.Errorf("application configuration is required")
}
label := func(index int, item config.Stage) string {
if item.ID != nil {
return fmt.Sprintf("stage %d (%s, id %q)", index+1, item.Module, *item.ID)
}
return fmt.Sprintf("stage %d (%s)", index+1, item.Module)
}
for index, item := range cfg.Pipeline.Stages {
if item.Module == modulecatalog.KeyGlossary && len(glossary.Select(cfg.GlossaryCollections, item.Glossaries)) == 0 {
return fmt.Errorf("%s has no glossary entries in selected collections %q", label(index, item), item.Glossaries)
}
}
for index, item := range cfg.Pipeline.Stages {
for _, selection := range []struct {
role string
profile *string
}{{"proposal", item.LLM.ProposalProfile}, {"validation", item.LLM.ValidationProfile}} {
if selection.profile == nil || *selection.profile == "" {
return fmt.Errorf("%s requires a %s profile", label(index, item), selection.role)
}
if inspectProfile == nil {
return fmt.Errorf("profile inspection is required")
}
if err := inspectProfile(ctx, *selection.profile); err != nil {
return fmt.Errorf("%s %s profile %q: %w", label(index, item), selection.role, *selection.profile, err)
}
}
}
return ctx.Err()
}

View File

@@ -0,0 +1,39 @@
package preflight
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/diagnostics"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
)
func TestLaterEmptyGlossaryFailsBeforeOutputPreparation(t *testing.T) {
profile, id := "selected", "spells"
cfg := &config.Config{Pipeline: config.Pipeline{Stages: []config.Stage{
{Module: "grammar", LLM: config.LLM{ProposalProfile: &profile, ValidationProfile: &profile}},
{Module: "glossary", ID: &id, Glossaries: []string{"empty"}, LLM: config.LLM{ProposalProfile: &profile, ValidationProfile: &profile}},
}}}
output := filepath.Join(t.TempDir(), "transcript.json")
calls := 0
_, err := Prepare(context.Background(), Input{Config: cfg, Transcript: &schema.SourceTranscript{}, Output: diagnostics.OutputSelection{TranscriptPath: output}, InspectProfile: func(context.Context, string) error { calls++; return nil }})
if err == nil || !strings.Contains(err.Error(), "stage 2") || !strings.Contains(err.Error(), "spells") || !strings.Contains(err.Error(), "empty") {
t.Fatalf("unexpected error: %v", err)
}
if calls != 0 {
t.Fatal("inspected profiles before checking all glossary selections")
}
if _, err := os.Stat(output); !os.IsNotExist(err) {
t.Fatalf("output exists: %v", err)
}
}
func TestEmptyPipelineNeedsNoProfile(t *testing.T) {
if err := CheckReadiness(context.Background(), &config.Config{}, nil); err != nil {
t.Fatal(err)
}
}

View File

@@ -3,12 +3,11 @@ package processreport
import (
"time"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/reporting"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/llm"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/runner"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/structuredoutput"
pk "gitea.maximumdirect.net/eric/promptkit"
pk "go.maximumdirect.net/promptkit"
)
func mapLLMSummary(source llm.ExecutionSummary) reporting.LLMSummary {
@@ -23,7 +22,7 @@ func mapLLMSummary(source llm.ExecutionSummary) reporting.LLMSummary {
}
// BuildUtilization groups complete logical calls by their selected backend.
func BuildUtilization(output *runner.RunOutput, backends map[string]config.Backend) reporting.UtilizationDiagnostics {
func BuildUtilization(output *runner.RunOutput, backends map[string]*pk.BackendInspection) reporting.UtilizationDiagnostics {
result := reporting.UtilizationDiagnostics{Backends: make(map[string]reporting.BackendUtilization, len(backends))}
if output == nil {
return result
@@ -85,7 +84,7 @@ func BuildUtilization(output *runner.RunOutput, backends map[string]config.Backe
}
}
result.Backends[id] = reporting.BackendUtilization{
Concurrency: backend.Concurrency, QueueCapacity: backend.QueueCapacity,
Concurrency: backend.ConcurrencyLimit, QueueCapacity: backend.QueueCapacity,
LLMSummary: mapLLMSummary(llm.Summarize(*group)),
CapacityWaitMS: wait.Milliseconds(), GenerationMS: generation.Milliseconds(),
RetryDelayMS: retry.Milliseconds(), LogicalWallMS: wall.Milliseconds(),

View File

@@ -10,7 +10,6 @@ import (
"path/filepath"
"strings"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/contracts"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/llm"
@@ -57,7 +56,6 @@ type Request struct {
ReplacementPolicy proposals.ReplacementPolicy `json:"replacement_policy"`
WorkingTranscript *schema.Transcript `json:"-"`
Section *contracts.SectionMetadata `json:"section,omitempty"`
Config *config.Config `json:"-"`
PromptID string `json:"prompt_id"`
Variables map[string]string `json:"variables"`
StageName string `json:"stage_name,omitempty"`

View File

@@ -35,10 +35,7 @@ func ExecuteModuleProposal(ctx context.Context, req ModuleProposalRequest) (cont
sectionIndex = req.ProposalRequest.Section.Index
}
transcriptDescription := ""
if req.ProposalRequest.Config != nil {
transcriptDescription = req.ProposalRequest.Config.Context.Description
}
transcriptDescription := req.ProposalRequest.Settings.Context.Description
variables, err := req.BuildVariables(
req.ProposalRequest.WorkingTranscript,
@@ -61,7 +58,6 @@ func ExecuteModuleProposal(ctx context.Context, req ModuleProposalRequest) (cont
ReplacementPolicy: req.ProposalRequest.RunSpec.ReplacementPolicy,
WorkingTranscript: req.ProposalRequest.WorkingTranscript,
Section: req.ProposalRequest.Section,
Config: req.ProposalRequest.Config,
PromptID: promptMetadata.PromptID,
Variables: variables,
StageName: stagename.ModuleProposal(req.ProposalRequest.RunSpec.InstanceName, sectionIndexPtr(req.ProposalRequest.Section)),

View File

@@ -0,0 +1,67 @@
package runner
import (
"context"
"testing"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/contracts"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/proposals"
)
type orderedFactory struct {
seen chan contracts.ProposalRequest
}
func (f orderedFactory) ModuleForSpec(spec contracts.ModuleRunSpec) (contracts.TranscriptModule, error) {
return orderedModule{spec: spec, seen: f.seen}, nil
}
type orderedModule struct {
spec contracts.ModuleRunSpec
seen chan contracts.ProposalRequest
}
func (m orderedModule) Key() string { return m.spec.ModuleKey }
func (m orderedModule) ReplacementPolicy() proposals.ReplacementPolicy {
return proposals.ReplacementPolicyRequireUnique
}
func (m orderedModule) Validators() []contracts.Validator { return nil }
func (m orderedModule) Propose(_ context.Context, req contracts.ProposalRequest) (contracts.ProposalResult, error) {
m.seen <- req
original, corrected := "party", "Party"
if m.spec.Index == 1 {
original, corrected = "spell", "Spell"
}
if m.spec.Index == 2 {
original, corrected = "Party Spell", "Party Spell!"
}
return contracts.ProposalResult{Proposals: []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: original, CorrectedText: corrected, Confidence: 1}}}, nil
}
func TestOrderedWorkflowUsesIndependentSelectionsAndPriorEdits(t *testing.T) {
cfg := &config.Config{GlossaryCollections: map[string]config.GlossaryCollection{
"party": {Entries: []config.GlossaryEntry{{ID: "party", Name: "Party", Category: "person"}}},
"spells": {Entries: []config.GlossaryEntry{{ID: "spell", Name: "Spell", Category: "spell"}}},
}, Pipeline: config.Pipeline{Stages: []config.Stage{
{Module: "glossary", Glossaries: []string{"party"}, ProtectedTerms: []string{"party", "spells"}, Context: config.Context{Description: "party context"}},
{Module: "glossary", Glossaries: []string{"spells"}, ProtectedTerms: []string{"party", "spells"}, Context: config.Context{Description: "spell context"}},
{Module: "grammar", Glossaries: []string{"party", "spells"}, ProtectedTerms: []string{"party", "spells"}},
}}}
seen := make(chan contracts.ProposalRequest, 3)
output, err := New(orderedFactory{seen: seen}).Run(context.Background(), RunInput{Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "party spell"}}}, ModuleSpecs: contracts.ResolveModuleRunSpecs(cfg)})
if err != nil {
t.Fatal(err)
}
if got := output.FinalTranscript.Segments[0].Text; got != "Party Spell!" {
t.Fatalf("final text = %q", got)
}
first, second, third := <-seen, <-seen, <-seen
if first.Settings.Context.Description != "party context" || second.Settings.Context.Description != "spell context" || len(first.Glossary) != 1 || first.Glossary[0].Name != "Party" || len(second.Glossary) != 1 || second.Glossary[0].Name != "Spell" || len(third.Glossary) != 2 {
t.Fatalf("incorrect context: %+v %+v %+v", first, second, third)
}
if second.WorkingTranscript.Segments[0].Text != "Party spell" || third.WorkingTranscript.Segments[0].Text != "Party Spell" {
t.Fatalf("edits not carried forward: %q, %q", second.WorkingTranscript.Segments[0].Text, third.WorkingTranscript.Segments[0].Text)
}
}

View File

@@ -11,6 +11,7 @@ import (
"gitea.maximumdirect.net/eric/audita/v3/internal/core/chunking"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/modulecatalog"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/contracts"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/llm"
@@ -93,11 +94,8 @@ type RunInput struct {
ReportDiagnosticError func(error) `json:"-"`
Config *config.Config
Transcript *schema.Transcript
Glossary *schema.Glossary
ModuleSpecs []contracts.ModuleRunSpec
ProposalLLMClient contracts.StructuredLLMClient
ProposalDiagnosticsDir string
ValidationLLMClient contracts.StructuredLLMClient
ValidationDiagnosticsDir string
CaptureCandidates bool
timings *validatorTimingCollector
@@ -185,12 +183,10 @@ func (r *Runner) run(ctx context.Context, input RunInput) (RunOutput, error) {
Module: module,
Spec: spec,
Policy: policy,
Config: input.Config,
Glossary: input.Glossary,
Sections: sections,
ProposalClient: input.ProposalLLMClient,
ProposalClient: spec.ProposalClient,
DiagnosticsDir: input.ProposalDiagnosticsDir,
ValidationClient: input.ValidationLLMClient,
ValidationClient: spec.ValidationClient,
ValidationDiagnosticsDir: input.ValidationDiagnosticsDir,
Timings: input.timings,
ReportDiagnosticError: input.ReportDiagnosticError,
@@ -220,7 +216,7 @@ func (r *Runner) run(ctx context.Context, input RunInput) (RunOutput, error) {
return snapshot(), fmt.Errorf("module %q failed: %w", spec.InstanceName, pipelineErr)
}
updated, applied, skipped, appliedBySection, applyErr := applyWithinSections(sections, pipelineResult.Approved, policy)
updated, applied, skipped, appliedBySection, applyErr := applyWithinSections(sections, pipelineResult.Approved, policy, validators.NewProtectedVocabulary(spec.ProtectedTerms), spec.ModuleKey == modulecatalog.KeySpokenWord)
if applyErr != nil {
return snapshot(), fmt.Errorf("module %q application failed: %w", spec.InstanceName, applyErr)
}
@@ -268,7 +264,7 @@ func transcriptFromSections(sections []chunking.Section) *schema.Transcript {
// applyWithinSections leaves section membership and initial metadata unchanged.
// Approved proposals are applied only to their identified section.
func applyWithinSections(sections []chunking.Section, approved []proposals.EnrichedCorrectionProposal, policy proposals.ReplacementPolicy) ([]chunking.Section, []proposals.AppliedChange, []proposals.SkippedChange, []int, error) {
func applyWithinSections(sections []chunking.Section, approved []proposals.EnrichedCorrectionProposal, policy proposals.ReplacementPolicy, vocabulary validators.ProtectedVocabulary, allowRepetitionCleanup bool) ([]chunking.Section, []proposals.AppliedChange, []proposals.SkippedChange, []int, error) {
bySection := make(map[int][]proposals.EnrichedCorrectionProposal, len(sections))
known := make(map[int]struct{}, len(sections))
for _, section := range sections {
@@ -289,7 +285,7 @@ func applyWithinSections(sections []chunking.Section, approved []proposals.Enric
skipped := make([]proposals.SkippedChange, 0)
appliedBySection := make([]int, len(sections))
for i, section := range sections {
result := proposals.ApplyProposals(transcriptFromSection(section), bySection[section.Index], policy)
result := proposals.ApplyProposals(transcriptFromSection(section), bySection[section.Index], policy, func(before, after string) bool { return vocabulary.AllowsChange(before, after, allowRepetitionCleanup) })
updated[i].Segments = result.Transcript.Segments
applied = append(applied, result.Applied...)
skipped = append(skipped, result.Skipped...)
@@ -304,8 +300,6 @@ type collectSectionProposalsInput struct {
Module contracts.TranscriptModule
Spec contracts.ModuleRunSpec
Policy proposals.ReplacementPolicy
Config *config.Config
Glossary *schema.Glossary
Sections []chunking.Section
ProposalClient contracts.StructuredLLMClient
DiagnosticsDir string
@@ -364,9 +358,10 @@ func collectSectionProposals(ctx context.Context, input collectSectionProposalsI
meta := contracts.SectionMetadataFromSection(section)
proposalResult, err := input.Module.Propose(runCtx, contracts.ProposalRequest{
ExecutionContext: contracts.ExecutionContext{
Config: input.Config,
Settings: input.Spec.Settings,
WorkingTranscript: transcriptFromSection(section),
Glossary: input.Glossary,
Glossary: input.Spec.Glossary,
ProtectedTerms: input.Spec.ProtectedTerms,
Section: &meta,
DiagnosticsDir: input.DiagnosticsDir,
ReportDiagnosticError: input.ReportDiagnosticError,
@@ -506,8 +501,6 @@ func runModulePipeline(ctx context.Context, input collectSectionProposalsInput)
validated, err := validateSectionCandidates(runCtx, validateSectionCandidatesInput{
Spec: input.Spec,
Policy: input.Policy,
Glossary: input.Glossary,
Config: input.Config,
WorkingTranscript: transcriptFromSection(sectionResult.section),
SectionIndex: &sectionMetadata.Index,
Validators: validatorsOrdered,
@@ -592,8 +585,6 @@ type validateSectionCandidatesInput struct {
ReportDiagnosticError func(error) `json:"-"`
Spec contracts.ModuleRunSpec
Policy proposals.ReplacementPolicy
Glossary *schema.Glossary
Config *config.Config
WorkingTranscript *schema.Transcript
SectionIndex *int
Validators []contracts.Validator
@@ -624,10 +615,16 @@ func validateSectionCandidates(ctx context.Context, input validateSectionCandida
}
var diagnosticsWriter validators.InteractionDiagnosticsWriter
if input.DiagnosticsDir != "" {
var secrets []string
if source, ok := input.ValidationLLMClient.(interface {
ConfiguredSecrets(context.Context) []string
}); ok {
secrets = source.ConfiguredSecrets(ctx)
}
diagnosticsWriter = &llmDiagnosticsWriterAdapter{
writer: llm.NewDiagnosticsWriter(
filepath.Join(input.DiagnosticsDir, input.Spec.InstanceName),
llm.ConfiguredSecrets(input.Config),
secrets,
),
}
}
@@ -641,8 +638,9 @@ func validateSectionCandidates(ctx context.Context, input validateSectionCandida
SectionIndex: input.SectionIndex,
ValidatorOrder: &validatorOrder,
ReplacementPolicy: input.Policy,
Glossary: input.Glossary,
Config: input.Config,
Glossary: input.Spec.Glossary,
ProtectedTerms: input.Spec.ProtectedTerms,
Settings: input.Spec.Settings,
LLMClient: input.ValidationLLMClient,
DiagnosticsWriter: diagnosticsWriter,
ReportDiagnosticError: input.ReportDiagnosticError,

View File

@@ -39,7 +39,7 @@ type ConfidenceThresholdValidator struct{}
func (v ConfidenceThresholdValidator) Name() string { return "confidence_threshold" }
func (v ConfidenceThresholdValidator) Validate(_ context.Context, req Request) (Result, error) {
threshold := confidenceThresholdForModule(req.ModuleKey, req.Config)
threshold := req.Settings.ConfidenceThreshold
decisions := make([]Decision, 0, len(req.CandidateProposal))
for _, c := range req.CandidateProposal {
if c.Confidence < threshold {

View File

@@ -72,8 +72,8 @@ func (v *LLMBackedValidator) Validate(ctx context.Context, req Request) (Result,
}
maxTokens := config.DefaultValidationMaxPromptTokens
if req.Config != nil && req.Config.Validation.MaxPromptTokens > 0 {
maxTokens = req.Config.Validation.MaxPromptTokens
if req.Settings.Validation.MaxPromptTokens > 0 {
maxTokens = req.Settings.Validation.MaxPromptTokens
}
for i := range validationReq.Items {
@@ -131,9 +131,7 @@ func (v *LLMBackedValidator) Validate(ctx context.Context, req Request) (Result,
return partialResult(), err
}
transcriptDescription := ""
if req.Config != nil {
transcriptDescription = req.Config.Context.Description
}
transcriptDescription = req.Settings.Context.Description
variables, err := BuildValidationVariables(batch.Items, transcriptDescription)
if err != nil {
return partialResult(), err

View File

@@ -7,7 +7,6 @@ import (
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/glossary"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/modulecatalog"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/outcomes"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/proposals"
@@ -44,7 +43,7 @@ type Request struct {
ReplacementPolicy proposals.ReplacementPolicy `json:"replacement_policy"`
Glossary []glossary.Entry `json:"-"`
ProtectedTerms []glossary.Term `json:"-"`
Config *config.Config `json:"-"`
Settings config.Stage `json:"-"`
LLMClient StructuredLLMClient `json:"-"`
DiagnosticsWriter InteractionDiagnosticsWriter `json:"-"`
}
@@ -142,21 +141,3 @@ func rejection(index int, reasonCode string, msg string) Decision {
func unavailable(index int, reasonCode string, msg string) Decision {
return Decision{ProposalIndex: index, Approved: false, Disposition: DispositionUnavailable, ReasonCode: reasonCode, Message: strings.TrimSpace(msg)}
}
func confidenceThresholdForModule(moduleKey string, cfg *config.Config) float64 {
if cfg == nil {
return 0.0
}
switch moduleKey {
case modulecatalog.KeyGlossary:
return cfg.Thresholds.Glossary
case modulecatalog.KeyGrammar:
return cfg.Thresholds.Grammar
case modulecatalog.KeyHomophones:
return cfg.Thresholds.Homophones
case modulecatalog.KeySpokenWord:
return cfg.Thresholds.SpokenWord
default:
return 0.0
}
}