Transport structured diagnostics through the pipeline

This commit is contained in:
2026-08-27 15:29:10 +00:00
parent acb04954eb
commit ba569594a1
20 changed files with 578 additions and 65 deletions

View File

@@ -162,9 +162,10 @@ type ChunkRequest struct {
}
type ChunkPlanResult struct {
Plan source.ChunkPlan `json:"plan"`
Warnings []Warning `json:"warnings,omitempty"`
ModelCandidate *ModelCandidate `json:"-"`
Plan source.ChunkPlan `json:"plan"`
Warnings []Warning `json:"warnings,omitempty"`
Diagnostics []ProducerDiagnostic `json:"diagnostics,omitempty"`
ModelCandidate *ModelCandidate `json:"-"`
}
type Chunker interface {
@@ -283,12 +284,13 @@ const (
)
type ValidationResult struct {
Approved bool `json:"approved"`
ReasonCode string `json:"reason_code,omitempty"`
Message string `json:"message,omitempty"`
CorrectionGuidance string `json:"-"`
DiagnosticArtifactPath string `json:"diagnostic_artifact_path,omitempty"`
Warnings []Warning `json:"warnings,omitempty"`
Approved bool `json:"approved"`
ReasonCode string `json:"reason_code,omitempty"`
Message string `json:"message,omitempty"`
CorrectionGuidance string `json:"-"`
DiagnosticArtifactPath string `json:"diagnostic_artifact_path,omitempty"`
Warnings []Warning `json:"warnings,omitempty"`
Diagnostics []ProducerDiagnostic `json:"diagnostics,omitempty"`
}
type Warning struct {
@@ -302,6 +304,7 @@ type OutputRequest struct {
NormalizeOutputs []SerializedOutput `json:"normalize_outputs,omitempty"`
Rejected []RejectedOutput `json:"rejected,omitempty"`
Warnings []Warning `json:"warnings,omitempty"`
Diagnostics DiagnosticCollection `json:"diagnostics,omitempty"`
LLMProfile string `json:"llm_profile,omitempty"`
StructuredOutputRepairAttempts *int `json:"structured_output_repair_attempts,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`

View File

@@ -206,8 +206,9 @@ func CloneProducerDiagnostics(diagnostics []ProducerDiagnostic) []ProducerDiagno
// CloneDiagnosticCollection returns independent collection ownership.
func CloneDiagnosticCollection(collection DiagnosticCollection) DiagnosticCollection {
collection.Groups = make([]DiagnosticGroup, len(collection.Groups))
for index, group := range collection.Groups {
groups := collection.Groups
collection.Groups = make([]DiagnosticGroup, len(groups))
for index, group := range groups {
collection.Groups[index] = cloneDiagnosticGroup(group)
}
return collection

View File

@@ -128,6 +128,24 @@ func TestDiagnosticGroupRejectsRepeatedSampleWithEqualChunkIndex(t *testing.T) {
}
}
func TestCloneDiagnosticCollectionOwnsGroupsAndChunkIndex(t *testing.T) {
chunkIndex := 0
collection := DiagnosticCollection{Groups: []DiagnosticGroup{{
Disposition: DiagnosticDispositionAdvisory,
Category: DiagnosticCategoryDataQuality,
ReasonCode: "unresolved",
Origin: DiagnosticOrigin{Stage: DiagnosticOriginStageExtract, StepID: "extract", LaneID: "spells", ModuleKey: "dnd/spells"},
OccurrenceCount: 1,
Samples: []DiagnosticSample{{Scope: "spells[0]", Message: "Spell was not found", ChunkIndex: &chunkIndex}},
}}}
cloned := CloneDiagnosticCollection(collection)
collection.Groups[0].Samples[0].Message = "changed"
*collection.Groups[0].Samples[0].ChunkIndex = 1
if got := cloned.Groups[0].Samples[0]; got.Message != "Spell was not found" || got.ChunkIndex == nil || *got.ChunkIndex != 0 {
t.Fatalf("cloned sample = %#v, want independently owned original", got)
}
}
func validProducerDiagnostic() ProducerDiagnostic {
return ProducerDiagnostic{
Disposition: DiagnosticDispositionWarning,

View File

@@ -49,6 +49,7 @@ type TypedExtractionRequest struct {
type TypedExtractionResult[T any] struct {
Value T
Warnings []Warning
Diagnostics []ProducerDiagnostic
ModelCandidate *ModelCandidate
}
@@ -74,6 +75,7 @@ type TypedMergeRequest[T any] struct {
type TypedMergeResult[T any] struct {
Value T
Warnings []Warning
Diagnostics []ProducerDiagnostic
ModelCandidate *ModelCandidate
}
@@ -98,6 +100,7 @@ type TypedNormalizeRequest[T any] struct {
type TypedNormalizeResult[T any] struct {
Value T
Warnings []Warning
Diagnostics []ProducerDiagnostic
Retry *NormalizeRetry
ModelCandidate *ModelCandidate
}
@@ -112,9 +115,10 @@ const (
// NormalizeRetry asks the framework to retry normalization while retaining a
// safe candidate for acceptance if the retry budget is exhausted.
type NormalizeRetry struct {
ReasonCode string
Message string
FallbackWarnings []Warning
ReasonCode string
Message string
FallbackWarnings []Warning
FallbackDiagnostics []ProducerDiagnostic
}
type Normalizer[T any] interface {