Redact NPC normalization context errors

This commit is contained in:
2026-07-26 02:38:03 +00:00
parent d63e5c6852
commit 5ad661f95f
2 changed files with 33 additions and 11 deletions

View File

@@ -119,15 +119,15 @@ func buildNormalizeContextMaterials(doc *source.SourceDocument, records []dnd.NP
windows, err := normalizeContextWindows(doc.Units, coalesceIntervals(intervals), cited)
if err != nil {
return normalizeContextMaterials{}, false, fmt.Errorf("build NPC normalization context: copy source metadata: %w", err)
return normalizeContextMaterials{}, false, fmt.Errorf("build NPC normalization context: invalid source metadata")
}
candidateContent, err := json.Marshal(normalizeCandidateInput{NPCs: candidates})
if err != nil {
return normalizeContextMaterials{}, false, fmt.Errorf("build NPC normalization context: encode candidates: %w", err)
return normalizeContextMaterials{}, false, fmt.Errorf("build NPC normalization context: invalid candidate material")
}
transcriptContent, err := json.Marshal(normalizeTranscriptInput{Windows: windows})
if err != nil {
return normalizeContextMaterials{}, false, fmt.Errorf("build NPC normalization context: encode transcript: %w", err)
return normalizeContextMaterials{}, false, fmt.Errorf("build NPC normalization context: invalid transcript material")
}
return normalizeContextMaterials{
Candidates: newNormalizeInputMaterial("candidates", candidateContent),

View File

@@ -136,17 +136,39 @@ func TestNormalizeInvalidStructuredOutputAndOperationalErrorsRemainDistinct(t *t
}
}
func TestNormalizeRejectsContextEncodingFailuresWithoutLeakingContent(t *testing.T) {
func TestNormalizeRedactsContextMaterialFailures(t *testing.T) {
client := &recordingNPCNormalizerClient{}
normalizer := newNormalizer(t, client)
doc := semanticDocument()
doc.Units[0].Metadata = map[string]any{"invalid": math.NaN()}
input := dnd.NPCList{NPCs: []dnd.NPC{
{Name: "Mira", SourceRefs: []source.SourceRef{{SourceID: doc.ID, StartUnitID: 10, EndUnitID: 10}}},
{Name: "Mira Thorn", SourceRefs: []source.SourceRef{{SourceID: doc.ID, StartUnitID: 20, EndUnitID: 20}}},
const (
metadataKey = "normalizer-sensitive-metadata-key"
metadataValue = "normalizer-sensitive-metadata-value"
sourceID = "normalizer-sensitive-source-id"
originPath = "file:///normalizer-sensitive-origin.json"
transcript = "normalizer-sensitive-transcript"
firstName = "Normalizer Sensitive"
secondName = "Normalizer Sensitive Alias"
)
doc := &source.SourceDocument{ID: sourceID, Units: []source.SourceUnit{
{ID: 10, Kind: "speech", Text: transcript, Metadata: map[string]any{metadataKey: math.NaN(), "value": metadataValue}},
{ID: 20, Kind: "speech", Text: "other context"},
}}
if _, err := normalizer.Normalize(context.Background(), normalizeRequestWithSource(input, doc)); err == nil || !strings.Contains(err.Error(), "build semantic context") || strings.Contains(err.Error(), doc.Units[0].Text) || len(client.requests) != 0 {
t.Fatalf("Normalize() error = %v, calls = %d; want safe preparation error before completion", err, len(client.requests))
input := dnd.NPCList{NPCs: []dnd.NPC{
{Name: firstName, SourceRefs: []source.SourceRef{{SourceID: doc.ID, StartUnitID: 10, EndUnitID: 10}}},
{Name: secondName, SourceRefs: []source.SourceRef{{SourceID: doc.ID, StartUnitID: 20, EndUnitID: 20}}},
}}
request := normalizeRequestWithSource(input, doc)
request.SourceInput = contracts.NewLLMInputMaterial("source", "application/json", []byte(transcript), "sha256:test", originPath)
_, err := normalizer.Normalize(context.Background(), request)
if err == nil || !strings.Contains(err.Error(), "build NPC normalization context: invalid source metadata") {
t.Fatalf("Normalize() error = %v; want content-safe context-material failure", err)
}
for _, forbidden := range []string{metadataKey, metadataValue, transcript, firstName, secondName, sourceID, originPath, "float64", "non-finite"} {
if strings.Contains(err.Error(), forbidden) {
t.Fatalf("Normalize() error leaked %q: %v", forbidden, err)
}
}
if len(client.requests) != 0 {
t.Fatalf("completion calls = %d, want context failure before completion", len(client.requests))
}
}