package trim import ( "fmt" artifactpkg "gitea.maximumdirect.net/eric/seriatim/internal/artifact" "gitea.maximumdirect.net/eric/seriatim/schema" ) const ( SchemaMinimal = artifactpkg.OutputSchemaMinimal SchemaIntermediate = artifactpkg.OutputSchemaIntermediate SchemaFull = artifactpkg.OutputSchemaFull ) // Artifact stores a parsed seriatim output artifact of one supported schema. type Artifact struct { Schema string Full *schema.Transcript Intermediate *schema.IntermediateTranscript Minimal *schema.MinimalTranscript } // ApplyArtifactResult contains trimmed artifact output and ID mapping metadata. type ApplyArtifactResult struct { Artifact Artifact OldToNewID map[int]int RemovedIDs []int OverlapGroupsRecomputed bool } // ParseArtifactJSON parses and validates a serialized seriatim output artifact. func ParseArtifactJSON(data []byte) (Artifact, error) { parsed, err := artifactpkg.ParseOutputArtifactJSON(data) if err != nil { return Artifact{}, err } return Artifact{ Schema: parsed.Schema, Full: parsed.Full, Intermediate: parsed.Intermediate, Minimal: parsed.Minimal, }, nil } // ValidateArtifact validates an artifact against its declared schema. func ValidateArtifact(artifact Artifact) error { switch artifact.Schema { case SchemaFull: payload, err := artifact.fullPayload() if err != nil { return err } return schema.ValidateTranscript(*payload) case SchemaIntermediate: payload, err := artifact.intermediatePayload() if err != nil { return err } return schema.ValidateIntermediateTranscript(*payload) case SchemaMinimal: payload, err := artifact.minimalPayload() if err != nil { return err } return schema.ValidateMinimalTranscript(*payload) default: return fmt.Errorf("unsupported artifact schema %q", artifact.Schema) } } // Value returns the artifact value for JSON serialization. func (artifact Artifact) Value() any { switch artifact.Schema { case SchemaFull: if artifact.Full == nil { return schema.Transcript{} } return *artifact.Full case SchemaIntermediate: if artifact.Intermediate == nil { return schema.IntermediateTranscript{} } return *artifact.Intermediate case SchemaMinimal: if artifact.Minimal == nil { return schema.MinimalTranscript{} } return *artifact.Minimal default: return nil } } // SegmentCount returns the number of segments in the artifact. func (artifact Artifact) SegmentCount() int { switch artifact.Schema { case SchemaFull: if artifact.Full == nil { return 0 } return len(artifact.Full.Segments) case SchemaIntermediate: if artifact.Intermediate == nil { return 0 } return len(artifact.Intermediate.Segments) case SchemaMinimal: if artifact.Minimal == nil { return 0 } return len(artifact.Minimal.Segments) default: return 0 } } // Application returns artifact metadata application name. func (artifact Artifact) Application() string { switch artifact.Schema { case SchemaFull: if artifact.Full == nil { return "" } return artifact.Full.Metadata.Application case SchemaIntermediate: if artifact.Intermediate == nil { return "" } return artifact.Intermediate.Metadata.Application case SchemaMinimal: if artifact.Minimal == nil { return "" } return artifact.Minimal.Metadata.Application default: return "" } } // Version returns artifact metadata version. func (artifact Artifact) Version() string { switch artifact.Schema { case SchemaFull: if artifact.Full == nil { return "" } return artifact.Full.Metadata.Version case SchemaIntermediate: if artifact.Intermediate == nil { return "" } return artifact.Intermediate.Metadata.Version case SchemaMinimal: if artifact.Minimal == nil { return "" } return artifact.Minimal.Metadata.Version default: return "" } } // ApplyArtifact trims a parsed artifact while preserving its input schema. func ApplyArtifact(input Artifact, opts Options) (ApplyArtifactResult, error) { switch input.Schema { case SchemaFull: payload, err := input.fullPayload() if err != nil { return ApplyArtifactResult{}, err } result, err := Apply(*payload, opts) if err != nil { return ApplyArtifactResult{}, err } out := result.Transcript return ApplyArtifactResult{ Artifact: Artifact{ Schema: SchemaFull, Full: &out, }, OldToNewID: result.OldToNewID, RemovedIDs: result.RemovedIDs, OverlapGroupsRecomputed: true, }, nil case SchemaIntermediate: payload, err := input.intermediatePayload() if err != nil { return ApplyArtifactResult{}, err } result, err := ApplyIntermediate(*payload, opts) if err != nil { return ApplyArtifactResult{}, err } out := result.Transcript return ApplyArtifactResult{ Artifact: Artifact{ Schema: SchemaIntermediate, Intermediate: &out, }, OldToNewID: result.OldToNewID, RemovedIDs: result.RemovedIDs, OverlapGroupsRecomputed: false, }, nil case SchemaMinimal: payload, err := input.minimalPayload() if err != nil { return ApplyArtifactResult{}, err } result, err := ApplyMinimal(*payload, opts) if err != nil { return ApplyArtifactResult{}, err } out := result.Transcript return ApplyArtifactResult{ Artifact: Artifact{ Schema: SchemaMinimal, Minimal: &out, }, OldToNewID: result.OldToNewID, RemovedIDs: result.RemovedIDs, OverlapGroupsRecomputed: false, }, nil default: return ApplyArtifactResult{}, fmt.Errorf("unsupported artifact schema %q", input.Schema) } } // ConvertArtifact converts a parsed artifact to another supported output schema. func ConvertArtifact(input Artifact, outputSchema string) (Artifact, error) { if outputSchema == "" || outputSchema == input.Schema { return input, nil } switch input.Schema { case SchemaFull: payload, err := input.fullPayload() if err != nil { return Artifact{}, err } switch outputSchema { case SchemaIntermediate: out := intermediateFromFull(*payload) return Artifact{ Schema: SchemaIntermediate, Intermediate: &out, }, nil case SchemaMinimal: out := minimalFromFull(*payload) return Artifact{ Schema: SchemaMinimal, Minimal: &out, }, nil default: return Artifact{}, fmt.Errorf("unsupported output schema %q", outputSchema) } case SchemaIntermediate: payload, err := input.intermediatePayload() if err != nil { return Artifact{}, err } switch outputSchema { case SchemaMinimal: out := minimalFromIntermediate(*payload) return Artifact{ Schema: SchemaMinimal, Minimal: &out, }, nil case SchemaFull: return Artifact{}, fmt.Errorf("cannot emit %q from %q input artifact", SchemaFull, SchemaIntermediate) default: return Artifact{}, fmt.Errorf("unsupported output schema %q", outputSchema) } case SchemaMinimal: payload, err := input.minimalPayload() if err != nil { return Artifact{}, err } switch outputSchema { case SchemaIntermediate: out := intermediateFromMinimal(*payload) return Artifact{ Schema: SchemaIntermediate, Intermediate: &out, }, nil case SchemaFull: return Artifact{}, fmt.Errorf("cannot emit %q from %q input artifact", SchemaFull, SchemaMinimal) default: return Artifact{}, fmt.Errorf("unsupported output schema %q", outputSchema) } default: return Artifact{}, fmt.Errorf("unsupported input schema %q", input.Schema) } } func (artifact Artifact) fullPayload() (*schema.Transcript, error) { if artifact.Full == nil { return nil, fmt.Errorf("full artifact payload is missing") } return artifact.Full, nil } func (artifact Artifact) intermediatePayload() (*schema.IntermediateTranscript, error) { if artifact.Intermediate == nil { return nil, fmt.Errorf("intermediate artifact payload is missing") } return artifact.Intermediate, nil } func (artifact Artifact) minimalPayload() (*schema.MinimalTranscript, error) { if artifact.Minimal == nil { return nil, fmt.Errorf("minimal artifact payload is missing") } return artifact.Minimal, nil } func intermediateFromFull(input schema.Transcript) schema.IntermediateTranscript { segments := make([]schema.IntermediateSegment, len(input.Segments)) for index, segment := range input.Segments { segments[index] = schema.IntermediateSegment{ ID: segment.ID, Start: segment.Start, End: segment.End, Speaker: segment.Speaker, Text: segment.Text, Categories: append([]string(nil), segment.Categories...), } } return schema.IntermediateTranscript{ Metadata: schema.IntermediateMetadata{ Application: input.Metadata.Application, Version: input.Metadata.Version, OutputSchema: SchemaIntermediate, }, Segments: segments, } } func minimalFromFull(input schema.Transcript) schema.MinimalTranscript { segments := make([]schema.MinimalSegment, len(input.Segments)) for index, segment := range input.Segments { segments[index] = schema.MinimalSegment{ ID: segment.ID, Start: segment.Start, End: segment.End, Speaker: segment.Speaker, Text: segment.Text, } } return schema.MinimalTranscript{ Metadata: schema.MinimalMetadata{ Application: input.Metadata.Application, Version: input.Metadata.Version, OutputSchema: SchemaMinimal, }, Segments: segments, } } func minimalFromIntermediate(input schema.IntermediateTranscript) schema.MinimalTranscript { segments := make([]schema.MinimalSegment, len(input.Segments)) for index, segment := range input.Segments { segments[index] = schema.MinimalSegment{ ID: segment.ID, Start: segment.Start, End: segment.End, Speaker: segment.Speaker, Text: segment.Text, } } return schema.MinimalTranscript{ Metadata: schema.MinimalMetadata{ Application: input.Metadata.Application, Version: input.Metadata.Version, OutputSchema: SchemaMinimal, }, Segments: segments, } } func intermediateFromMinimal(input schema.MinimalTranscript) schema.IntermediateTranscript { segments := make([]schema.IntermediateSegment, len(input.Segments)) for index, segment := range input.Segments { segments[index] = schema.IntermediateSegment{ ID: segment.ID, Start: segment.Start, End: segment.End, Speaker: segment.Speaker, Text: segment.Text, } } return schema.IntermediateTranscript{ Metadata: schema.IntermediateMetadata{ Application: input.Metadata.Application, Version: input.Metadata.Version, OutputSchema: SchemaIntermediate, }, Segments: segments, } }