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.
Files changed (187) hide show
  1. hushscript/__init__.py +97 -0
  2. hushscript/_client.py +687 -0
  3. hushscript/_clock.py +36 -0
  4. hushscript/_config.py +139 -0
  5. hushscript/_errors.py +563 -0
  6. hushscript/_guard.py +61 -0
  7. hushscript/_http.py +624 -0
  8. hushscript/_json.py +55 -0
  9. hushscript/_log.py +155 -0
  10. hushscript/_models.py +144 -0
  11. hushscript/_pacing.py +51 -0
  12. hushscript/_redact.py +140 -0
  13. hushscript/_response.py +67 -0
  14. hushscript/_retry.py +205 -0
  15. hushscript/_shape.py +197 -0
  16. hushscript/_shapes.py +248 -0
  17. hushscript/_version.py +1 -0
  18. hushscript/format.py +224 -0
  19. hushscript/generated/__init__.py +8 -0
  20. hushscript/generated/api/__init__.py +1 -0
  21. hushscript/generated/api/default/__init__.py +1 -0
  22. hushscript/generated/api/default/buy_minutes.py +248 -0
  23. hushscript/generated/api/default/claim_agent_account.py +238 -0
  24. hushscript/generated/api/default/complete_upload.py +238 -0
  25. hushscript/generated/api/default/create_agent_account_signup.py +261 -0
  26. hushscript/generated/api/default/create_setup_session.py +244 -0
  27. hushscript/generated/api/default/create_upload.py +225 -0
  28. hushscript/generated/api/default/get_account.py +169 -0
  29. hushscript/generated/api/default/get_job.py +198 -0
  30. hushscript/generated/api/default/get_org.py +169 -0
  31. hushscript/generated/api/default/get_transcript.py +226 -0
  32. hushscript/generated/api/default/get_transcription_context.py +173 -0
  33. hushscript/generated/api/default/get_upload_part_urls.py +246 -0
  34. hushscript/generated/api/default/heartbeat_upload.py +197 -0
  35. hushscript/generated/api/default/lift_transcript_hold.py +206 -0
  36. hushscript/generated/api/default/list_credit_ledger.py +229 -0
  37. hushscript/generated/api/default/list_deleted_transcripts.py +237 -0
  38. hushscript/generated/api/default/list_holds.py +233 -0
  39. hushscript/generated/api/default/list_jobs.py +249 -0
  40. hushscript/generated/api/default/list_org_audit.py +221 -0
  41. hushscript/generated/api/default/list_org_invoices.py +221 -0
  42. hushscript/generated/api/default/list_org_ledger.py +221 -0
  43. hushscript/generated/api/default/list_org_matters.py +247 -0
  44. hushscript/generated/api/default/list_org_members.py +221 -0
  45. hushscript/generated/api/default/list_org_transcripts.py +233 -0
  46. hushscript/generated/api/default/list_packs.py +169 -0
  47. hushscript/generated/api/default/list_saved_cards.py +169 -0
  48. hushscript/generated/api/default/list_transactions.py +225 -0
  49. hushscript/generated/api/default/list_transcripts.py +225 -0
  50. hushscript/generated/api/default/org_audit_csv.py +163 -0
  51. hushscript/generated/api/default/org_ledger_csv.py +163 -0
  52. hushscript/generated/api/default/org_usage_report.py +247 -0
  53. hushscript/generated/api/default/org_usage_report_csv.py +229 -0
  54. hushscript/generated/api/default/patch_org_member.py +237 -0
  55. hushscript/generated/api/default/place_transcript_hold.py +206 -0
  56. hushscript/generated/api/default/rotate_agent_credentials.py +177 -0
  57. hushscript/generated/api/default/schedule_org_custody.py +215 -0
  58. hushscript/generated/api/default/set_default_card.py +223 -0
  59. hushscript/generated/api/default/upload_part.py +249 -0
  60. hushscript/generated/catalog.py +138 -0
  61. hushscript/generated/client.py +282 -0
  62. hushscript/generated/errors.py +16 -0
  63. hushscript/generated/hints.py +28 -0
  64. hushscript/generated/models/__init__.py +193 -0
  65. hushscript/generated/models/account_response.py +114 -0
  66. hushscript/generated/models/active_hold.py +88 -0
  67. hushscript/generated/models/active_hold_kind.py +18 -0
  68. hushscript/generated/models/agent_claim_request.py +61 -0
  69. hushscript/generated/models/agent_claim_response.py +85 -0
  70. hushscript/generated/models/agent_credentials_rotate_response.py +101 -0
  71. hushscript/generated/models/agent_signup_request.py +118 -0
  72. hushscript/generated/models/agent_signup_request_agent.py +80 -0
  73. hushscript/generated/models/agent_signup_response.py +111 -0
  74. hushscript/generated/models/buy_minutes_body.py +89 -0
  75. hushscript/generated/models/card.py +132 -0
  76. hushscript/generated/models/cards_response.py +90 -0
  77. hushscript/generated/models/complete_upload_body.py +107 -0
  78. hushscript/generated/models/complete_upload_body_parts_type_0_item.py +69 -0
  79. hushscript/generated/models/context_dictionary.py +143 -0
  80. hushscript/generated/models/context_prompt_preset.py +118 -0
  81. hushscript/generated/models/create_setup_session_body.py +62 -0
  82. hushscript/generated/models/create_upload_body.py +254 -0
  83. hushscript/generated/models/create_upload_body_transcription_options_type_0.py +178 -0
  84. hushscript/generated/models/credit_ledger_entry.py +113 -0
  85. hushscript/generated/models/credit_ledger_response.py +90 -0
  86. hushscript/generated/models/deleted_transcript.py +148 -0
  87. hushscript/generated/models/deleted_transcript_cause.py +18 -0
  88. hushscript/generated/models/deleted_transcript_receipt_type_0.py +107 -0
  89. hushscript/generated/models/deleted_transcript_receipt_type_0_items_item.py +69 -0
  90. hushscript/generated/models/deleted_transcripts_response.py +90 -0
  91. hushscript/generated/models/dictionary_entry.py +123 -0
  92. hushscript/generated/models/dictionary_entry_category.py +37 -0
  93. hushscript/generated/models/error.py +155 -0
  94. hushscript/generated/models/error_details.py +94 -0
  95. hushscript/generated/models/get_upload_part_urls_response_200_type_0.py +108 -0
  96. hushscript/generated/models/heartbeat_upload_response_200.py +61 -0
  97. hushscript/generated/models/holds_response.py +90 -0
  98. hushscript/generated/models/job.py +309 -0
  99. hushscript/generated/models/job_failure_kind_type_1.py +16 -0
  100. hushscript/generated/models/job_failure_kind_type_2_type_1.py +16 -0
  101. hushscript/generated/models/job_failure_kind_type_3_type_1.py +16 -0
  102. hushscript/generated/models/job_list.py +90 -0
  103. hushscript/generated/models/lift_transcript_hold_response_200.py +61 -0
  104. hushscript/generated/models/list_jobs_active.py +17 -0
  105. hushscript/generated/models/list_org_matters_status.py +16 -0
  106. hushscript/generated/models/options_summary.py +135 -0
  107. hushscript/generated/models/org_audit_entry.py +105 -0
  108. hushscript/generated/models/org_audit_response.py +90 -0
  109. hushscript/generated/models/org_invoice.py +175 -0
  110. hushscript/generated/models/org_invoices_response.py +90 -0
  111. hushscript/generated/models/org_ledger_entry.py +155 -0
  112. hushscript/generated/models/org_ledger_response.py +90 -0
  113. hushscript/generated/models/org_matter.py +79 -0
  114. hushscript/generated/models/org_matter_status.py +16 -0
  115. hushscript/generated/models/org_matters_response.py +75 -0
  116. hushscript/generated/models/org_member.py +123 -0
  117. hushscript/generated/models/org_members_response.py +90 -0
  118. hushscript/generated/models/org_summary_response.py +482 -0
  119. hushscript/generated/models/org_transcript.py +103 -0
  120. hushscript/generated/models/org_transcripts_response.py +90 -0
  121. hushscript/generated/models/org_usage_report_csv_group_by.py +17 -0
  122. hushscript/generated/models/org_usage_report_group_by.py +17 -0
  123. hushscript/generated/models/org_usage_response.py +112 -0
  124. hushscript/generated/models/org_usage_response_group_by.py +17 -0
  125. hushscript/generated/models/org_usage_row.py +86 -0
  126. hushscript/generated/models/pack.py +107 -0
  127. hushscript/generated/models/packs_response.py +75 -0
  128. hushscript/generated/models/patch_org_member_body.py +102 -0
  129. hushscript/generated/models/patch_org_member_response_200.py +61 -0
  130. hushscript/generated/models/place_transcript_hold_response_200.py +61 -0
  131. hushscript/generated/models/presigned_part_url.py +69 -0
  132. hushscript/generated/models/purchase_response.py +76 -0
  133. hushscript/generated/models/schedule_org_custody_body.py +66 -0
  134. hushscript/generated/models/schedule_org_custody_body_custody.py +17 -0
  135. hushscript/generated/models/schedule_org_custody_response_200.py +69 -0
  136. hushscript/generated/models/set_default_card_body.py +61 -0
  137. hushscript/generated/models/set_default_card_response_200.py +61 -0
  138. hushscript/generated/models/setup_session_response.py +77 -0
  139. hushscript/generated/models/transaction.py +183 -0
  140. hushscript/generated/models/transactions_response.py +98 -0
  141. hushscript/generated/models/transcript_detail.py +194 -0
  142. hushscript/generated/models/transcript_detail_speaker_names.py +47 -0
  143. hushscript/generated/models/transcript_envelope.py +89 -0
  144. hushscript/generated/models/transcript_envelope_translations.py +62 -0
  145. hushscript/generated/models/transcript_language.py +75 -0
  146. hushscript/generated/models/transcript_list.py +90 -0
  147. hushscript/generated/models/transcript_list_row.py +152 -0
  148. hushscript/generated/models/transcript_list_row_insights_state.py +20 -0
  149. hushscript/generated/models/transcription_context_response.py +95 -0
  150. hushscript/generated/models/upload_complete_response.py +100 -0
  151. hushscript/generated/models/upload_create_response.py +148 -0
  152. hushscript/generated/models/upload_create_response_funding.py +16 -0
  153. hushscript/generated/models/upload_create_response_upload_type_0.py +108 -0
  154. hushscript/generated/models/upload_limits.py +86 -0
  155. hushscript/generated/models/upload_part_response_200.py +69 -0
  156. hushscript/generated/models/utterance.py +85 -0
  157. hushscript/generated/scopes.py +42 -0
  158. hushscript/generated/types.py +54 -0
  159. hushscript/money.py +79 -0
  160. hushscript/paginate.py +78 -0
  161. hushscript/py.typed +0 -0
  162. hushscript/resources/__init__.py +1 -0
  163. hushscript/resources/_params.py +33 -0
  164. hushscript/resources/account.py +121 -0
  165. hushscript/resources/agent.py +421 -0
  166. hushscript/resources/billing.py +567 -0
  167. hushscript/resources/jobs.py +105 -0
  168. hushscript/resources/transcripts.py +209 -0
  169. hushscript/transcribe/__init__.py +1 -0
  170. hushscript/transcribe/apipeline.py +234 -0
  171. hushscript/transcribe/aupload.py +197 -0
  172. hushscript/transcribe/heartbeat.py +78 -0
  173. hushscript/transcribe/pipeline.py +312 -0
  174. hushscript/transcribe/preflight.py +272 -0
  175. hushscript/transcribe/probe.py +185 -0
  176. hushscript/transcribe/resume.py +115 -0
  177. hushscript/transcribe/segment.py +23 -0
  178. hushscript/transcribe/session.py +34 -0
  179. hushscript/transcribe/sniff.py +30 -0
  180. hushscript/transcribe/source.py +218 -0
  181. hushscript/transcribe/upload.py +498 -0
  182. hushscript/transcribe/wait.py +124 -0
  183. hushscript/transcript.py +197 -0
  184. hushscript-1.0.0.dist-info/METADATA +502 -0
  185. hushscript-1.0.0.dist-info/RECORD +187 -0
  186. hushscript-1.0.0.dist-info/WHEEL +4 -0
  187. hushscript-1.0.0.dist-info/licenses/LICENSE +21 -0
@@ -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
+ )