Files
seriatim/internal/trim/artifact.go

401 lines
10 KiB
Go

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,
}
}