File size: 3,381 Bytes
51e84d2
 
 
 
 
 
 
 
fc1dcbc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# License: Apache-2.0 License
# Created by: Patrick Lumbantobing, VertoX-AI
# Copyright (c) 2026 VertoX-AI. All rights reserved.
#
# This work is licensed under the Apache-2.0 License.
# To view a copy of this license, visit
# https://www.apache.org/licenses/LICENSE-2.0

"""Extract exactly the six contract boundary fields from validated alignments."""

from __future__ import annotations

from dataclasses import dataclass

from speech_text_data_aligners.core.errors import AlignmentValidationError
from speech_text_data_aligners.core.models import AlignmentRequest, AlignmentResult
from speech_text_data_aligners.core.validation import validate_alignment_result

BOUNDARY_COLUMNS = (
    "first_word_text",
    "first_word_start_s",
    "first_word_end_s",
    "last_word_text",
    "last_word_start_s",
    "last_word_end_s",
)


@dataclass(frozen=True, slots=True)
class BoundaryFields:
    """Hold original-facing first/last lexical surfaces and acoustic boundaries.

    Parameters
    ----------
        first_word_text: Original surface of the first lexical word.
        first_word_start_s: Acoustic start of the first lexical word.
        first_word_end_s: Acoustic end of the first lexical word.
        last_word_text: Original surface of the last lexical word.
        last_word_start_s: Acoustic start of the last lexical word.
        last_word_end_s: Acoustic end of the last lexical word.

    """

    first_word_text: str
    first_word_start_s: float
    first_word_end_s: float
    last_word_text: str
    last_word_start_s: float
    last_word_end_s: float

    def as_dict(self) -> dict[str, str | float]:
        """Return fields in the exact output-schema insertion order.

        Returns:
            A new ordered dictionary with the six contract column names.

        """
        return {
            "first_word_text": self.first_word_text,
            "first_word_start_s": self.first_word_start_s,
            "first_word_end_s": self.first_word_end_s,
            "last_word_text": self.last_word_text,
            "last_word_start_s": self.last_word_start_s,
            "last_word_end_s": self.last_word_end_s,
        }


def extract_boundary_fields(
    request: AlignmentRequest,
    result: AlignmentResult,
) -> BoundaryFields:
    """Validate a result and select its first/last lexical alignments.

    A one-word utterance intentionally uses the same record for both endpoints.

    Parameters
    ----------
        request: Authoritative request used for semantic validation.
        result: Candidate complete result.

    Returns
    -------
        Exactly six backend-neutral boundary values using original word surfaces.

    Raises
    ------
        AlignmentValidationError: If validation fails or no aligned words exist.
        TranscriptError: If the authoritative transcript has no lexical words.

    """
    validated = validate_alignment_result(request, result)
    if not validated.words:
        raise AlignmentValidationError("cannot extract boundaries from no words")
    first = validated.words[0]
    last = validated.words[-1]
    return BoundaryFields(
        first_word_text=first.lexical_word.text,
        first_word_start_s=first.start_s,
        first_word_end_s=first.end_s,
        last_word_text=last.lexical_word.text,
        last_word_start_s=last.start_s,
        last_word_end_s=last.end_s,
    )