Bind extraction reuse to transcript identity
This commit is contained in:
76
internal/artifacts/extraction_input.go
Normal file
76
internal/artifacts/extraction_input.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/fileops"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
||||
)
|
||||
|
||||
const extractionMetadataInput = "direct_input"
|
||||
|
||||
// ExtractionInputIdentity identifies the exact trimmed transcript used for extraction.
|
||||
type ExtractionInputIdentity struct {
|
||||
Path string `json:"-"`
|
||||
Checksum string `json:"checksum"`
|
||||
SourceID string `json:"source_id"`
|
||||
ProducerStage string `json:"producer_stage"`
|
||||
OutputKind string `json:"output_kind"`
|
||||
ProducerRunID string `json:"producer_run_id"`
|
||||
Provenance string `json:"provenance"`
|
||||
}
|
||||
|
||||
// ResolveExtractionInputIdentity resolves and hashes the direct transcript input.
|
||||
func ResolveExtractionInputIdentity(paths SessionPaths, m *manifest.Manifest) (ExtractionInputIdentity, error) {
|
||||
resolved, err := ResolveSessionArtifact(paths, m, ArtifactTranscriptFinalTrimmed)
|
||||
if err != nil {
|
||||
return ExtractionInputIdentity{}, err
|
||||
}
|
||||
path, err := filepath.Abs(resolved.Path)
|
||||
if err != nil {
|
||||
return ExtractionInputIdentity{}, fmt.Errorf("resolve trimmed transcript path: %w", err)
|
||||
}
|
||||
bytes, err := fileops.ReadRegularFile(path, MaxResolvedArtifactBytes)
|
||||
if err != nil {
|
||||
return ExtractionInputIdentity{}, fmt.Errorf("read trimmed transcript: %w", err)
|
||||
}
|
||||
digest := sha256.Sum256(bytes)
|
||||
return ExtractionInputIdentity{
|
||||
Path: filepath.Clean(path),
|
||||
Checksum: hex.EncodeToString(digest[:]),
|
||||
SourceID: resolved.ID,
|
||||
ProducerStage: resolved.ProducerStage,
|
||||
OutputKind: resolved.OutputKind,
|
||||
ProducerRunID: resolved.ProducerRunID,
|
||||
Provenance: resolved.Provenance,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Metadata returns the durable fields needed to compare this identity on reuse.
|
||||
func (identity ExtractionInputIdentity) Metadata() map[string]any {
|
||||
return map[string]any{
|
||||
"checksum": identity.Checksum,
|
||||
"source_id": identity.SourceID,
|
||||
"producer_stage": identity.ProducerStage,
|
||||
"output_kind": identity.OutputKind,
|
||||
"producer_run_id": identity.ProducerRunID,
|
||||
"provenance": identity.Provenance,
|
||||
}
|
||||
}
|
||||
|
||||
func extractionInputMatchesMetadata(metadata map[string]any, current ExtractionInputIdentity) bool {
|
||||
stored, ok := metadata[extractionMetadataInput].(map[string]any)
|
||||
if !ok || strings.TrimSpace(current.Checksum) == "" || strings.TrimSpace(current.SourceID) == "" {
|
||||
return false
|
||||
}
|
||||
for key, want := range current.Metadata() {
|
||||
if extractionMetadataString(stored, key) != want {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user