From 3e12143b55731d8a872bf9e0c2b298b43071609e Mon Sep 17 00:00:00 2001 From: "onepin-pipeline-bot[bot]" <290953255+onepin-pipeline-bot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 10:55:59 +0000 Subject: [PATCH] feat: sync SDK to OnePin API v0.41.97 --- .spec-sha | 2 +- src/onepin/.fern/metadata.json | 2 +- src/onepin/__init__.py | 24 +++ ...s_api_v1dictionary_get_request_language.py | 3 +- ...ionary_search_get_request_language_item.py | 3 +- src/onepin/reference.md | 129 +++++++++++- src/onepin/types/__init__.py | 21 ++ src/onepin/types/api_error_detail.py | 2 + .../types/api_response_voice_preview_out.py | 22 ++ src/onepin/types/balance_response.py | 43 +++- src/onepin/types/estimate_response.py | 3 + src/onepin/types/plan_limits.py | 4 + .../types/plan_limits_credits_renewal.py | 5 + .../types/voice_model_capability_out.py | 29 +++ src/onepin/types/voice_out.py | 18 +- src/onepin/types/voice_preview_out.py | 37 ++++ src/onepin/types/voice_similar_out.py | 18 +- src/onepin/types/workflow_run_detail_out.py | 5 + src/onepin/types/workflow_run_outputs_out.py | 5 + .../workflow_run_sink_output_delivery.py | 21 ++ ...workflow_run_sink_output_delivery_check.py | 22 ++ .../types/workflow_run_sink_output_line.py | 22 ++ .../types/workflow_run_sink_output_out.py | 3 +- src/onepin/users/client.py | 22 +- src/onepin/users/raw_client.py | 22 +- src/onepin/voices/__init__.py | 3 + src/onepin/voices/client.py | 148 +++++++++++++- src/onepin/voices/raw_client.py | 190 +++++++++++++++++- src/onepin/voices/types/__init__.py | 3 + .../list_voices_request_language_item.py | 3 +- .../types/preview_voices_request_language.py | 28 +++ 31 files changed, 821 insertions(+), 41 deletions(-) create mode 100644 src/onepin/types/api_response_voice_preview_out.py create mode 100644 src/onepin/types/plan_limits_credits_renewal.py create mode 100644 src/onepin/types/voice_model_capability_out.py create mode 100644 src/onepin/types/voice_preview_out.py create mode 100644 src/onepin/types/workflow_run_sink_output_delivery.py create mode 100644 src/onepin/types/workflow_run_sink_output_delivery_check.py create mode 100644 src/onepin/types/workflow_run_sink_output_line.py create mode 100644 src/onepin/voices/types/preview_voices_request_language.py diff --git a/.spec-sha b/.spec-sha index 7dd08d4..0e7be57 100644 --- a/.spec-sha +++ b/.spec-sha @@ -1 +1 @@ -6ace098c56527cf0a0e5018b67ec454412ee6926 +5b349feb6ae5f31abbc181e369f6ea2d90d15b6e diff --git a/src/onepin/.fern/metadata.json b/src/onepin/.fern/metadata.json index 6f11267..da28c6e 100644 --- a/src/onepin/.fern/metadata.json +++ b/src/onepin/.fern/metadata.json @@ -30,7 +30,7 @@ } ] }, - "originGitCommit": "9e634eff3eb65ab32b4a77b6fee5e0d27aa126cf", + "originGitCommit": "8161f2e78f880139a10f4fbd560a506103452095", "originGitCommitIsDirty": false, "invokedBy": "ci", "ciProvider": "github" diff --git a/src/onepin/__init__.py b/src/onepin/__init__.py index 3fa3327..c1beb23 100644 --- a/src/onepin/__init__.py +++ b/src/onepin/__init__.py @@ -49,6 +49,7 @@ ApiResponseUsageSummaryOut, ApiResponseVoiceFacetsOut, ApiResponseVoiceOut, + ApiResponseVoicePreviewOut, ApiResponseWorkflowNameAvailabilityOut, ApiResponseWorkflowOut, ApiResponseWorkflowRunDetailOut, @@ -98,6 +99,7 @@ NumericOption, PaginationMeta, PlanLimits, + PlanLimitsCreditsRenewal, PlanTier, PortOut, PronunciationSuggestion, @@ -144,7 +146,9 @@ VoiceFacetItem, VoiceFacetsOut, VoiceGender, + VoiceModelCapabilityOut, VoiceOut, + VoicePreviewOut, VoiceSimilarOut, VoiceSource, WorkflowDefinitionInput, @@ -192,6 +196,9 @@ WorkflowRunOverviewValidatorOut, WorkflowRunOverviewValidatorOutStatus, WorkflowRunOverviewWorkflowOut, + WorkflowRunSinkOutputDelivery, + WorkflowRunSinkOutputDeliveryCheck, + WorkflowRunSinkOutputLine, WorkflowRunSinkOutputOut, WorkflowRunStartIn, WorkflowRunStatusOut, @@ -249,6 +256,7 @@ ListVoicesRequestOrderItem, ListVoicesRequestSortItem, ListVoicesRequestSourceItem, + PreviewVoicesRequestLanguage, ) from .workflows import ListWorkflowsRequestOrderItem, ListWorkflowsRequestSortItem _dynamic_imports: typing.Dict[str, str] = { @@ -294,6 +302,7 @@ "ApiResponseUsageSummaryOut": ".types", "ApiResponseVoiceFacetsOut": ".types", "ApiResponseVoiceOut": ".types", + "ApiResponseVoicePreviewOut": ".types", "ApiResponseWorkflowNameAvailabilityOut": ".types", "ApiResponseWorkflowOut": ".types", "ApiResponseWorkflowRunDetailOut": ".types", @@ -368,8 +377,10 @@ "OnepinClient": "._client", "PaginationMeta": ".types", "PlanLimits": ".types", + "PlanLimitsCreditsRenewal": ".types", "PlanTier": ".types", "PortOut": ".types", + "PreviewVoicesRequestLanguage": ".voices", "PronunciationSuggestion": ".types", "ProviderGroupOut": ".types", "RunStatusCounts": ".types", @@ -425,7 +436,9 @@ "VoiceFacetItem": ".types", "VoiceFacetsOut": ".types", "VoiceGender": ".types", + "VoiceModelCapabilityOut": ".types", "VoiceOut": ".types", + "VoicePreviewOut": ".types", "VoiceSimilarOut": ".types", "VoiceSource": ".types", "WorkflowDefinitionInput": ".types", @@ -473,6 +486,9 @@ "WorkflowRunOverviewValidatorOut": ".types", "WorkflowRunOverviewValidatorOutStatus": ".types", "WorkflowRunOverviewWorkflowOut": ".types", + "WorkflowRunSinkOutputDelivery": ".types", + "WorkflowRunSinkOutputDeliveryCheck": ".types", + "WorkflowRunSinkOutputLine": ".types", "WorkflowRunSinkOutputOut": ".types", "WorkflowRunStartIn": ".types", "WorkflowRunStatusOut": ".types", @@ -565,6 +581,7 @@ def __dir__(): "ApiResponseUsageSummaryOut", "ApiResponseVoiceFacetsOut", "ApiResponseVoiceOut", + "ApiResponseVoicePreviewOut", "ApiResponseWorkflowNameAvailabilityOut", "ApiResponseWorkflowOut", "ApiResponseWorkflowRunDetailOut", @@ -639,8 +656,10 @@ def __dir__(): "OnepinClient", "PaginationMeta", "PlanLimits", + "PlanLimitsCreditsRenewal", "PlanTier", "PortOut", + "PreviewVoicesRequestLanguage", "PronunciationSuggestion", "ProviderGroupOut", "RunStatusCounts", @@ -696,7 +715,9 @@ def __dir__(): "VoiceFacetItem", "VoiceFacetsOut", "VoiceGender", + "VoiceModelCapabilityOut", "VoiceOut", + "VoicePreviewOut", "VoiceSimilarOut", "VoiceSource", "WorkflowDefinitionInput", @@ -744,6 +765,9 @@ def __dir__(): "WorkflowRunOverviewValidatorOut", "WorkflowRunOverviewValidatorOutStatus", "WorkflowRunOverviewWorkflowOut", + "WorkflowRunSinkOutputDelivery", + "WorkflowRunSinkOutputDeliveryCheck", + "WorkflowRunSinkOutputLine", "WorkflowRunSinkOutputOut", "WorkflowRunStartIn", "WorkflowRunStatusOut", diff --git a/src/onepin/dictionary/types/list_dictionary_entries_api_v1dictionary_get_request_language.py b/src/onepin/dictionary/types/list_dictionary_entries_api_v1dictionary_get_request_language.py index a062a7d..225635d 100644 --- a/src/onepin/dictionary/types/list_dictionary_entries_api_v1dictionary_get_request_language.py +++ b/src/onepin/dictionary/types/list_dictionary_entries_api_v1dictionary_get_request_language.py @@ -3,5 +3,6 @@ import typing ListDictionaryEntriesApiV1DictionaryGetRequestLanguage = typing.Union[ - typing.Literal["de-de", "en-gb", "en-us", "es-es", "fr-fr", "it-it", "ja-jp", "ko-kr", "pt-br", "zh-cn"], typing.Any + typing.Literal["de-de", "en-gb", "en-us", "es-es", "es-mx", "fr-fr", "ja-jp", "ko-kr", "pt-br", "pt-pt", "zh-cn"], + typing.Any, ] diff --git a/src/onepin/dictionary/types/search_dictionary_entries_api_v1dictionary_search_get_request_language_item.py b/src/onepin/dictionary/types/search_dictionary_entries_api_v1dictionary_search_get_request_language_item.py index 91bec6a..52ef385 100644 --- a/src/onepin/dictionary/types/search_dictionary_entries_api_v1dictionary_search_get_request_language_item.py +++ b/src/onepin/dictionary/types/search_dictionary_entries_api_v1dictionary_search_get_request_language_item.py @@ -3,5 +3,6 @@ import typing SearchDictionaryEntriesApiV1DictionarySearchGetRequestLanguageItem = typing.Union[ - typing.Literal["de-de", "en-gb", "en-us", "es-es", "fr-fr", "it-it", "ja-jp", "ko-kr", "pt-br", "zh-cn"], typing.Any + typing.Literal["de-de", "en-gb", "en-us", "es-es", "es-mx", "fr-fr", "ja-jp", "ko-kr", "pt-br", "pt-pt", "zh-cn"], + typing.Any, ] diff --git a/src/onepin/reference.md b/src/onepin/reference.md index 0271a82..1b01989 100644 --- a/src/onepin/reference.md +++ b/src/onepin/reference.md @@ -2682,7 +2682,7 @@ client.voices.list()
-**search:** `typing.Optional[str]` — Full-text search against voice name, description, and tags. +**search:** `typing.Optional[str]` — Searches name, tags, and the voice's summary-derived descriptor text (closely tracks the served description; summary beyond 200 chars is not searched).
@@ -3123,6 +3123,122 @@ client.voices.similar( + + + + +
client.voices.preview(...) -> ApiResponseVoicePreviewOut +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Fetch ready-to-play preview audio for one voice in one language. + +Returns the preview's `locale`, the `model` it was synthesized with, its +stored `content_type`, and a `sample_url` — a time-limited presigned URL +valid for 1 hour; regenerate it by calling this endpoint again rather than +caching it long-term. Pass `model` to prefer a specific TTS model; a +preview from another model is still returned when that model has none. + +`language` accepts a bare family (`ko`, `en`) as well as an exact locale +(`ko-kr`, `en-gb`). A bare family expands to every supported locale in it, +and which region wins is deterministic but arbitrary — so the response +echoes the `locale` actually served. Read `locale`, never the request +parameter, when labelling what the caller is hearing. + +404 means no preview audio has been generated for this voice in that +locale — NOT that the voice cannot speak it. `supported_languages` on the +voice is the claim about what it can speak; `preview_locales` is the list +of locales this endpoint will succeed for. 404 is also returned when the +voice does not exist or is not accessible to the caller's workspace. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from onepin import OnePinClient +from onepin.environment import OnePinClientEnvironment + +client = OnePinClient( + token="", + environment=OnePinClientEnvironment.PROD, +) + +client.voices.preview( + voice_id="voice_id", + language="de", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**voice_id:** `str` + +
+
+ +
+
+ +**language:** `PreviewVoicesRequestLanguage` — BCP-47 language code, e.g. en-us, ko-kr + +
+
+ +
+
+ +**model:** `typing.Optional[str]` — TTS model id, e.g. sonic-2 + +
+
+ +
+
+ +**workspace_id:** `typing.Optional[str]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -5316,9 +5432,14 @@ attempt a workflow run. `remaining` is a display convenience derived from settled ledger entries and may temporarily exceed `balance` while a workflow run holds an open reserve. `used` reflects credits consumed in the current billing period. `plan_grant` is the total monthly credit allowance for the -caller's plan, enabling a "X / Y used" display. `period_start` and -`period_end` mark the boundaries of the current billing window; free-tier -callers use a calendar-month boundary. +caller's plan, enabling a "X / Y used" display. `period_start` is the current +credit anchor and `period_end` is the next EXPECTED credit-reset boundary +(`period_start` + 1 month), or null when no reset is promised — Free/one-time, +unanchored, a canceling/ended entitlement, or a monthly renewal whose boundary +passed without confirmed payment. `period_end` is the expected boundary, not a +guaranteed grant time: monthly credits stay gated on successful Stripe payment. +For an annual subscriber this GET may perform idempotent maintenance, granting +any due intermediate monthly credits before returning; retries remain safe. diff --git a/src/onepin/types/__init__.py b/src/onepin/types/__init__.py index c2f5808..fdf2ac4 100644 --- a/src/onepin/types/__init__.py +++ b/src/onepin/types/__init__.py @@ -48,6 +48,7 @@ from .api_response_usage_summary_out import ApiResponseUsageSummaryOut from .api_response_voice_facets_out import ApiResponseVoiceFacetsOut from .api_response_voice_out import ApiResponseVoiceOut + from .api_response_voice_preview_out import ApiResponseVoicePreviewOut from .api_response_workflow_name_availability_out import ApiResponseWorkflowNameAvailabilityOut from .api_response_workflow_out import ApiResponseWorkflowOut from .api_response_workflow_run_detail_out import ApiResponseWorkflowRunDetailOut @@ -99,6 +100,7 @@ from .numeric_option import NumericOption from .pagination_meta import PaginationMeta from .plan_limits import PlanLimits + from .plan_limits_credits_renewal import PlanLimitsCreditsRenewal from .plan_tier import PlanTier from .port_out import PortOut from .pronunciation_suggestion import PronunciationSuggestion @@ -145,7 +147,9 @@ from .voice_facet_item import VoiceFacetItem from .voice_facets_out import VoiceFacetsOut from .voice_gender import VoiceGender + from .voice_model_capability_out import VoiceModelCapabilityOut from .voice_out import VoiceOut + from .voice_preview_out import VoicePreviewOut from .voice_similar_out import VoiceSimilarOut from .voice_source import VoiceSource from .workflow_definition_input import WorkflowDefinitionInput @@ -193,6 +197,9 @@ from .workflow_run_overview_validator_out import WorkflowRunOverviewValidatorOut from .workflow_run_overview_validator_out_status import WorkflowRunOverviewValidatorOutStatus from .workflow_run_overview_workflow_out import WorkflowRunOverviewWorkflowOut + from .workflow_run_sink_output_delivery import WorkflowRunSinkOutputDelivery + from .workflow_run_sink_output_delivery_check import WorkflowRunSinkOutputDeliveryCheck + from .workflow_run_sink_output_line import WorkflowRunSinkOutputLine from .workflow_run_sink_output_out import WorkflowRunSinkOutputOut from .workflow_run_start_in import WorkflowRunStartIn from .workflow_run_status_out import WorkflowRunStatusOut @@ -246,6 +253,7 @@ "ApiResponseUsageSummaryOut": ".api_response_usage_summary_out", "ApiResponseVoiceFacetsOut": ".api_response_voice_facets_out", "ApiResponseVoiceOut": ".api_response_voice_out", + "ApiResponseVoicePreviewOut": ".api_response_voice_preview_out", "ApiResponseWorkflowNameAvailabilityOut": ".api_response_workflow_name_availability_out", "ApiResponseWorkflowOut": ".api_response_workflow_out", "ApiResponseWorkflowRunDetailOut": ".api_response_workflow_run_detail_out", @@ -295,6 +303,7 @@ "NumericOption": ".numeric_option", "PaginationMeta": ".pagination_meta", "PlanLimits": ".plan_limits", + "PlanLimitsCreditsRenewal": ".plan_limits_credits_renewal", "PlanTier": ".plan_tier", "PortOut": ".port_out", "PronunciationSuggestion": ".pronunciation_suggestion", @@ -341,7 +350,9 @@ "VoiceFacetItem": ".voice_facet_item", "VoiceFacetsOut": ".voice_facets_out", "VoiceGender": ".voice_gender", + "VoiceModelCapabilityOut": ".voice_model_capability_out", "VoiceOut": ".voice_out", + "VoicePreviewOut": ".voice_preview_out", "VoiceSimilarOut": ".voice_similar_out", "VoiceSource": ".voice_source", "WorkflowDefinitionInput": ".workflow_definition_input", @@ -389,6 +400,9 @@ "WorkflowRunOverviewValidatorOut": ".workflow_run_overview_validator_out", "WorkflowRunOverviewValidatorOutStatus": ".workflow_run_overview_validator_out_status", "WorkflowRunOverviewWorkflowOut": ".workflow_run_overview_workflow_out", + "WorkflowRunSinkOutputDelivery": ".workflow_run_sink_output_delivery", + "WorkflowRunSinkOutputDeliveryCheck": ".workflow_run_sink_output_delivery_check", + "WorkflowRunSinkOutputLine": ".workflow_run_sink_output_line", "WorkflowRunSinkOutputOut": ".workflow_run_sink_output_out", "WorkflowRunStartIn": ".workflow_run_start_in", "WorkflowRunStatusOut": ".workflow_run_status_out", @@ -466,6 +480,7 @@ def __dir__(): "ApiResponseUsageSummaryOut", "ApiResponseVoiceFacetsOut", "ApiResponseVoiceOut", + "ApiResponseVoicePreviewOut", "ApiResponseWorkflowNameAvailabilityOut", "ApiResponseWorkflowOut", "ApiResponseWorkflowRunDetailOut", @@ -515,6 +530,7 @@ def __dir__(): "NumericOption", "PaginationMeta", "PlanLimits", + "PlanLimitsCreditsRenewal", "PlanTier", "PortOut", "PronunciationSuggestion", @@ -561,7 +577,9 @@ def __dir__(): "VoiceFacetItem", "VoiceFacetsOut", "VoiceGender", + "VoiceModelCapabilityOut", "VoiceOut", + "VoicePreviewOut", "VoiceSimilarOut", "VoiceSource", "WorkflowDefinitionInput", @@ -609,6 +627,9 @@ def __dir__(): "WorkflowRunOverviewValidatorOut", "WorkflowRunOverviewValidatorOutStatus", "WorkflowRunOverviewWorkflowOut", + "WorkflowRunSinkOutputDelivery", + "WorkflowRunSinkOutputDeliveryCheck", + "WorkflowRunSinkOutputLine", "WorkflowRunSinkOutputOut", "WorkflowRunStartIn", "WorkflowRunStatusOut", diff --git a/src/onepin/types/api_error_detail.py b/src/onepin/types/api_error_detail.py index 8b6b8eb..1c95971 100644 --- a/src/onepin/types/api_error_detail.py +++ b/src/onepin/types/api_error_detail.py @@ -9,6 +9,8 @@ class ApiErrorDetail(UniversalBaseModel): field: str message: str + line_numbers: typing.Optional[typing.List[int]] = None + min_chars: typing.Optional[int] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/onepin/types/api_response_voice_preview_out.py b/src/onepin/types/api_response_voice_preview_out.py new file mode 100644 index 0000000..6303f04 --- /dev/null +++ b/src/onepin/types/api_response_voice_preview_out.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .meta import Meta +from .voice_preview_out import VoicePreviewOut + + +class ApiResponseVoicePreviewOut(UniversalBaseModel): + data: VoicePreviewOut + meta: Meta + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/onepin/types/balance_response.py b/src/onepin/types/balance_response.py index 18e36c6..99ea9fd 100644 --- a/src/onepin/types/balance_response.py +++ b/src/onepin/types/balance_response.py @@ -13,7 +13,26 @@ class BalanceResponse(UniversalBaseModel): Per-user credit balance + period anchor (consumed by GET /users/me/credits). """ - balance: int + balance: int = pydantic.Field() + """ + Total spendable credits = monthly_balance + free_balance. + """ + + monthly_balance: typing.Optional[int] = pydantic.Field(default=None) + """ + Expiring bucket: the current paid cycle's allowance, replaced each renewal. 0 on Free. + """ + + free_balance: typing.Optional[int] = pydantic.Field(default=None) + """ + Lifetime bucket: the one-time Free grant. Never expires; spent last (after monthly). + """ + + free_grant: typing.Optional[int] = pydantic.Field(default=None) + """ + All-time ceiling of the lifetime free bucket. + """ + used: typing.Optional[int] = pydantic.Field(default=None) """ Ledger-derived credits used in the current billing period. @@ -24,10 +43,28 @@ class BalanceResponse(UniversalBaseModel): Ledger-derived settled credits remaining for display only. Excludes in-flight open reserves, so this may exceed `balance` while a workflow is executing. Use `balance` for gate decisions. """ - period_start: typing.Optional[dt.datetime] = None - period_end: typing.Optional[dt.datetime] = None + period_start: typing.Optional[dt.datetime] = pydantic.Field(default=None) + """ + Start of the current credit period (the stored credit anchor). Null on Free/unanchored accounts. + """ + + period_end: typing.Optional[dt.datetime] = pydantic.Field(default=None) + """ + Next EXPECTED credit-reset boundary (`period_start` + 1 calendar month), or null when no reset is currently promised: Free/one-time or unanchored accounts, a canceling/ended entitlement with no future grant, or a monthly renewal whose boundary has already passed without confirmed payment. This is the expected boundary, not a guaranteed grant time — monthly credits (including an annual subscription's renewal) stay gated on successful Stripe payment and may become available shortly after this time. An annual subscription's intermediate monthly boundaries are deterministic and are granted on access. Distinct from `CustomerSubscriptionResponse.current_period_end`, which remains the Stripe billing/entitlement boundary and must not be used as the credit-refresh time. + """ + plan_grant: int plan_tier: PlanTier + overage_rate_cents_per_credit: typing.Optional[float] = None + overage_cents: typing.Optional[int] = pydantic.Field(default=None) + """ + Overage billed so far this billing cycle, in cents. + """ + + credits_absorbed: typing.Optional[float] = pydantic.Field(default=None) + """ + Total credits discounted by floor-rounding this billing period. Excludes any minimum charge adjustment. + """ if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/onepin/types/estimate_response.py b/src/onepin/types/estimate_response.py index d17cae6..bdede5b 100644 --- a/src/onepin/types/estimate_response.py +++ b/src/onepin/types/estimate_response.py @@ -22,6 +22,9 @@ class EstimateResponse(UniversalBaseModel): current_balance: int deficit_at_max: int can_run: bool + overage_rate_cents_per_credit: typing.Optional[float] = None + will_incur_overage: typing.Optional[bool] = None + estimated_overage_cents: typing.Optional[int] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/onepin/types/plan_limits.py b/src/onepin/types/plan_limits.py index 71c2c19..95d541c 100644 --- a/src/onepin/types/plan_limits.py +++ b/src/onepin/types/plan_limits.py @@ -4,6 +4,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .plan_limits_credits_renewal import PlanLimitsCreditsRenewal class PlanLimits(UniversalBaseModel): @@ -18,6 +19,9 @@ class PlanLimits(UniversalBaseModel): retention_days: typing.Optional[int] = None byok_enabled: typing.Optional[bool] = None auto_fix_enabled: typing.Optional[bool] = None + credits_renewal: typing.Optional[PlanLimitsCreditsRenewal] = None + downloads_enabled: typing.Optional[bool] = None + overage_rate_cents_per_credit: typing.Optional[float] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/onepin/types/plan_limits_credits_renewal.py b/src/onepin/types/plan_limits_credits_renewal.py new file mode 100644 index 0000000..29af1b6 --- /dev/null +++ b/src/onepin/types/plan_limits_credits_renewal.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +PlanLimitsCreditsRenewal = typing.Union[typing.Literal["monthly", "one_time"], typing.Any] diff --git a/src/onepin/types/voice_model_capability_out.py b/src/onepin/types/voice_model_capability_out.py new file mode 100644 index 0000000..5d77462 --- /dev/null +++ b/src/onepin/types/voice_model_capability_out.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class VoiceModelCapabilityOut(UniversalBaseModel): + model: str = pydantic.Field() + """ + Model identifier for this exact voice capability. + """ + + languages_known: bool = pydantic.Field() + """ + Whether the provider authoritatively supplied locale metadata. False means unknown; true with an empty supported_languages array means known-empty. + """ + + supported_languages: typing.Optional[typing.List[str]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/onepin/types/voice_out.py b/src/onepin/types/voice_out.py index c4e7610..a40863b 100644 --- a/src/onepin/types/voice_out.py +++ b/src/onepin/types/voice_out.py @@ -9,6 +9,7 @@ from .voice_age import VoiceAge from .voice_category import VoiceCategory from .voice_gender import VoiceGender +from .voice_model_capability_out import VoiceModelCapabilityOut from .voice_source import VoiceSource @@ -73,11 +74,6 @@ class VoiceOut(UniversalBaseModel): Freeform keyword tags for filtering and search. """ - descriptor: typing.Optional[str] = pydantic.Field(default=None) - """ - Short one-line voice personality descriptor. - """ - uses_count: typing.Optional[int] = pydantic.Field(default=None) """ Number of times this voice has been used in workflow runs across the platform. @@ -108,9 +104,19 @@ class VoiceOut(UniversalBaseModel): BCP-47 language codes this voice supports. Null means the voice declares no locales; it is not matched by any `language` filter — a voice must positively declare a locale to surface under that filter. """ + preview_locales: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + Locales this voice has ready-to-play preview audio for. Fetch it with GET /voices/{voice_id}/preview?language=. This is what the voice can be HEARD in, not what it can SPEAK — see supported_languages for that. Empty means no locale preview exists yet. + """ + supported_models: typing.Optional[typing.List[str]] = pydantic.Field(default=None) """ - Model identifiers this voice is compatible with. Null means compatible with all available models for the provider. + Deprecated compatibility union of model identifiers this voice is compatible with. Null means compatible with all available models for the provider. + """ + + model_capabilities: typing.Optional[typing.List[VoiceModelCapabilityOut]] = pydantic.Field(default=None) + """ + Authoritative model-to-language observations after a successful full provider sync. Empty means no authoritative pair observation is available yet. """ is_favorite: typing.Optional[bool] = pydantic.Field(default=None) diff --git a/src/onepin/types/voice_preview_out.py b/src/onepin/types/voice_preview_out.py new file mode 100644 index 0000000..7704e30 --- /dev/null +++ b/src/onepin/types/voice_preview_out.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class VoicePreviewOut(UniversalBaseModel): + locale: str = pydantic.Field() + """ + The actual regioned locale of the selected preview tuple, not a reflection of the request's `language` parameter. A bare-family request like `?language=en` expands to both `en-us` and `en-gb`, so without this field the caller cannot know which one they are hearing. + """ + + model: str = pydantic.Field() + """ + The TTS model the preview was synthesized with. + """ + + sample_url: str = pydantic.Field() + """ + Time-limited presigned URL for the audio preview sample. Valid for 1 hour; regenerate by fetching the preview again. + """ + + content_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The selected row's stored `preview_content_type`, read from the database row — not a guarantee that the object in S3 is playable. May be null. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/onepin/types/voice_similar_out.py b/src/onepin/types/voice_similar_out.py index d20d6fe..7b1a456 100644 --- a/src/onepin/types/voice_similar_out.py +++ b/src/onepin/types/voice_similar_out.py @@ -9,6 +9,7 @@ from .voice_age import VoiceAge from .voice_category import VoiceCategory from .voice_gender import VoiceGender +from .voice_model_capability_out import VoiceModelCapabilityOut from .voice_source import VoiceSource @@ -73,11 +74,6 @@ class VoiceSimilarOut(UniversalBaseModel): Freeform keyword tags for filtering and search. """ - descriptor: typing.Optional[str] = pydantic.Field(default=None) - """ - Short one-line voice personality descriptor. - """ - uses_count: typing.Optional[int] = pydantic.Field(default=None) """ Number of times this voice has been used in workflow runs across the platform. @@ -108,9 +104,19 @@ class VoiceSimilarOut(UniversalBaseModel): BCP-47 language codes this voice supports. Null means the voice declares no locales; it is not matched by any `language` filter — a voice must positively declare a locale to surface under that filter. """ + preview_locales: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + Locales this voice has ready-to-play preview audio for. Fetch it with GET /voices/{voice_id}/preview?language=. This is what the voice can be HEARD in, not what it can SPEAK — see supported_languages for that. Empty means no locale preview exists yet. + """ + supported_models: typing.Optional[typing.List[str]] = pydantic.Field(default=None) """ - Model identifiers this voice is compatible with. Null means compatible with all available models for the provider. + Deprecated compatibility union of model identifiers this voice is compatible with. Null means compatible with all available models for the provider. + """ + + model_capabilities: typing.Optional[typing.List[VoiceModelCapabilityOut]] = pydantic.Field(default=None) + """ + Authoritative model-to-language observations after a successful full provider sync. Empty means no authoritative pair observation is available yet. """ is_favorite: typing.Optional[bool] = pydantic.Field(default=None) diff --git a/src/onepin/types/workflow_run_detail_out.py b/src/onepin/types/workflow_run_detail_out.py index f018add..148641c 100644 --- a/src/onepin/types/workflow_run_detail_out.py +++ b/src/onepin/types/workflow_run_detail_out.py @@ -36,6 +36,11 @@ class WorkflowRunDetailOut(UniversalBaseModel): has_export: typing.Optional[bool] = None triggered_by: typing.Optional[TriggeredByOut] = None credits: typing.Optional[int] = None + credits_absorbed: typing.Optional[float] = pydantic.Field(default=None) + """ + Credits discounted by floor-rounding for this run. This is the positive sub-credit remainder above the charged floor and excludes any minimum charge adjustment; charged credits plus this value reconstructs true cost only for fully covered, uncapped runs that were not lifted by the minimum charge. Returns 0 for unsettled or legacy runs. + """ + definition_snapshot: typing.Optional[typing.Dict[str, typing.Any]] = None if IS_PYDANTIC_V2: diff --git a/src/onepin/types/workflow_run_outputs_out.py b/src/onepin/types/workflow_run_outputs_out.py index 73c4dcb..99fb1f1 100644 --- a/src/onepin/types/workflow_run_outputs_out.py +++ b/src/onepin/types/workflow_run_outputs_out.py @@ -11,6 +11,11 @@ class WorkflowRunOutputsOut(UniversalBaseModel): run_id: str run_status: str credits: typing.Optional[int] = None + credits_absorbed: typing.Optional[float] = pydantic.Field(default=None) + """ + Credits discounted by floor-rounding for this run. This is the positive sub-credit remainder above the charged floor and excludes any minimum charge adjustment; charged credits plus this value reconstructs true cost only for fully covered, uncapped runs that were not lifted by the minimum charge. Returns 0 for unsettled or legacy runs. + """ + outputs: typing.List[WorkflowRunSinkOutputOut] if IS_PYDANTIC_V2: diff --git a/src/onepin/types/workflow_run_sink_output_delivery.py b/src/onepin/types/workflow_run_sink_output_delivery.py new file mode 100644 index 0000000..295bdfb --- /dev/null +++ b/src/onepin/types/workflow_run_sink_output_delivery.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .workflow_run_sink_output_delivery_check import WorkflowRunSinkOutputDeliveryCheck + + +class WorkflowRunSinkOutputDelivery(UniversalBaseModel): + passed: bool + failing_checks: typing.List[WorkflowRunSinkOutputDeliveryCheck] + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/onepin/types/workflow_run_sink_output_delivery_check.py b/src/onepin/types/workflow_run_sink_output_delivery_check.py new file mode 100644 index 0000000..d0f2249 --- /dev/null +++ b/src/onepin/types/workflow_run_sink_output_delivery_check.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class WorkflowRunSinkOutputDeliveryCheck(UniversalBaseModel): + kind: str + label: str + score: typing.Optional[float] = None + threshold: typing.Optional[float] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/onepin/types/workflow_run_sink_output_line.py b/src/onepin/types/workflow_run_sink_output_line.py new file mode 100644 index 0000000..cf09ceb --- /dev/null +++ b/src/onepin/types/workflow_run_sink_output_line.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .workflow_run_sink_output_delivery import WorkflowRunSinkOutputDelivery + + +class WorkflowRunSinkOutputLine(UniversalBaseModel): + audio_files: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = None + recommended_audio_file_index: typing.Optional[int] = None + delivery: typing.Optional[WorkflowRunSinkOutputDelivery] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/onepin/types/workflow_run_sink_output_out.py b/src/onepin/types/workflow_run_sink_output_out.py index af315b0..d3110e2 100644 --- a/src/onepin/types/workflow_run_sink_output_out.py +++ b/src/onepin/types/workflow_run_sink_output_out.py @@ -4,6 +4,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .workflow_run_sink_output_line import WorkflowRunSinkOutputLine class WorkflowRunSinkOutputOut(UniversalBaseModel): @@ -11,7 +12,7 @@ class WorkflowRunSinkOutputOut(UniversalBaseModel): node_type: str display_name: str status: str - lines: typing.List[typing.Dict[str, typing.Any]] + lines: typing.List[WorkflowRunSinkOutputLine] line_count: int truncated: bool diff --git a/src/onepin/users/client.py b/src/onepin/users/client.py index 4de6674..e2a5d91 100644 --- a/src/onepin/users/client.py +++ b/src/onepin/users/client.py @@ -42,9 +42,14 @@ def get_my_credits(self, *, request_options: typing.Optional[RequestOptions] = N settled ledger entries and may temporarily exceed `balance` while a workflow run holds an open reserve. `used` reflects credits consumed in the current billing period. `plan_grant` is the total monthly credit allowance for the - caller's plan, enabling a "X / Y used" display. `period_start` and - `period_end` mark the boundaries of the current billing window; free-tier - callers use a calendar-month boundary. + caller's plan, enabling a "X / Y used" display. `period_start` is the current + credit anchor and `period_end` is the next EXPECTED credit-reset boundary + (`period_start` + 1 month), or null when no reset is promised — Free/one-time, + unanchored, a canceling/ended entitlement, or a monthly renewal whose boundary + passed without confirmed payment. `period_end` is the expected boundary, not a + guaranteed grant time: monthly credits stay gated on successful Stripe payment. + For an annual subscriber this GET may perform idempotent maintenance, granting + any due intermediate monthly credits before returning; retries remain safe. Parameters ---------- @@ -276,9 +281,14 @@ async def get_my_credits( settled ledger entries and may temporarily exceed `balance` while a workflow run holds an open reserve. `used` reflects credits consumed in the current billing period. `plan_grant` is the total monthly credit allowance for the - caller's plan, enabling a "X / Y used" display. `period_start` and - `period_end` mark the boundaries of the current billing window; free-tier - callers use a calendar-month boundary. + caller's plan, enabling a "X / Y used" display. `period_start` is the current + credit anchor and `period_end` is the next EXPECTED credit-reset boundary + (`period_start` + 1 month), or null when no reset is promised — Free/one-time, + unanchored, a canceling/ended entitlement, or a monthly renewal whose boundary + passed without confirmed payment. `period_end` is the expected boundary, not a + guaranteed grant time: monthly credits stay gated on successful Stripe payment. + For an annual subscriber this GET may perform idempotent maintenance, granting + any due intermediate monthly credits before returning; retries remain safe. Parameters ---------- diff --git a/src/onepin/users/raw_client.py b/src/onepin/users/raw_client.py index 6da6376..d86b812 100644 --- a/src/onepin/users/raw_client.py +++ b/src/onepin/users/raw_client.py @@ -39,9 +39,14 @@ def get_my_credits( settled ledger entries and may temporarily exceed `balance` while a workflow run holds an open reserve. `used` reflects credits consumed in the current billing period. `plan_grant` is the total monthly credit allowance for the - caller's plan, enabling a "X / Y used" display. `period_start` and - `period_end` mark the boundaries of the current billing window; free-tier - callers use a calendar-month boundary. + caller's plan, enabling a "X / Y used" display. `period_start` is the current + credit anchor and `period_end` is the next EXPECTED credit-reset boundary + (`period_start` + 1 month), or null when no reset is promised — Free/one-time, + unanchored, a canceling/ended entitlement, or a monthly renewal whose boundary + passed without confirmed payment. `period_end` is the expected boundary, not a + guaranteed grant time: monthly credits stay gated on successful Stripe payment. + For an annual subscriber this GET may perform idempotent maintenance, granting + any due intermediate monthly credits before returning; retries remain safe. Parameters ---------- @@ -385,9 +390,14 @@ async def get_my_credits( settled ledger entries and may temporarily exceed `balance` while a workflow run holds an open reserve. `used` reflects credits consumed in the current billing period. `plan_grant` is the total monthly credit allowance for the - caller's plan, enabling a "X / Y used" display. `period_start` and - `period_end` mark the boundaries of the current billing window; free-tier - callers use a calendar-month boundary. + caller's plan, enabling a "X / Y used" display. `period_start` is the current + credit anchor and `period_end` is the next EXPECTED credit-reset boundary + (`period_start` + 1 month), or null when no reset is promised — Free/one-time, + unanchored, a canceling/ended entitlement, or a monthly renewal whose boundary + passed without confirmed payment. `period_end` is the expected boundary, not a + guaranteed grant time: monthly credits stay gated on successful Stripe payment. + For an annual subscriber this GET may perform idempotent maintenance, granting + any due intermediate monthly credits before returning; retries remain safe. Parameters ---------- diff --git a/src/onepin/voices/__init__.py b/src/onepin/voices/__init__.py index ce7c6af..b086207 100644 --- a/src/onepin/voices/__init__.py +++ b/src/onepin/voices/__init__.py @@ -12,6 +12,7 @@ ListVoicesRequestOrderItem, ListVoicesRequestSortItem, ListVoicesRequestSourceItem, + PreviewVoicesRequestLanguage, ) _dynamic_imports: typing.Dict[str, str] = { "GetVoiceFacetsApiV1VoicesFacetsGetRequestSourceItem": ".types", @@ -19,6 +20,7 @@ "ListVoicesRequestOrderItem": ".types", "ListVoicesRequestSortItem": ".types", "ListVoicesRequestSourceItem": ".types", + "PreviewVoicesRequestLanguage": ".types", } @@ -49,4 +51,5 @@ def __dir__(): "ListVoicesRequestOrderItem", "ListVoicesRequestSortItem", "ListVoicesRequestSourceItem", + "PreviewVoicesRequestLanguage", ] diff --git a/src/onepin/voices/client.py b/src/onepin/voices/client.py index 8ad7f77..4f224d7 100644 --- a/src/onepin/voices/client.py +++ b/src/onepin/voices/client.py @@ -9,6 +9,7 @@ from ..types.api_response_dict import ApiResponseDict from ..types.api_response_voice_facets_out import ApiResponseVoiceFacetsOut from ..types.api_response_voice_out import ApiResponseVoiceOut +from ..types.api_response_voice_preview_out import ApiResponseVoicePreviewOut from ..types.voice_accent import VoiceAccent from ..types.voice_age import VoiceAge from ..types.voice_category import VoiceCategory @@ -21,6 +22,7 @@ from .types.list_voices_request_order_item import ListVoicesRequestOrderItem from .types.list_voices_request_sort_item import ListVoicesRequestSortItem from .types.list_voices_request_source_item import ListVoicesRequestSourceItem +from .types.preview_voices_request_language import PreviewVoicesRequestLanguage class VoicesClient: @@ -107,7 +109,7 @@ def list( Repeat for OR search : typing.Optional[str] - Full-text search against voice name, description, and tags. + Searches name, tags, and the voice's summary-derived descriptor text (closely tracks the served description; summary beyond 200 chars is not searched). sort : typing.Optional[typing.Sequence[ListVoicesRequestSortItem]] Repeat for multi-sort. Pairs with `order` index-wise. @@ -382,6 +384,73 @@ def similar( ) return _response.data + def preview( + self, + voice_id: str, + *, + language: PreviewVoicesRequestLanguage, + model: typing.Optional[str] = None, + workspace_id: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ApiResponseVoicePreviewOut: + """ + Fetch ready-to-play preview audio for one voice in one language. + + Returns the preview's `locale`, the `model` it was synthesized with, its + stored `content_type`, and a `sample_url` — a time-limited presigned URL + valid for 1 hour; regenerate it by calling this endpoint again rather than + caching it long-term. Pass `model` to prefer a specific TTS model; a + preview from another model is still returned when that model has none. + + `language` accepts a bare family (`ko`, `en`) as well as an exact locale + (`ko-kr`, `en-gb`). A bare family expands to every supported locale in it, + and which region wins is deterministic but arbitrary — so the response + echoes the `locale` actually served. Read `locale`, never the request + parameter, when labelling what the caller is hearing. + + 404 means no preview audio has been generated for this voice in that + locale — NOT that the voice cannot speak it. `supported_languages` on the + voice is the claim about what it can speak; `preview_locales` is the list + of locales this endpoint will succeed for. 404 is also returned when the + voice does not exist or is not accessible to the caller's workspace. + + Parameters + ---------- + voice_id : str + + language : PreviewVoicesRequestLanguage + BCP-47 language code, e.g. en-us, ko-kr + + model : typing.Optional[str] + TTS model id, e.g. sonic-2 + + workspace_id : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ApiResponseVoicePreviewOut + Successful Response + + Examples + -------- + from onepin import OnePinClient + + client = OnePinClient( + token="YOUR_TOKEN", + ) + client.voices.preview( + voice_id="voice_id", + language="de", + ) + """ + _response = self._raw_client.preview( + voice_id, language=language, model=model, workspace_id=workspace_id, request_options=request_options + ) + return _response.data + def favorite_voice( self, voice_id: str, @@ -556,7 +625,7 @@ async def list( Repeat for OR search : typing.Optional[str] - Full-text search against voice name, description, and tags. + Searches name, tags, and the voice's summary-derived descriptor text (closely tracks the served description; summary beyond 200 chars is not searched). sort : typing.Optional[typing.Sequence[ListVoicesRequestSortItem]] Repeat for multi-sort. Pairs with `order` index-wise. @@ -863,6 +932,81 @@ async def main() -> None: ) return _response.data + async def preview( + self, + voice_id: str, + *, + language: PreviewVoicesRequestLanguage, + model: typing.Optional[str] = None, + workspace_id: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ApiResponseVoicePreviewOut: + """ + Fetch ready-to-play preview audio for one voice in one language. + + Returns the preview's `locale`, the `model` it was synthesized with, its + stored `content_type`, and a `sample_url` — a time-limited presigned URL + valid for 1 hour; regenerate it by calling this endpoint again rather than + caching it long-term. Pass `model` to prefer a specific TTS model; a + preview from another model is still returned when that model has none. + + `language` accepts a bare family (`ko`, `en`) as well as an exact locale + (`ko-kr`, `en-gb`). A bare family expands to every supported locale in it, + and which region wins is deterministic but arbitrary — so the response + echoes the `locale` actually served. Read `locale`, never the request + parameter, when labelling what the caller is hearing. + + 404 means no preview audio has been generated for this voice in that + locale — NOT that the voice cannot speak it. `supported_languages` on the + voice is the claim about what it can speak; `preview_locales` is the list + of locales this endpoint will succeed for. 404 is also returned when the + voice does not exist or is not accessible to the caller's workspace. + + Parameters + ---------- + voice_id : str + + language : PreviewVoicesRequestLanguage + BCP-47 language code, e.g. en-us, ko-kr + + model : typing.Optional[str] + TTS model id, e.g. sonic-2 + + workspace_id : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ApiResponseVoicePreviewOut + Successful Response + + Examples + -------- + import asyncio + + from onepin import AsyncOnePinClient + + client = AsyncOnePinClient( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.voices.preview( + voice_id="voice_id", + language="de", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.preview( + voice_id, language=language, model=model, workspace_id=workspace_id, request_options=request_options + ) + return _response.data + async def favorite_voice( self, voice_id: str, diff --git a/src/onepin/voices/raw_client.py b/src/onepin/voices/raw_client.py index ecfef41..1e22d87 100644 --- a/src/onepin/voices/raw_client.py +++ b/src/onepin/voices/raw_client.py @@ -16,6 +16,7 @@ from ..types.api_response_dict import ApiResponseDict from ..types.api_response_voice_facets_out import ApiResponseVoiceFacetsOut from ..types.api_response_voice_out import ApiResponseVoiceOut +from ..types.api_response_voice_preview_out import ApiResponseVoicePreviewOut from ..types.voice_accent import VoiceAccent from ..types.voice_age import VoiceAge from ..types.voice_category import VoiceCategory @@ -27,6 +28,7 @@ from .types.list_voices_request_order_item import ListVoicesRequestOrderItem from .types.list_voices_request_sort_item import ListVoicesRequestSortItem from .types.list_voices_request_source_item import ListVoicesRequestSourceItem +from .types.preview_voices_request_language import PreviewVoicesRequestLanguage from pydantic import ValidationError @@ -103,7 +105,7 @@ def list( Repeat for OR search : typing.Optional[str] - Full-text search against voice name, description, and tags. + Searches name, tags, and the voice's summary-derived descriptor text (closely tracks the served description; summary beyond 200 chars is not searched). sort : typing.Optional[typing.Sequence[ListVoicesRequestSortItem]] Repeat for multi-sort. Pairs with `order` index-wise. @@ -478,6 +480,98 @@ def similar( ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def preview( + self, + voice_id: str, + *, + language: PreviewVoicesRequestLanguage, + model: typing.Optional[str] = None, + workspace_id: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ApiResponseVoicePreviewOut]: + """ + Fetch ready-to-play preview audio for one voice in one language. + + Returns the preview's `locale`, the `model` it was synthesized with, its + stored `content_type`, and a `sample_url` — a time-limited presigned URL + valid for 1 hour; regenerate it by calling this endpoint again rather than + caching it long-term. Pass `model` to prefer a specific TTS model; a + preview from another model is still returned when that model has none. + + `language` accepts a bare family (`ko`, `en`) as well as an exact locale + (`ko-kr`, `en-gb`). A bare family expands to every supported locale in it, + and which region wins is deterministic but arbitrary — so the response + echoes the `locale` actually served. Read `locale`, never the request + parameter, when labelling what the caller is hearing. + + 404 means no preview audio has been generated for this voice in that + locale — NOT that the voice cannot speak it. `supported_languages` on the + voice is the claim about what it can speak; `preview_locales` is the list + of locales this endpoint will succeed for. 404 is also returned when the + voice does not exist or is not accessible to the caller's workspace. + + Parameters + ---------- + voice_id : str + + language : PreviewVoicesRequestLanguage + BCP-47 language code, e.g. en-us, ko-kr + + model : typing.Optional[str] + TTS model id, e.g. sonic-2 + + workspace_id : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ApiResponseVoicePreviewOut] + Successful Response + """ + _response = self._client_wrapper.httpx_client.request( + f"api/v1/voices/{encode_path_param(voice_id)}/preview", + method="GET", + params={ + "language": language, + "model": model, + }, + headers={ + "X-Workspace-Id": str(workspace_id) if workspace_id is not None else None, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ApiResponseVoicePreviewOut, + parse_obj_as( + type_=ApiResponseVoicePreviewOut, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 422: + raise UnprocessableEntityError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def favorite_voice( self, voice_id: str, @@ -685,7 +779,7 @@ async def list( Repeat for OR search : typing.Optional[str] - Full-text search against voice name, description, and tags. + Searches name, tags, and the voice's summary-derived descriptor text (closely tracks the served description; summary beyond 200 chars is not searched). sort : typing.Optional[typing.Sequence[ListVoicesRequestSortItem]] Repeat for multi-sort. Pairs with `order` index-wise. @@ -1060,6 +1154,98 @@ async def similar( ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + async def preview( + self, + voice_id: str, + *, + language: PreviewVoicesRequestLanguage, + model: typing.Optional[str] = None, + workspace_id: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ApiResponseVoicePreviewOut]: + """ + Fetch ready-to-play preview audio for one voice in one language. + + Returns the preview's `locale`, the `model` it was synthesized with, its + stored `content_type`, and a `sample_url` — a time-limited presigned URL + valid for 1 hour; regenerate it by calling this endpoint again rather than + caching it long-term. Pass `model` to prefer a specific TTS model; a + preview from another model is still returned when that model has none. + + `language` accepts a bare family (`ko`, `en`) as well as an exact locale + (`ko-kr`, `en-gb`). A bare family expands to every supported locale in it, + and which region wins is deterministic but arbitrary — so the response + echoes the `locale` actually served. Read `locale`, never the request + parameter, when labelling what the caller is hearing. + + 404 means no preview audio has been generated for this voice in that + locale — NOT that the voice cannot speak it. `supported_languages` on the + voice is the claim about what it can speak; `preview_locales` is the list + of locales this endpoint will succeed for. 404 is also returned when the + voice does not exist or is not accessible to the caller's workspace. + + Parameters + ---------- + voice_id : str + + language : PreviewVoicesRequestLanguage + BCP-47 language code, e.g. en-us, ko-kr + + model : typing.Optional[str] + TTS model id, e.g. sonic-2 + + workspace_id : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ApiResponseVoicePreviewOut] + Successful Response + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/v1/voices/{encode_path_param(voice_id)}/preview", + method="GET", + params={ + "language": language, + "model": model, + }, + headers={ + "X-Workspace-Id": str(workspace_id) if workspace_id is not None else None, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ApiResponseVoicePreviewOut, + parse_obj_as( + type_=ApiResponseVoicePreviewOut, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 422: + raise UnprocessableEntityError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + async def favorite_voice( self, voice_id: str, diff --git a/src/onepin/voices/types/__init__.py b/src/onepin/voices/types/__init__.py index 3c4972a..1654a73 100644 --- a/src/onepin/voices/types/__init__.py +++ b/src/onepin/voices/types/__init__.py @@ -13,12 +13,14 @@ from .list_voices_request_order_item import ListVoicesRequestOrderItem from .list_voices_request_sort_item import ListVoicesRequestSortItem from .list_voices_request_source_item import ListVoicesRequestSourceItem + from .preview_voices_request_language import PreviewVoicesRequestLanguage _dynamic_imports: typing.Dict[str, str] = { "GetVoiceFacetsApiV1VoicesFacetsGetRequestSourceItem": ".get_voice_facets_api_v1voices_facets_get_request_source_item", "ListVoicesRequestLanguageItem": ".list_voices_request_language_item", "ListVoicesRequestOrderItem": ".list_voices_request_order_item", "ListVoicesRequestSortItem": ".list_voices_request_sort_item", "ListVoicesRequestSourceItem": ".list_voices_request_source_item", + "PreviewVoicesRequestLanguage": ".preview_voices_request_language", } @@ -49,4 +51,5 @@ def __dir__(): "ListVoicesRequestOrderItem", "ListVoicesRequestSortItem", "ListVoicesRequestSourceItem", + "PreviewVoicesRequestLanguage", ] diff --git a/src/onepin/voices/types/list_voices_request_language_item.py b/src/onepin/voices/types/list_voices_request_language_item.py index 535d427..d2a3d6f 100644 --- a/src/onepin/voices/types/list_voices_request_language_item.py +++ b/src/onepin/voices/types/list_voices_request_language_item.py @@ -3,5 +3,6 @@ import typing ListVoicesRequestLanguageItem = typing.Union[ - typing.Literal["de-de", "en-gb", "en-us", "es-es", "fr-fr", "it-it", "ja-jp", "ko-kr", "pt-br", "zh-cn"], typing.Any + typing.Literal["de-de", "en-gb", "en-us", "es-es", "es-mx", "fr-fr", "ja-jp", "ko-kr", "pt-br", "pt-pt", "zh-cn"], + typing.Any, ] diff --git a/src/onepin/voices/types/preview_voices_request_language.py b/src/onepin/voices/types/preview_voices_request_language.py new file mode 100644 index 0000000..2a575ab --- /dev/null +++ b/src/onepin/voices/types/preview_voices_request_language.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +PreviewVoicesRequestLanguage = typing.Union[ + typing.Literal[ + "de", + "de-de", + "en", + "en-gb", + "en-us", + "es", + "es-es", + "es-mx", + "fr", + "fr-fr", + "ja", + "ja-jp", + "ko", + "ko-kr", + "pt", + "pt-br", + "pt-pt", + "zh", + "zh-cn", + ], + typing.Any, +]