Skip to content

Commit 5eae33b

Browse files
dlange-aaiAssemblyAI
andauthored
chore: sync sdk code with DeepLearning repo (#193)
Co-authored-by: AssemblyAI <engineering.sdk@assemblyai.com>
1 parent 5f1f5dc commit 5eae33b

4 files changed

Lines changed: 155 additions & 1 deletion

File tree

assemblyai/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.62.0"
1+
__version__ = "0.63.0"

assemblyai/transcriber.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,30 @@ def utterances(self) -> Optional[List[types.Utterance]]:
491491

492492
return self._impl.transcript.utterances
493493

494+
@property
495+
def unredacted_text(self) -> Optional[str]:
496+
"The unredacted transcript text, when `redact_pii_return_unredacted` was enabled."
497+
if not self._impl.transcript:
498+
raise ValueError("The internal Transcript object is None.")
499+
500+
return self._impl.transcript.unredacted_text
501+
502+
@property
503+
def unredacted_words(self) -> Optional[List[types.Word]]:
504+
"The unredacted list of words, when `redact_pii_return_unredacted` was enabled."
505+
if not self._impl.transcript:
506+
raise ValueError("The internal Transcript object is None.")
507+
508+
return self._impl.transcript.unredacted_words
509+
510+
@property
511+
def unredacted_utterances(self) -> Optional[List[types.Utterance]]:
512+
"The unredacted list of utterances, when `redact_pii_return_unredacted` was enabled."
513+
if not self._impl.transcript:
514+
raise ValueError("The internal Transcript object is None.")
515+
516+
return self._impl.transcript.unredacted_utterances
517+
494518
@property
495519
def confidence(self) -> Optional[float]:
496520
"The confidence our model has in the transcribed text, between 0 and 1"

assemblyai/types.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,8 @@ class RawTranscriptionConfig(BaseModel):
876876
"The list of PII Redaction policies to enable."
877877
redact_pii_sub: Optional[PIISubstitutionPolicy] = None
878878
"The replacement logic for detected PII."
879+
redact_pii_return_unredacted: Optional[bool] = None
880+
"If `redact_pii` is enabled, also return the unredacted text/words/utterances alongside the redacted fields."
879881

880882
speaker_labels: Optional[bool] = None
881883
"Enable Speaker Diarization."
@@ -1008,6 +1010,7 @@ def __init__(
10081010
redact_pii_audio_options: Optional[RedactPiiAudioOptions] = None,
10091011
redact_pii_policies: Optional[List[PIIRedactionPolicy]] = None,
10101012
redact_pii_sub: Optional[PIISubstitutionPolicy] = None,
1013+
redact_pii_return_unredacted: Optional[bool] = None,
10111014
speaker_labels: Optional[bool] = None,
10121015
speakers_expected: Optional[int] = None,
10131016
speaker_options: Optional[SpeakerOptions] = None,
@@ -1060,6 +1063,7 @@ def __init__(
10601063
redact_pii_audio_options: Options for controlling PII audio redaction behavior (e.g., override the redaction method to silence).
10611064
redact_pii_policies: The list of PII Redaction policies to enable.
10621065
redact_pii_sub: The replacement logic for detected PII.
1066+
redact_pii_return_unredacted: If `redact_pii` is enabled, also return the unredacted text/words/utterances on the transcript response. Requires `redact_pii=True`.
10631067
speaker_labels: Enable Speaker Diarization.
10641068
speakers_expected: The number of speakers you expect to hear in your audio file. Up to 10 speakers are supported.
10651069
speaker_options: Advanced options for controlling speaker diarization parameters, including min and max speakers expected.
@@ -1117,6 +1121,7 @@ def __init__(
11171121
redact_pii_audio_options,
11181122
redact_pii_policies,
11191123
redact_pii_sub,
1124+
redact_pii_return_unredacted,
11201125
)
11211126
self.set_speaker_diarization(speaker_labels, speakers_expected, speaker_options)
11221127
self.set_content_safety(content_safety, content_safety_confidence)
@@ -1397,6 +1402,12 @@ def redact_pii_sub(self) -> Optional[PIISubstitutionPolicy]:
13971402

13981403
return self._raw_transcription_config.redact_pii_sub
13991404

1405+
@property
1406+
def redact_pii_return_unredacted(self) -> Optional[bool]:
1407+
"Returns whether the unredacted text/words/utterances should also be returned alongside redacted fields."
1408+
1409+
return self._raw_transcription_config.redact_pii_return_unredacted
1410+
14001411
@property
14011412
def speaker_labels(self) -> Optional[bool]:
14021413
"Returns the status of the Speaker Diarization feature."
@@ -1797,6 +1808,7 @@ def set_redact_pii(
17971808
redact_audio_options: Optional[RedactPiiAudioOptions] = None,
17981809
policies: Optional[List[PIIRedactionPolicy]] = None,
17991810
substitution: Optional[PIISubstitutionPolicy] = None,
1811+
return_unredacted: Optional[bool] = None,
18001812
) -> Self:
18011813
"""
18021814
Enables Personal Identifiable Information (PII) Redaction feature.
@@ -1808,6 +1820,7 @@ def set_redact_pii(
18081820
redact_audio_options: Options for controlling PII audio redaction behavior (e.g., override the redaction method to silence).
18091821
policies: A list of PII redaction policies to enable.
18101822
substitution: The replacement logic for detected PII (`PIISubstutionPolicy.hash` by default).
1823+
return_unredacted: Also return the unredacted text/words/utterances on the transcript response. Only valid when redaction is enabled.
18111824
"""
18121825

18131826
if not enable:
@@ -1817,6 +1830,7 @@ def set_redact_pii(
18171830
self._raw_transcription_config.redact_pii_audio_options = None
18181831
self._raw_transcription_config.redact_pii_policies = None
18191832
self._raw_transcription_config.redact_pii_sub = None
1833+
self._raw_transcription_config.redact_pii_return_unredacted = None
18201834

18211835
return self
18221836

@@ -1829,6 +1843,7 @@ def set_redact_pii(
18291843
self._raw_transcription_config.redact_pii_audio_options = redact_audio_options
18301844
self._raw_transcription_config.redact_pii_policies = policies
18311845
self._raw_transcription_config.redact_pii_sub = substitution
1846+
self._raw_transcription_config.redact_pii_return_unredacted = return_unredacted
18321847

18331848
return self
18341849

@@ -2280,6 +2295,8 @@ class BaseTranscript(BaseModel):
22802295
"The list of PII Redaction policies to enable."
22812296
redact_pii_sub: Optional[PIISubstitutionPolicy] = None
22822297
"The replacement logic for detected PII."
2298+
redact_pii_return_unredacted: Optional[bool] = None
2299+
"If `redact_pii` is enabled, also return the unredacted text/words/utterances alongside the redacted fields."
22832300

22842301
speaker_labels: Optional[bool] = None
22852302
"Enable Speaker Diarization."
@@ -2424,6 +2441,15 @@ class TranscriptResponse(BaseTranscript):
24242441
utterances: Optional[List[Utterance]] = None
24252442
"When `dual_channel`, `multichannel`, or `speaker_labels` is enabled, a list of turn-by-turn utterances"
24262443

2444+
unredacted_text: Optional[str] = None
2445+
"The unredacted transcript text. Returned only when `redact_pii_return_unredacted` was set with `redact_pii`."
2446+
2447+
unredacted_words: Optional[List[Word]] = None
2448+
"The unredacted list of individual words. Returned only when `redact_pii_return_unredacted` was set with `redact_pii`."
2449+
2450+
unredacted_utterances: Optional[List[Utterance]] = None
2451+
"The unredacted list of utterances. Returned only when `redact_pii_return_unredacted` was set with `redact_pii` and channel/speaker modes are enabled."
2452+
24272453
confidence: Optional[float] = None
24282454
"The confidence our model has in the transcribed text, between 0.0 and 1.0"
24292455

tests/unit/test_redact_pii.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,110 @@ def test_redact_pii_params_excluded_when_disabled(httpx_mock: HTTPXMock):
198198
assert request_body.get("redact_pii_sub") is None
199199

200200

201+
def test_redact_pii_return_unredacted_default_absent(httpx_mock: HTTPXMock):
202+
"""
203+
Tests that `redact_pii_return_unredacted` is absent from the request body
204+
when not explicitly set, even when `redact_pii` is enabled.
205+
"""
206+
request_body, _ = unit_test_utils.submit_mock_transcription_request(
207+
httpx_mock,
208+
mock_response=factories.generate_dict_factory(
209+
TranscriptWithPIIRedactionResponseFactory
210+
)(),
211+
config=aai.TranscriptionConfig(
212+
redact_pii=True,
213+
redact_pii_policies=[aai.types.PIIRedactionPolicy.date],
214+
),
215+
)
216+
217+
assert request_body.get("redact_pii_return_unredacted") is None
218+
219+
220+
def test_redact_pii_return_unredacted_in_request(httpx_mock: HTTPXMock):
221+
"""
222+
Tests that setting `return_unredacted=True` on `set_redact_pii` puts
223+
`redact_pii_return_unredacted=True` in the submission request body.
224+
"""
225+
config = aai.TranscriptionConfig().set_redact_pii(
226+
policies=[aai.types.PIIRedactionPolicy.date],
227+
return_unredacted=True,
228+
)
229+
230+
request_body, _ = unit_test_utils.submit_mock_transcription_request(
231+
httpx_mock,
232+
mock_response=factories.generate_dict_factory(
233+
TranscriptWithPIIRedactionResponseFactory
234+
)(),
235+
config=config,
236+
)
237+
238+
assert request_body.get("redact_pii") is True
239+
assert request_body.get("redact_pii_return_unredacted") is True
240+
241+
242+
def test_redact_pii_return_unredacted_cleared_on_disable():
243+
"""
244+
Tests that calling `set_redact_pii(enable=False)` after enabling with
245+
`return_unredacted=True` clears the flag from the raw config.
246+
"""
247+
config = aai.TranscriptionConfig().set_redact_pii(
248+
policies=[aai.types.PIIRedactionPolicy.date],
249+
return_unredacted=True,
250+
)
251+
assert config.redact_pii_return_unredacted is True
252+
253+
config.set_redact_pii(enable=False)
254+
assert config.redact_pii_return_unredacted is None
255+
256+
257+
def test_unredacted_response_fields_surface_on_transcript(httpx_mock: HTTPXMock):
258+
"""
259+
Tests that `unredacted_text`, `unredacted_words`, and `unredacted_utterances`
260+
in the API response are accessible via the `Transcript` wrapper.
261+
"""
262+
base_response = factories.generate_dict_factory(
263+
TranscriptWithPIIRedactionResponseFactory
264+
)()
265+
unredacted_words = [{"text": "hello", "start": 0, "end": 500, "confidence": 0.99}]
266+
unredacted_utterances = [
267+
{
268+
"text": "hello world",
269+
"start": 0,
270+
"end": 1000,
271+
"confidence": 0.99,
272+
"speaker": "A",
273+
"words": [
274+
{"text": "hello", "start": 0, "end": 500, "confidence": 0.99},
275+
{"text": "world", "start": 500, "end": 1000, "confidence": 0.99},
276+
],
277+
}
278+
]
279+
mock_response = {
280+
**base_response,
281+
"redact_pii_return_unredacted": True,
282+
"unredacted_text": "hello world",
283+
"unredacted_words": unredacted_words,
284+
"unredacted_utterances": unredacted_utterances,
285+
}
286+
287+
_, transcript = unit_test_utils.submit_mock_transcription_request(
288+
httpx_mock,
289+
mock_response=mock_response,
290+
config=aai.TranscriptionConfig().set_redact_pii(
291+
policies=[aai.types.PIIRedactionPolicy.date],
292+
return_unredacted=True,
293+
),
294+
)
295+
296+
assert transcript.unredacted_text == "hello world"
297+
assert transcript.unredacted_words is not None
298+
assert len(transcript.unredacted_words) == 1
299+
assert transcript.unredacted_words[0].text == "hello"
300+
assert transcript.unredacted_utterances is not None
301+
assert len(transcript.unredacted_utterances) == 1
302+
assert transcript.unredacted_utterances[0].text == "hello world"
303+
304+
201305
def __get_redacted_audio_api_url(transcript: aai.Transcript) -> str:
202306
return (
203307
f"{aai.settings.base_url}{ENDPOINT_TRANSCRIPT}/{transcript.id}/redacted-audio"

0 commit comments

Comments
 (0)