From 5986ec089dda2ec7681a1173f7fa09ab1040a039 Mon Sep 17 00:00:00 2001 From: DX-Bandwidth Date: Tue, 23 Jun 2026 15:05:09 +0000 Subject: [PATCH 1/3] Generate SDK with OpenAPI Generator Version --- .openapi-generator/FILES | 2 + README.md | 1 + bandwidth.yml | 70 +++++++++++ bandwidth/__init__.py | 2 + bandwidth/models/__init__.py | 1 + .../models/recording_transcription_clip.py | 110 ++++++++++++++++++ bandwidth/models/recording_transcriptions.py | 16 ++- bandwidth/models/transcription.py | 4 +- docs/RecordingTranscriptionClip.md | 33 ++++++ docs/RecordingTranscriptions.md | 1 + docs/Transcription.md | 1 + 11 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 bandwidth/models/recording_transcription_clip.py create mode 100644 docs/RecordingTranscriptionClip.md diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES index 50e7d8f3..a070dc27 100644 --- a/.openapi-generator/FILES +++ b/.openapi-generator/FILES @@ -172,6 +172,7 @@ bandwidth/models/rbm_web_view_enum.py bandwidth/models/recording_available_callback.py bandwidth/models/recording_complete_callback.py bandwidth/models/recording_state_enum.py +bandwidth/models/recording_transcription_clip.py bandwidth/models/recording_transcription_metadata.py bandwidth/models/recording_transcriptions.py bandwidth/models/redirect_callback.py @@ -380,6 +381,7 @@ docs/RbmWebViewEnum.md docs/RecordingAvailableCallback.md docs/RecordingCompleteCallback.md docs/RecordingStateEnum.md +docs/RecordingTranscriptionClip.md docs/RecordingTranscriptionMetadata.md docs/RecordingTranscriptions.md docs/RecordingsApi.md diff --git a/README.md b/README.md index b965734b..c0eb322f 100644 --- a/README.md +++ b/README.md @@ -314,6 +314,7 @@ Class | Method | HTTP request | Description - [RecordingAvailableCallback](docs/RecordingAvailableCallback.md) - [RecordingCompleteCallback](docs/RecordingCompleteCallback.md) - [RecordingStateEnum](docs/RecordingStateEnum.md) + - [RecordingTranscriptionClip](docs/RecordingTranscriptionClip.md) - [RecordingTranscriptionMetadata](docs/RecordingTranscriptionMetadata.md) - [RecordingTranscriptions](docs/RecordingTranscriptions.md) - [RedirectCallback](docs/RedirectCallback.md) diff --git a/bandwidth.yml b/bandwidth.yml index a380e26d..fd39c5cb 100644 --- a/bandwidth.yml +++ b/bandwidth.yml @@ -4354,6 +4354,72 @@ components: type: array items: $ref: '#/components/schemas/transcription' + clips: + type: array + description: >- + A list of individual speech clips with speaker, timing, and + confidence information. + items: + $ref: '#/components/schemas/recordingTranscriptionClip' + example: + transcripts: + - speaker: 0 + text: Hi, is Thursday at two still good for you? Perfect, talk soon. + confidence: 0.96 + - speaker: 1 + text: Yes, that works great. See you then! Sounds good, bye! + confidence: 0.97 + clips: + - speaker: 0 + text: Hi, is Thursday at two still good for you? + confidence: 0.97 + startTimeSeconds: 0.4 + endTimeSeconds: 3.1 + - speaker: 1 + text: Yes, that works great. See you then! + confidence: 0.95 + startTimeSeconds: 3.8 + endTimeSeconds: 6.2 + - speaker: 0 + text: Perfect, talk soon. + confidence: 0.94 + startTimeSeconds: 6.9 + endTimeSeconds: 8.1 + - speaker: 1 + text: Sounds good, bye! + confidence: 0.98 + startTimeSeconds: 8.5 + endTimeSeconds: 9.7 + recordingTranscriptionClip: + type: object + properties: + speaker: + type: integer + description: Zero-based index identifying the speaker. + example: 0 + text: + type: string + description: The transcribed text of this clip. + example: Hi there, thanks for calling! + confidence: + type: number + format: double + minimum: 0 + maximum: 1 + description: >- + How confident the transcription engine was in transcribing this clip + (from `0.0` to `1.0`). + example: 0.85 + startTimeSeconds: + type: number + format: double + description: The start time of this clip within the recording, in seconds. + example: 2.3 + endTimeSeconds: + type: number + format: double + description: The end time of this clip within the recording, in seconds. + example: 3.1 callTranscriptionMetadataList: type: array items: @@ -5480,6 +5546,10 @@ components: transcription: type: object properties: + speaker: + type: integer + description: Zero-based index identifying the speaker. + example: 0 text: type: string description: The transcribed text diff --git a/bandwidth/__init__.py b/bandwidth/__init__.py index 13602e40..2356d8c2 100644 --- a/bandwidth/__init__.py +++ b/bandwidth/__init__.py @@ -192,6 +192,7 @@ "RecordingAvailableCallback", "RecordingCompleteCallback", "RecordingStateEnum", + "RecordingTranscriptionClip", "RecordingTranscriptionMetadata", "RecordingTranscriptions", "RedirectCallback", @@ -418,6 +419,7 @@ from bandwidth.models.recording_available_callback import RecordingAvailableCallback as RecordingAvailableCallback from bandwidth.models.recording_complete_callback import RecordingCompleteCallback as RecordingCompleteCallback from bandwidth.models.recording_state_enum import RecordingStateEnum as RecordingStateEnum +from bandwidth.models.recording_transcription_clip import RecordingTranscriptionClip as RecordingTranscriptionClip from bandwidth.models.recording_transcription_metadata import RecordingTranscriptionMetadata as RecordingTranscriptionMetadata from bandwidth.models.recording_transcriptions import RecordingTranscriptions as RecordingTranscriptions from bandwidth.models.redirect_callback import RedirectCallback as RedirectCallback diff --git a/bandwidth/models/__init__.py b/bandwidth/models/__init__.py index 9e15825d..701d2a00 100644 --- a/bandwidth/models/__init__.py +++ b/bandwidth/models/__init__.py @@ -166,6 +166,7 @@ from bandwidth.models.recording_available_callback import RecordingAvailableCallback from bandwidth.models.recording_complete_callback import RecordingCompleteCallback from bandwidth.models.recording_state_enum import RecordingStateEnum +from bandwidth.models.recording_transcription_clip import RecordingTranscriptionClip from bandwidth.models.recording_transcription_metadata import RecordingTranscriptionMetadata from bandwidth.models.recording_transcriptions import RecordingTranscriptions from bandwidth.models.redirect_callback import RedirectCallback diff --git a/bandwidth/models/recording_transcription_clip.py b/bandwidth/models/recording_transcription_clip.py new file mode 100644 index 00000000..e7d0394d --- /dev/null +++ b/bandwidth/models/recording_transcription_clip.py @@ -0,0 +1,110 @@ +# coding: utf-8 + +""" + Bandwidth + + Bandwidth's Communication APIs + + The version of the OpenAPI document: 1.0.0 + Contact: letstalk@bandwidth.com + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictFloat, StrictInt, StrictStr +from typing import Any, ClassVar, Dict, List, Optional, Union +from typing_extensions import Annotated +from typing import Optional, Set +from typing_extensions import Self + +class RecordingTranscriptionClip(BaseModel): + """ + RecordingTranscriptionClip + """ # noqa: E501 + speaker: Optional[StrictInt] = Field(default=None, description="Zero-based index identifying the speaker.") + text: Optional[StrictStr] = Field(default=None, description="The transcribed text of this clip.") + confidence: Optional[Union[Annotated[float, Field(le=1, strict=True, ge=0)], Annotated[int, Field(le=1, strict=True, ge=0)]]] = Field(default=None, description="How confident the transcription engine was in transcribing this clip (from `0.0` to `1.0`).") + start_time_seconds: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="The start time of this clip within the recording, in seconds.", alias="startTimeSeconds") + end_time_seconds: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="The end time of this clip within the recording, in seconds.", alias="endTimeSeconds") + additional_properties: Dict[str, Any] = {} + __properties: ClassVar[List[str]] = ["speaker", "text", "confidence", "startTimeSeconds", "endTimeSeconds"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of RecordingTranscriptionClip from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + * Fields in `self.additional_properties` are added to the output dict. + """ + excluded_fields: Set[str] = set([ + "additional_properties", + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # puts key-value pairs in additional_properties in the top level + if self.additional_properties is not None: + for _key, _value in self.additional_properties.items(): + _dict[_key] = _value + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of RecordingTranscriptionClip from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "speaker": obj.get("speaker"), + "text": obj.get("text"), + "confidence": obj.get("confidence"), + "startTimeSeconds": obj.get("startTimeSeconds"), + "endTimeSeconds": obj.get("endTimeSeconds") + }) + # store additional fields in additional_properties + for _key in obj.keys(): + if _key not in cls.__properties: + _obj.additional_properties[_key] = obj.get(_key) + + return _obj + + diff --git a/bandwidth/models/recording_transcriptions.py b/bandwidth/models/recording_transcriptions.py index f30910e5..236006b3 100644 --- a/bandwidth/models/recording_transcriptions.py +++ b/bandwidth/models/recording_transcriptions.py @@ -18,8 +18,9 @@ import re # noqa: F401 import json -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel, ConfigDict, Field from typing import Any, ClassVar, Dict, List, Optional +from bandwidth.models.recording_transcription_clip import RecordingTranscriptionClip from bandwidth.models.transcription import Transcription from typing import Optional, Set from typing_extensions import Self @@ -29,8 +30,9 @@ class RecordingTranscriptions(BaseModel): RecordingTranscriptions """ # noqa: E501 transcripts: Optional[List[Transcription]] = None + clips: Optional[List[RecordingTranscriptionClip]] = Field(default=None, description="A list of individual speech clips with speaker, timing, and confidence information.") additional_properties: Dict[str, Any] = {} - __properties: ClassVar[List[str]] = ["transcripts"] + __properties: ClassVar[List[str]] = ["transcripts", "clips"] model_config = ConfigDict( populate_by_name=True, @@ -80,6 +82,13 @@ def to_dict(self) -> Dict[str, Any]: if _item_transcripts: _items.append(_item_transcripts.to_dict()) _dict['transcripts'] = _items + # override the default output from pydantic by calling `to_dict()` of each item in clips (list) + _items = [] + if self.clips: + for _item_clips in self.clips: + if _item_clips: + _items.append(_item_clips.to_dict()) + _dict['clips'] = _items # puts key-value pairs in additional_properties in the top level if self.additional_properties is not None: for _key, _value in self.additional_properties.items(): @@ -97,7 +106,8 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: return cls.model_validate(obj) _obj = cls.model_validate({ - "transcripts": [Transcription.from_dict(_item) for _item in obj["transcripts"]] if obj.get("transcripts") is not None else None + "transcripts": [Transcription.from_dict(_item) for _item in obj["transcripts"]] if obj.get("transcripts") is not None else None, + "clips": [RecordingTranscriptionClip.from_dict(_item) for _item in obj["clips"]] if obj.get("clips") is not None else None }) # store additional fields in additional_properties for _key in obj.keys(): diff --git a/bandwidth/models/transcription.py b/bandwidth/models/transcription.py index 9934ee1a..71a6679e 100644 --- a/bandwidth/models/transcription.py +++ b/bandwidth/models/transcription.py @@ -27,10 +27,11 @@ class Transcription(BaseModel): """ Transcription """ # noqa: E501 + speaker: Optional[StrictInt] = Field(default=None, description="Zero-based index identifying the speaker.") text: Optional[StrictStr] = Field(default=None, description="The transcribed text") confidence: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="The confidence on the recognized content, ranging from `0.0` to `1.0` with `1.0` being the highest confidence.") additional_properties: Dict[str, Any] = {} - __properties: ClassVar[List[str]] = ["text", "confidence"] + __properties: ClassVar[List[str]] = ["speaker", "text", "confidence"] model_config = ConfigDict( populate_by_name=True, @@ -90,6 +91,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: return cls.model_validate(obj) _obj = cls.model_validate({ + "speaker": obj.get("speaker"), "text": obj.get("text"), "confidence": obj.get("confidence") }) diff --git a/docs/RecordingTranscriptionClip.md b/docs/RecordingTranscriptionClip.md new file mode 100644 index 00000000..a090908e --- /dev/null +++ b/docs/RecordingTranscriptionClip.md @@ -0,0 +1,33 @@ +# RecordingTranscriptionClip + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**speaker** | **int** | Zero-based index identifying the speaker. | [optional] +**text** | **str** | The transcribed text of this clip. | [optional] +**confidence** | **float** | How confident the transcription engine was in transcribing this clip (from `0.0` to `1.0`). | [optional] +**start_time_seconds** | **float** | The start time of this clip within the recording, in seconds. | [optional] +**end_time_seconds** | **float** | The end time of this clip within the recording, in seconds. | [optional] + +## Example + +```python +from bandwidth.models.recording_transcription_clip import RecordingTranscriptionClip + +# TODO update the JSON string below +json = "{}" +# create an instance of RecordingTranscriptionClip from a JSON string +recording_transcription_clip_instance = RecordingTranscriptionClip.from_json(json) +# print the JSON string representation of the object +print(RecordingTranscriptionClip.to_json()) + +# convert the object into a dict +recording_transcription_clip_dict = recording_transcription_clip_instance.to_dict() +# create an instance of RecordingTranscriptionClip from a dict +recording_transcription_clip_from_dict = RecordingTranscriptionClip.from_dict(recording_transcription_clip_dict) +``` +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/docs/RecordingTranscriptions.md b/docs/RecordingTranscriptions.md index 6379fc6c..606452a1 100644 --- a/docs/RecordingTranscriptions.md +++ b/docs/RecordingTranscriptions.md @@ -6,6 +6,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **transcripts** | [**List[Transcription]**](Transcription.md) | | [optional] +**clips** | [**List[RecordingTranscriptionClip]**](RecordingTranscriptionClip.md) | A list of individual speech clips with speaker, timing, and confidence information. | [optional] ## Example diff --git a/docs/Transcription.md b/docs/Transcription.md index 5f9a2508..c5770ee7 100644 --- a/docs/Transcription.md +++ b/docs/Transcription.md @@ -5,6 +5,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- +**speaker** | **int** | Zero-based index identifying the speaker. | [optional] **text** | **str** | The transcribed text | [optional] **confidence** | **float** | The confidence on the recognized content, ranging from `0.0` to `1.0` with `1.0` being the highest confidence. | [optional] From 0740ea3730ea83f9f33b7ca9b07f615971b05352 Mon Sep 17 00:00:00 2001 From: ckoegel Date: Tue, 23 Jun 2026 14:27:46 -0400 Subject: [PATCH 2/3] unit tests --- .../test_recording_transcription_clip.py | 59 +++++++++++++++++++ .../models/test_recording_transcriptions.py | 19 +++++- test/unit/models/test_transcription.py | 2 + 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 test/unit/models/test_recording_transcription_clip.py diff --git a/test/unit/models/test_recording_transcription_clip.py b/test/unit/models/test_recording_transcription_clip.py new file mode 100644 index 00000000..95d0f3ab --- /dev/null +++ b/test/unit/models/test_recording_transcription_clip.py @@ -0,0 +1,59 @@ +# coding: utf-8 + +""" + Bandwidth + + Bandwidth's Communication APIs + + The version of the OpenAPI document: 1.0.0 + Contact: letstalk@bandwidth.com + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +import unittest + +from bandwidth.models.recording_transcription_clip import RecordingTranscriptionClip + +class TestRecordingTranscriptionClip(unittest.TestCase): + """RecordingTranscriptionClip unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def make_instance(self, include_optional) -> RecordingTranscriptionClip: + """Test RecordingTranscriptionClip + include_optional is a boolean, when False only required + params are included, when True both required and + optional params are included """ + if include_optional: + return RecordingTranscriptionClip( + speaker = 0, + text = 'Hi there, thanks for calling!', + confidence = 0.85, + start_time_seconds = 2.3, + end_time_seconds = 3.1 + ) + else: + return RecordingTranscriptionClip( + ) + + + def testRecordingTranscriptionClip(self): + """Test RecordingTranscriptionClip""" + instance = self.make_instance(True) + assert instance is not None + assert isinstance(instance, RecordingTranscriptionClip) + assert instance.speaker == 0 + assert instance.text == 'Hi there, thanks for calling!' + assert instance.confidence == 0.85 + assert instance.start_time_seconds == 2.3 + assert instance.end_time_seconds == 3.1 + +if __name__ == '__main__': + unittest.main() diff --git a/test/unit/models/test_recording_transcriptions.py b/test/unit/models/test_recording_transcriptions.py index c5e876ee..c7b5c5a1 100644 --- a/test/unit/models/test_recording_transcriptions.py +++ b/test/unit/models/test_recording_transcriptions.py @@ -17,6 +17,7 @@ from bandwidth.models.recording_transcriptions import RecordingTranscriptions from bandwidth.models.transcription import Transcription +from bandwidth.models.recording_transcription_clip import RecordingTranscriptionClip class TestRecordingTranscriptions(unittest.TestCase): """RecordingTranscriptions unit test stubs""" @@ -38,7 +39,16 @@ def make_instance(self, include_optional) -> RecordingTranscriptions: Transcription( text = 'Nice talking to you, friend!', confidence = 0.9, ) - ] + ], + clips = [ + RecordingTranscriptionClip( + speaker = 0, + text = 'Hi there, thanks for calling!', + confidence = 0.85, + start_time_seconds = 2.3, + end_time_seconds = 3.1 + ) + ] ) else: return RecordingTranscriptions( @@ -53,6 +63,13 @@ def testRecordingTranscriptions(self): assert isinstance(instance.transcripts[0], Transcription) assert instance.transcripts[0].text == 'Nice talking to you, friend!' assert instance.transcripts[0].confidence == 0.9 + assert len(instance.clips) == 1 + assert isinstance(instance.clips[0], RecordingTranscriptionClip) + assert instance.clips[0].speaker == 0 + assert instance.clips[0].text == 'Hi there, thanks for calling!' + assert instance.clips[0].confidence == 0.85 + assert instance.clips[0].start_time_seconds == 2.3 + assert instance.clips[0].end_time_seconds == 3.1 if __name__ == '__main__': unittest.main() diff --git a/test/unit/models/test_transcription.py b/test/unit/models/test_transcription.py index 2f4b0af1..d561b098 100644 --- a/test/unit/models/test_transcription.py +++ b/test/unit/models/test_transcription.py @@ -33,6 +33,7 @@ def make_instance(self, include_optional) -> Transcription: optional params are included """ if include_optional: return Transcription( + speaker = 0, text = 'Nice talking to you, friend!', confidence = 0.9 ) @@ -45,6 +46,7 @@ def testTranscription(self): instance = self.make_instance(True) assert instance is not None assert isinstance(instance, Transcription) + assert instance.speaker == 0 assert instance.text == 'Nice talking to you, friend!' assert instance.confidence == 0.9 From 0ffb751f5a68008eb1e548bc764c7ec4adef903d Mon Sep 17 00:00:00 2001 From: ckoegel Date: Tue, 23 Jun 2026 14:30:11 -0400 Subject: [PATCH 3/3] api unit test --- test/unit/api/test_recordings_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/api/test_recordings_api.py b/test/unit/api/test_recordings_api.py index 78c66d41..ee52c812 100644 --- a/test/unit/api/test_recordings_api.py +++ b/test/unit/api/test_recordings_api.py @@ -203,6 +203,7 @@ def test_get_recording_transcription(self) -> None: assert_that(response.data, instance_of(RecordingTranscriptions)) assert_that(response.data.transcripts, instance_of(list)) assert_that(response.data.transcripts[0], instance_of(Transcription)) + assert_that(response.data.transcripts[0].speaker, instance_of(int)) assert_that(response.data.transcripts[0].text, instance_of(str)) assert_that(response.data.transcripts[0].confidence, instance_of(float))