hushscript 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hushscript/__init__.py +97 -0
- hushscript/_client.py +687 -0
- hushscript/_clock.py +36 -0
- hushscript/_config.py +139 -0
- hushscript/_errors.py +563 -0
- hushscript/_guard.py +61 -0
- hushscript/_http.py +624 -0
- hushscript/_json.py +55 -0
- hushscript/_log.py +155 -0
- hushscript/_models.py +144 -0
- hushscript/_pacing.py +51 -0
- hushscript/_redact.py +140 -0
- hushscript/_response.py +67 -0
- hushscript/_retry.py +205 -0
- hushscript/_shape.py +197 -0
- hushscript/_shapes.py +248 -0
- hushscript/_version.py +1 -0
- hushscript/format.py +224 -0
- hushscript/generated/__init__.py +8 -0
- hushscript/generated/api/__init__.py +1 -0
- hushscript/generated/api/default/__init__.py +1 -0
- hushscript/generated/api/default/buy_minutes.py +248 -0
- hushscript/generated/api/default/claim_agent_account.py +238 -0
- hushscript/generated/api/default/complete_upload.py +238 -0
- hushscript/generated/api/default/create_agent_account_signup.py +261 -0
- hushscript/generated/api/default/create_setup_session.py +244 -0
- hushscript/generated/api/default/create_upload.py +225 -0
- hushscript/generated/api/default/get_account.py +169 -0
- hushscript/generated/api/default/get_job.py +198 -0
- hushscript/generated/api/default/get_org.py +169 -0
- hushscript/generated/api/default/get_transcript.py +226 -0
- hushscript/generated/api/default/get_transcription_context.py +173 -0
- hushscript/generated/api/default/get_upload_part_urls.py +246 -0
- hushscript/generated/api/default/heartbeat_upload.py +197 -0
- hushscript/generated/api/default/lift_transcript_hold.py +206 -0
- hushscript/generated/api/default/list_credit_ledger.py +229 -0
- hushscript/generated/api/default/list_deleted_transcripts.py +237 -0
- hushscript/generated/api/default/list_holds.py +233 -0
- hushscript/generated/api/default/list_jobs.py +249 -0
- hushscript/generated/api/default/list_org_audit.py +221 -0
- hushscript/generated/api/default/list_org_invoices.py +221 -0
- hushscript/generated/api/default/list_org_ledger.py +221 -0
- hushscript/generated/api/default/list_org_matters.py +247 -0
- hushscript/generated/api/default/list_org_members.py +221 -0
- hushscript/generated/api/default/list_org_transcripts.py +233 -0
- hushscript/generated/api/default/list_packs.py +169 -0
- hushscript/generated/api/default/list_saved_cards.py +169 -0
- hushscript/generated/api/default/list_transactions.py +225 -0
- hushscript/generated/api/default/list_transcripts.py +225 -0
- hushscript/generated/api/default/org_audit_csv.py +163 -0
- hushscript/generated/api/default/org_ledger_csv.py +163 -0
- hushscript/generated/api/default/org_usage_report.py +247 -0
- hushscript/generated/api/default/org_usage_report_csv.py +229 -0
- hushscript/generated/api/default/patch_org_member.py +237 -0
- hushscript/generated/api/default/place_transcript_hold.py +206 -0
- hushscript/generated/api/default/rotate_agent_credentials.py +177 -0
- hushscript/generated/api/default/schedule_org_custody.py +215 -0
- hushscript/generated/api/default/set_default_card.py +223 -0
- hushscript/generated/api/default/upload_part.py +249 -0
- hushscript/generated/catalog.py +138 -0
- hushscript/generated/client.py +282 -0
- hushscript/generated/errors.py +16 -0
- hushscript/generated/hints.py +28 -0
- hushscript/generated/models/__init__.py +193 -0
- hushscript/generated/models/account_response.py +114 -0
- hushscript/generated/models/active_hold.py +88 -0
- hushscript/generated/models/active_hold_kind.py +18 -0
- hushscript/generated/models/agent_claim_request.py +61 -0
- hushscript/generated/models/agent_claim_response.py +85 -0
- hushscript/generated/models/agent_credentials_rotate_response.py +101 -0
- hushscript/generated/models/agent_signup_request.py +118 -0
- hushscript/generated/models/agent_signup_request_agent.py +80 -0
- hushscript/generated/models/agent_signup_response.py +111 -0
- hushscript/generated/models/buy_minutes_body.py +89 -0
- hushscript/generated/models/card.py +132 -0
- hushscript/generated/models/cards_response.py +90 -0
- hushscript/generated/models/complete_upload_body.py +107 -0
- hushscript/generated/models/complete_upload_body_parts_type_0_item.py +69 -0
- hushscript/generated/models/context_dictionary.py +143 -0
- hushscript/generated/models/context_prompt_preset.py +118 -0
- hushscript/generated/models/create_setup_session_body.py +62 -0
- hushscript/generated/models/create_upload_body.py +254 -0
- hushscript/generated/models/create_upload_body_transcription_options_type_0.py +178 -0
- hushscript/generated/models/credit_ledger_entry.py +113 -0
- hushscript/generated/models/credit_ledger_response.py +90 -0
- hushscript/generated/models/deleted_transcript.py +148 -0
- hushscript/generated/models/deleted_transcript_cause.py +18 -0
- hushscript/generated/models/deleted_transcript_receipt_type_0.py +107 -0
- hushscript/generated/models/deleted_transcript_receipt_type_0_items_item.py +69 -0
- hushscript/generated/models/deleted_transcripts_response.py +90 -0
- hushscript/generated/models/dictionary_entry.py +123 -0
- hushscript/generated/models/dictionary_entry_category.py +37 -0
- hushscript/generated/models/error.py +155 -0
- hushscript/generated/models/error_details.py +94 -0
- hushscript/generated/models/get_upload_part_urls_response_200_type_0.py +108 -0
- hushscript/generated/models/heartbeat_upload_response_200.py +61 -0
- hushscript/generated/models/holds_response.py +90 -0
- hushscript/generated/models/job.py +309 -0
- hushscript/generated/models/job_failure_kind_type_1.py +16 -0
- hushscript/generated/models/job_failure_kind_type_2_type_1.py +16 -0
- hushscript/generated/models/job_failure_kind_type_3_type_1.py +16 -0
- hushscript/generated/models/job_list.py +90 -0
- hushscript/generated/models/lift_transcript_hold_response_200.py +61 -0
- hushscript/generated/models/list_jobs_active.py +17 -0
- hushscript/generated/models/list_org_matters_status.py +16 -0
- hushscript/generated/models/options_summary.py +135 -0
- hushscript/generated/models/org_audit_entry.py +105 -0
- hushscript/generated/models/org_audit_response.py +90 -0
- hushscript/generated/models/org_invoice.py +175 -0
- hushscript/generated/models/org_invoices_response.py +90 -0
- hushscript/generated/models/org_ledger_entry.py +155 -0
- hushscript/generated/models/org_ledger_response.py +90 -0
- hushscript/generated/models/org_matter.py +79 -0
- hushscript/generated/models/org_matter_status.py +16 -0
- hushscript/generated/models/org_matters_response.py +75 -0
- hushscript/generated/models/org_member.py +123 -0
- hushscript/generated/models/org_members_response.py +90 -0
- hushscript/generated/models/org_summary_response.py +482 -0
- hushscript/generated/models/org_transcript.py +103 -0
- hushscript/generated/models/org_transcripts_response.py +90 -0
- hushscript/generated/models/org_usage_report_csv_group_by.py +17 -0
- hushscript/generated/models/org_usage_report_group_by.py +17 -0
- hushscript/generated/models/org_usage_response.py +112 -0
- hushscript/generated/models/org_usage_response_group_by.py +17 -0
- hushscript/generated/models/org_usage_row.py +86 -0
- hushscript/generated/models/pack.py +107 -0
- hushscript/generated/models/packs_response.py +75 -0
- hushscript/generated/models/patch_org_member_body.py +102 -0
- hushscript/generated/models/patch_org_member_response_200.py +61 -0
- hushscript/generated/models/place_transcript_hold_response_200.py +61 -0
- hushscript/generated/models/presigned_part_url.py +69 -0
- hushscript/generated/models/purchase_response.py +76 -0
- hushscript/generated/models/schedule_org_custody_body.py +66 -0
- hushscript/generated/models/schedule_org_custody_body_custody.py +17 -0
- hushscript/generated/models/schedule_org_custody_response_200.py +69 -0
- hushscript/generated/models/set_default_card_body.py +61 -0
- hushscript/generated/models/set_default_card_response_200.py +61 -0
- hushscript/generated/models/setup_session_response.py +77 -0
- hushscript/generated/models/transaction.py +183 -0
- hushscript/generated/models/transactions_response.py +98 -0
- hushscript/generated/models/transcript_detail.py +194 -0
- hushscript/generated/models/transcript_detail_speaker_names.py +47 -0
- hushscript/generated/models/transcript_envelope.py +89 -0
- hushscript/generated/models/transcript_envelope_translations.py +62 -0
- hushscript/generated/models/transcript_language.py +75 -0
- hushscript/generated/models/transcript_list.py +90 -0
- hushscript/generated/models/transcript_list_row.py +152 -0
- hushscript/generated/models/transcript_list_row_insights_state.py +20 -0
- hushscript/generated/models/transcription_context_response.py +95 -0
- hushscript/generated/models/upload_complete_response.py +100 -0
- hushscript/generated/models/upload_create_response.py +148 -0
- hushscript/generated/models/upload_create_response_funding.py +16 -0
- hushscript/generated/models/upload_create_response_upload_type_0.py +108 -0
- hushscript/generated/models/upload_limits.py +86 -0
- hushscript/generated/models/upload_part_response_200.py +69 -0
- hushscript/generated/models/utterance.py +85 -0
- hushscript/generated/scopes.py +42 -0
- hushscript/generated/types.py +54 -0
- hushscript/money.py +79 -0
- hushscript/paginate.py +78 -0
- hushscript/py.typed +0 -0
- hushscript/resources/__init__.py +1 -0
- hushscript/resources/_params.py +33 -0
- hushscript/resources/account.py +121 -0
- hushscript/resources/agent.py +421 -0
- hushscript/resources/billing.py +567 -0
- hushscript/resources/jobs.py +105 -0
- hushscript/resources/transcripts.py +209 -0
- hushscript/transcribe/__init__.py +1 -0
- hushscript/transcribe/apipeline.py +234 -0
- hushscript/transcribe/aupload.py +197 -0
- hushscript/transcribe/heartbeat.py +78 -0
- hushscript/transcribe/pipeline.py +312 -0
- hushscript/transcribe/preflight.py +272 -0
- hushscript/transcribe/probe.py +185 -0
- hushscript/transcribe/resume.py +115 -0
- hushscript/transcribe/segment.py +23 -0
- hushscript/transcribe/session.py +34 -0
- hushscript/transcribe/sniff.py +30 -0
- hushscript/transcribe/source.py +218 -0
- hushscript/transcribe/upload.py +498 -0
- hushscript/transcribe/wait.py +124 -0
- hushscript/transcript.py +197 -0
- hushscript-1.0.0.dist-info/METADATA +502 -0
- hushscript-1.0.0.dist-info/RECORD +187 -0
- hushscript-1.0.0.dist-info/WHEEL +4 -0
- hushscript-1.0.0.dist-info/licenses/LICENSE +21 -0
hushscript/transcript.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
import math
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import TYPE_CHECKING, Final, cast
|
|
7
|
+
|
|
8
|
+
from ._json import ExtraFields, as_dict, as_list, extra_fields
|
|
9
|
+
from ._response import BadBodyError
|
|
10
|
+
from ._shape import (
|
|
11
|
+
as_flag,
|
|
12
|
+
as_text,
|
|
13
|
+
as_whole,
|
|
14
|
+
check_shape,
|
|
15
|
+
finite_number,
|
|
16
|
+
maybe_text,
|
|
17
|
+
to_datetime,
|
|
18
|
+
)
|
|
19
|
+
from ._shapes import TRANSCRIPT_DETAIL
|
|
20
|
+
from .format import js_strip, render_srt, render_text, render_vtt
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from collections.abc import Mapping
|
|
24
|
+
from datetime import datetime
|
|
25
|
+
|
|
26
|
+
CLEANED: Final = "cleaned"
|
|
27
|
+
"""The reserved translation key that holds the AI-cleaned body."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True, slots=True)
|
|
31
|
+
class Utterance(ExtraFields):
|
|
32
|
+
speaker: str
|
|
33
|
+
"""The provider's raw label; empty for a transcript without speaker labels."""
|
|
34
|
+
speaker_name: str
|
|
35
|
+
"""The saved name, else `Speaker {label}`, else empty for an empty label."""
|
|
36
|
+
start_ms: int
|
|
37
|
+
end_ms: int
|
|
38
|
+
text: str
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def display_name(label: str, names: Mapping[str, str]) -> str:
|
|
42
|
+
saved = js_strip(names.get(label, ""))
|
|
43
|
+
if saved:
|
|
44
|
+
return saved
|
|
45
|
+
return "" if not js_strip(label) else f"Speaker {label}"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _utterances(language: object, names: Mapping[str, str]) -> list[Utterance]:
|
|
49
|
+
body = as_dict(language) or {}
|
|
50
|
+
out: list[Utterance] = []
|
|
51
|
+
for item in as_list(body.get("utterances")) or []:
|
|
52
|
+
fields = as_dict(item) or {}
|
|
53
|
+
speaker = as_text(fields["speaker"])
|
|
54
|
+
out.append(
|
|
55
|
+
Utterance(
|
|
56
|
+
_extra=extra_fields(fields, "speaker", "start_ms", "end_ms", "text"),
|
|
57
|
+
speaker=speaker,
|
|
58
|
+
speaker_name=display_name(speaker, names),
|
|
59
|
+
start_ms=_milliseconds(fields["start_ms"]),
|
|
60
|
+
end_ms=_milliseconds(fields["end_ms"]),
|
|
61
|
+
text=as_text(fields["text"]),
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
return out
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _milliseconds(value: object) -> int:
|
|
68
|
+
"""Whole milliseconds, floored: the app's timestamps are."""
|
|
69
|
+
if not finite_number(value):
|
|
70
|
+
raise BadBodyError("a number of milliseconds")
|
|
71
|
+
return math.floor(cast("float", value))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _no_fields() -> Mapping[str, object]:
|
|
75
|
+
return {}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _no_translations() -> Mapping[str, list[Utterance]]:
|
|
79
|
+
return {}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass(frozen=True, slots=True, repr=False)
|
|
83
|
+
class Transcript(ExtraFields):
|
|
84
|
+
"""A finished transcript with its formatters (spec 6.2). `to_json()` is the API's body."""
|
|
85
|
+
|
|
86
|
+
id: str
|
|
87
|
+
title: str
|
|
88
|
+
language: str | None
|
|
89
|
+
duration_seconds: int
|
|
90
|
+
created_at: datetime
|
|
91
|
+
updated_at: datetime
|
|
92
|
+
auto_delete_at: datetime | None
|
|
93
|
+
tags: list[str]
|
|
94
|
+
edited: bool
|
|
95
|
+
speakers: dict[str, str]
|
|
96
|
+
"""Label to display name, for the speakers that were renamed."""
|
|
97
|
+
utterances: list[Utterance]
|
|
98
|
+
"""The original language."""
|
|
99
|
+
languages: list[str]
|
|
100
|
+
"""The translation locales, without the reserved `cleaned`, in the API's order."""
|
|
101
|
+
cleaned: list[Utterance] | None
|
|
102
|
+
"""The AI-cleaned body, when there is one."""
|
|
103
|
+
_raw: Mapping[str, object] = field(
|
|
104
|
+
kw_only=True, default_factory=_no_fields, repr=False, compare=False
|
|
105
|
+
)
|
|
106
|
+
_translations: Mapping[str, list[Utterance]] = field(
|
|
107
|
+
kw_only=True, default_factory=_no_translations, repr=False, compare=False
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
__hash__ = None # type: ignore[assignment] # holds lists, so it is unhashable by design
|
|
111
|
+
|
|
112
|
+
@classmethod
|
|
113
|
+
def from_api(cls, body: object) -> Transcript:
|
|
114
|
+
"""Builds a transcript from a `GET /v1/transcripts/{id}` body; one that does not fit raises
|
|
115
|
+
BadBodyError, which the client reports as `bad_response`."""
|
|
116
|
+
check_shape(body, TRANSCRIPT_DETAIL, "transcript response")
|
|
117
|
+
fields = as_dict(body) or {}
|
|
118
|
+
names = {
|
|
119
|
+
key: as_text(value) for key, value in (as_dict(fields["speaker_names"]) or {}).items()
|
|
120
|
+
}
|
|
121
|
+
envelope = as_dict(fields["body"]) or {}
|
|
122
|
+
translations = {
|
|
123
|
+
locale: _utterances(language, names)
|
|
124
|
+
for locale, language in (as_dict(envelope["translations"]) or {}).items()
|
|
125
|
+
}
|
|
126
|
+
auto_delete = fields["auto_delete_at"]
|
|
127
|
+
return cls(
|
|
128
|
+
_extra=extra_fields(
|
|
129
|
+
fields,
|
|
130
|
+
"id",
|
|
131
|
+
"title",
|
|
132
|
+
"language_detected",
|
|
133
|
+
"duration_seconds",
|
|
134
|
+
"created_at",
|
|
135
|
+
"updated_at",
|
|
136
|
+
"auto_delete_at",
|
|
137
|
+
"tags",
|
|
138
|
+
"edited",
|
|
139
|
+
"speaker_names",
|
|
140
|
+
"body",
|
|
141
|
+
),
|
|
142
|
+
id=as_text(fields["id"]),
|
|
143
|
+
title=as_text(fields["title"]),
|
|
144
|
+
language=maybe_text(fields["language_detected"]),
|
|
145
|
+
duration_seconds=as_whole(fields["duration_seconds"]),
|
|
146
|
+
created_at=to_datetime(fields["created_at"]),
|
|
147
|
+
updated_at=to_datetime(fields["updated_at"]),
|
|
148
|
+
auto_delete_at=None if auto_delete is None else to_datetime(auto_delete),
|
|
149
|
+
tags=[as_text(tag) for tag in as_list(fields["tags"]) or []],
|
|
150
|
+
edited=as_flag(fields["edited"]),
|
|
151
|
+
speakers=names,
|
|
152
|
+
utterances=_utterances(envelope["original"], names),
|
|
153
|
+
languages=[locale for locale in translations if locale != CLEANED],
|
|
154
|
+
cleaned=translations.get(CLEANED),
|
|
155
|
+
_raw=fields,
|
|
156
|
+
_translations=translations,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def text(self) -> str:
|
|
161
|
+
"""The plain text, the same as `to_text()`."""
|
|
162
|
+
return self.to_text()
|
|
163
|
+
|
|
164
|
+
def utterances_for(self, language: str | None = None) -> list[Utterance]:
|
|
165
|
+
"""The translation (or `cleaned` body) for `language`; the original when `language` is
|
|
166
|
+
empty or the transcript has no such translation. The list is a copy."""
|
|
167
|
+
if not language:
|
|
168
|
+
return list(self.utterances)
|
|
169
|
+
return list(self._translations.get(language, self.utterances))
|
|
170
|
+
|
|
171
|
+
def to_srt(self, language: str | None = None, *, captions: bool = True) -> str:
|
|
172
|
+
return render_srt(self.utterances_for(language), captions=captions)
|
|
173
|
+
|
|
174
|
+
def to_vtt(self, language: str | None = None, *, captions: bool = True) -> str:
|
|
175
|
+
return render_vtt(self.utterances_for(language), captions=captions)
|
|
176
|
+
|
|
177
|
+
def to_text(
|
|
178
|
+
self,
|
|
179
|
+
language: str | None = None,
|
|
180
|
+
*,
|
|
181
|
+
speakers: bool = True,
|
|
182
|
+
timestamps: bool = False,
|
|
183
|
+
merge: bool = True,
|
|
184
|
+
) -> str:
|
|
185
|
+
return render_text(
|
|
186
|
+
self.utterances_for(language), speakers=speakers, timestamps=timestamps, merge=merge
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def to_json(self) -> Mapping[str, object]:
|
|
190
|
+
"""A copy of the API body this transcript was built from, unknown fields included."""
|
|
191
|
+
return copy.deepcopy(dict(self._raw))
|
|
192
|
+
|
|
193
|
+
def __repr__(self) -> str:
|
|
194
|
+
return (
|
|
195
|
+
f"Transcript(id={self.id!r}, duration_seconds={self.duration_seconds!r}, "
|
|
196
|
+
f"language={self.language!r})"
|
|
197
|
+
)
|