diff --git a/assemblyai/types.py b/assemblyai/types.py index bafa3c7..967ce10 100644 --- a/assemblyai/types.py +++ b/assemblyai/types.py @@ -2140,7 +2140,12 @@ class Timestamp(BaseModel): class AutohighlightResult(BaseModel): count: int - rank: float + # The Auto Highlights API has been observed in production to return + # individual `results` entries with no `rank` field set (see #148). + # Treat it as optional so a TranscriptResponse parses successfully when + # any single highlight is missing the rank; the absence is then + # observable to callers via `rank is None`. + rank: Optional[float] = None text: str timestamps: List[Timestamp] diff --git a/tests/unit/test_auto_highlights.py b/tests/unit/test_auto_highlights.py index 4648760..1932b6e 100644 --- a/tests/unit/test_auto_highlights.py +++ b/tests/unit/test_auto_highlights.py @@ -93,3 +93,39 @@ def test_auto_highlights_enabled(httpx_mock: HTTPXMock): ): assert transcript_timestamp.start == response_timestamp["start"] assert transcript_timestamp.end == response_timestamp["end"] + + +def test_auto_highlights_parses_result_without_rank(httpx_mock: HTTPXMock): + """ + Regression for #148. The Auto Highlights API has been observed in + production to return individual `results` entries with no `rank` field + set, which previously raised a Pydantic ValidationError and made the + entire TranscriptResponse unparseable. The field is now Optional, so a + missing rank parses as None and the rest of the response is preserved. + """ + mock_response = factories.generate_dict_factory( + AutohighlightTranscriptResponseFactory + )() + # Strip the `rank` from the first highlight to simulate the API's + # observed behavior. Other fields (count, text, timestamps) are left + # in place. + assert mock_response["auto_highlights_result"]["results"], ( + "factory should produce at least one highlight" + ) + del mock_response["auto_highlights_result"]["results"][0]["rank"] + + _, transcript = unit_test_utils.submit_mock_transcription_request( + httpx_mock, + mock_response=mock_response, + config=aai.TranscriptionConfig(auto_highlights=True), + ) + + assert transcript.error is None + assert transcript.auto_highlights is not None + assert transcript.auto_highlights.results is not None + assert transcript.auto_highlights.results[0].rank is None + # The rest of the first result is still present, and other entries + # are unaffected. + assert transcript.auto_highlights.results[0].text is not None + if len(transcript.auto_highlights.results) > 1: + assert transcript.auto_highlights.results[1].rank is not None