diff --git a/.fern/metadata.json b/.fern/metadata.json index c9ecd919..81b37dcc 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -1,7 +1,7 @@ { - "cliVersion": "3.77.1", + "cliVersion": "4.4.1", "generatorName": "fernapi/fern-python-sdk", - "generatorVersion": "4.57.2", + "generatorVersion": "4.61.4", "generatorConfig": { "client": { "class_name": "BaseClient", @@ -16,5 +16,5 @@ "skip_validation": true } }, - "sdkVersion": "6.0.1" + "sdkVersion": "6.1.0" } \ No newline at end of file diff --git a/.fernignore b/.fernignore index 1086ff6d..14ac34da 100644 --- a/.fernignore +++ b/.fernignore @@ -6,10 +6,12 @@ src/deepgram/client.py # WireMock mappings: removed duplicate empty-body /v1/listen stub that causes # non-deterministic matching failures -wiremock/wiremock-mappings.json +# [temporarily frozen — .bak preserves our patch during regen] +wiremock/wiremock-mappings.json.bak # Wire test with manual fix: transcribe_file() requires request=bytes parameter -tests/wire/test_listen_v1_media.py +# [temporarily frozen — .bak preserves our patch during regen] +tests/wire/test_listen_v1_media.py.bak # WebSocket socket clients: # - Optional message parameter defaults for send_flush, send_close, send_clear, @@ -17,22 +19,26 @@ tests/wire/test_listen_v1_media.py # - construct_type instead of parse_obj_as (skip_validation for unknown WS messages) # - except Exception (broad catch for custom transports) # - _sanitize_numeric_types in agent socket client (float→int for API) -src/deepgram/speak/v1/socket_client.py -src/deepgram/listen/v1/socket_client.py -src/deepgram/listen/v2/socket_client.py -src/deepgram/agent/v1/socket_client.py +# [temporarily frozen — .bak preserves our patches during regen] +src/deepgram/speak/v1/socket_client.py.bak +src/deepgram/listen/v1/socket_client.py.bak +src/deepgram/listen/v2/socket_client.py.bak +src/deepgram/agent/v1/socket_client.py.bak # Type files with manual int type corrections (Fern generates float for speaker/channel/num_words) -src/deepgram/types/listen_v1response_results_utterances_item.py -src/deepgram/types/listen_v1response_results_utterances_item_words_item.py -src/deepgram/types/listen_v1response_results_channels_item_alternatives_item_paragraphs_paragraphs_item.py +# [temporarily frozen — .bak preserves our patches during regen] +src/deepgram/types/listen_v1response_results_utterances_item.py.bak +src/deepgram/types/listen_v1response_results_utterances_item_words_item.py.bak +src/deepgram/types/listen_v1response_results_channels_item_alternatives_item_paragraphs_paragraphs_item.py.bak # Redact type with Union[str, Sequence[str]] support (Fern narrows to Union[Literal, Any]) -src/deepgram/types/listen_v1redact.py +# [temporarily frozen — .bak preserves our patch during regen] +src/deepgram/types/listen_v1redact.py.bak # Listen client files with Union[str, Sequence[str]] array param support -src/deepgram/listen/v1/client.py -src/deepgram/listen/v2/client.py +# [temporarily frozen — .bak preserves our patches during regen] +src/deepgram/listen/v1/client.py.bak +src/deepgram/listen/v2/client.py.bak # Hand-written custom tests tests/custom/test_text_builder.py @@ -75,4 +81,4 @@ AGENTS.md # Folders to ignore .github docs -examples \ No newline at end of file +examples diff --git a/poetry.lock b/poetry.lock index 4d721e91..fc5512eb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -38,13 +38,13 @@ trio = ["trio (>=0.26.1)"] [[package]] name = "certifi" -version = "2026.1.4" +version = "2026.2.25" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" files = [ - {file = "certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c"}, - {file = "certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120"}, + {file = "certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa"}, + {file = "certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index a11551e5..a45ee00d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ dynamic = ["version"] [tool.poetry] name = "deepgram-sdk" -version = "6.0.1" +version = "6.1.0" description = "" readme = "README.md" authors = [] diff --git a/src/deepgram/agent/__init__.py b/src/deepgram/agent/__init__.py index 30038648..cd2e01da 100644 --- a/src/deepgram/agent/__init__.py +++ b/src/deepgram/agent/__init__.py @@ -82,10 +82,18 @@ AgentV1SettingsParams, AgentV1SpeakUpdated, AgentV1SpeakUpdatedParams, + AgentV1ThinkUpdated, + AgentV1ThinkUpdatedParams, AgentV1UpdatePrompt, AgentV1UpdatePromptParams, AgentV1UpdateSpeak, AgentV1UpdateSpeakParams, + AgentV1UpdateSpeakSpeak, + AgentV1UpdateSpeakSpeakParams, + AgentV1UpdateThink, + AgentV1UpdateThinkParams, + AgentV1UpdateThinkThink, + AgentV1UpdateThinkThinkParams, AgentV1UserStartedSpeaking, AgentV1UserStartedSpeakingParams, AgentV1Warning, @@ -168,10 +176,18 @@ "AgentV1SettingsParams": ".v1", "AgentV1SpeakUpdated": ".v1", "AgentV1SpeakUpdatedParams": ".v1", + "AgentV1ThinkUpdated": ".v1", + "AgentV1ThinkUpdatedParams": ".v1", "AgentV1UpdatePrompt": ".v1", "AgentV1UpdatePromptParams": ".v1", "AgentV1UpdateSpeak": ".v1", "AgentV1UpdateSpeakParams": ".v1", + "AgentV1UpdateSpeakSpeak": ".v1", + "AgentV1UpdateSpeakSpeakParams": ".v1", + "AgentV1UpdateThink": ".v1", + "AgentV1UpdateThinkParams": ".v1", + "AgentV1UpdateThinkThink": ".v1", + "AgentV1UpdateThinkThinkParams": ".v1", "AgentV1UserStartedSpeaking": ".v1", "AgentV1UserStartedSpeakingParams": ".v1", "AgentV1Warning": ".v1", @@ -278,10 +294,18 @@ def __dir__(): "AgentV1SettingsParams", "AgentV1SpeakUpdated", "AgentV1SpeakUpdatedParams", + "AgentV1ThinkUpdated", + "AgentV1ThinkUpdatedParams", "AgentV1UpdatePrompt", "AgentV1UpdatePromptParams", "AgentV1UpdateSpeak", "AgentV1UpdateSpeakParams", + "AgentV1UpdateSpeakSpeak", + "AgentV1UpdateSpeakSpeakParams", + "AgentV1UpdateThink", + "AgentV1UpdateThinkParams", + "AgentV1UpdateThinkThink", + "AgentV1UpdateThinkThinkParams", "AgentV1UserStartedSpeaking", "AgentV1UserStartedSpeakingParams", "AgentV1Warning", diff --git a/src/deepgram/agent/v1/__init__.py b/src/deepgram/agent/v1/__init__.py index b7347cb8..a6de165a 100644 --- a/src/deepgram/agent/v1/__init__.py +++ b/src/deepgram/agent/v1/__init__.py @@ -46,8 +46,12 @@ AgentV1SettingsAudioOutputEncoding, AgentV1SettingsFlags, AgentV1SpeakUpdated, + AgentV1ThinkUpdated, AgentV1UpdatePrompt, AgentV1UpdateSpeak, + AgentV1UpdateSpeakSpeak, + AgentV1UpdateThink, + AgentV1UpdateThinkThink, AgentV1UserStartedSpeaking, AgentV1Warning, AgentV1Welcome, @@ -89,8 +93,12 @@ AgentV1SettingsFlagsParams, AgentV1SettingsParams, AgentV1SpeakUpdatedParams, + AgentV1ThinkUpdatedParams, AgentV1UpdatePromptParams, AgentV1UpdateSpeakParams, + AgentV1UpdateSpeakSpeakParams, + AgentV1UpdateThinkParams, + AgentV1UpdateThinkThinkParams, AgentV1UserStartedSpeakingParams, AgentV1WarningParams, AgentV1WelcomeParams, @@ -170,10 +178,18 @@ "AgentV1SettingsParams": ".requests", "AgentV1SpeakUpdated": ".types", "AgentV1SpeakUpdatedParams": ".requests", + "AgentV1ThinkUpdated": ".types", + "AgentV1ThinkUpdatedParams": ".requests", "AgentV1UpdatePrompt": ".types", "AgentV1UpdatePromptParams": ".requests", "AgentV1UpdateSpeak": ".types", "AgentV1UpdateSpeakParams": ".requests", + "AgentV1UpdateSpeakSpeak": ".types", + "AgentV1UpdateSpeakSpeakParams": ".requests", + "AgentV1UpdateThink": ".types", + "AgentV1UpdateThinkParams": ".requests", + "AgentV1UpdateThinkThink": ".types", + "AgentV1UpdateThinkThinkParams": ".requests", "AgentV1UserStartedSpeaking": ".types", "AgentV1UserStartedSpeakingParams": ".requests", "AgentV1Warning": ".types", @@ -280,10 +296,18 @@ def __dir__(): "AgentV1SettingsParams", "AgentV1SpeakUpdated", "AgentV1SpeakUpdatedParams", + "AgentV1ThinkUpdated", + "AgentV1ThinkUpdatedParams", "AgentV1UpdatePrompt", "AgentV1UpdatePromptParams", "AgentV1UpdateSpeak", "AgentV1UpdateSpeakParams", + "AgentV1UpdateSpeakSpeak", + "AgentV1UpdateSpeakSpeakParams", + "AgentV1UpdateThink", + "AgentV1UpdateThinkParams", + "AgentV1UpdateThinkThink", + "AgentV1UpdateThinkThinkParams", "AgentV1UserStartedSpeaking", "AgentV1UserStartedSpeakingParams", "AgentV1Warning", diff --git a/src/deepgram/agent/v1/requests/__init__.py b/src/deepgram/agent/v1/requests/__init__.py index 9708fd24..381a3538 100644 --- a/src/deepgram/agent/v1/requests/__init__.py +++ b/src/deepgram/agent/v1/requests/__init__.py @@ -49,8 +49,12 @@ from .agent_v1settings_audio_output import AgentV1SettingsAudioOutputParams from .agent_v1settings_flags import AgentV1SettingsFlagsParams from .agent_v1speak_updated import AgentV1SpeakUpdatedParams + from .agent_v1think_updated import AgentV1ThinkUpdatedParams from .agent_v1update_prompt import AgentV1UpdatePromptParams from .agent_v1update_speak import AgentV1UpdateSpeakParams + from .agent_v1update_speak_speak import AgentV1UpdateSpeakSpeakParams + from .agent_v1update_think import AgentV1UpdateThinkParams + from .agent_v1update_think_think import AgentV1UpdateThinkThinkParams from .agent_v1user_started_speaking import AgentV1UserStartedSpeakingParams from .agent_v1warning import AgentV1WarningParams from .agent_v1welcome import AgentV1WelcomeParams @@ -90,8 +94,12 @@ "AgentV1SettingsFlagsParams": ".agent_v1settings_flags", "AgentV1SettingsParams": ".agent_v1settings", "AgentV1SpeakUpdatedParams": ".agent_v1speak_updated", + "AgentV1ThinkUpdatedParams": ".agent_v1think_updated", "AgentV1UpdatePromptParams": ".agent_v1update_prompt", "AgentV1UpdateSpeakParams": ".agent_v1update_speak", + "AgentV1UpdateSpeakSpeakParams": ".agent_v1update_speak_speak", + "AgentV1UpdateThinkParams": ".agent_v1update_think", + "AgentV1UpdateThinkThinkParams": ".agent_v1update_think_think", "AgentV1UserStartedSpeakingParams": ".agent_v1user_started_speaking", "AgentV1WarningParams": ".agent_v1warning", "AgentV1WelcomeParams": ".agent_v1welcome", @@ -155,8 +163,12 @@ def __dir__(): "AgentV1SettingsFlagsParams", "AgentV1SettingsParams", "AgentV1SpeakUpdatedParams", + "AgentV1ThinkUpdatedParams", "AgentV1UpdatePromptParams", "AgentV1UpdateSpeakParams", + "AgentV1UpdateSpeakSpeakParams", + "AgentV1UpdateThinkParams", + "AgentV1UpdateThinkThinkParams", "AgentV1UserStartedSpeakingParams", "AgentV1WarningParams", "AgentV1WelcomeParams", diff --git a/src/deepgram/agent/v1/requests/agent_v1think_updated.py b/src/deepgram/agent/v1/requests/agent_v1think_updated.py new file mode 100644 index 00000000..6fd752a5 --- /dev/null +++ b/src/deepgram/agent/v1/requests/agent_v1think_updated.py @@ -0,0 +1,12 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import typing_extensions + + +class AgentV1ThinkUpdatedParams(typing_extensions.TypedDict): + type: typing.Literal["ThinkUpdated"] + """ + Message type identifier for think update confirmation + """ diff --git a/src/deepgram/agent/v1/requests/agent_v1update_speak.py b/src/deepgram/agent/v1/requests/agent_v1update_speak.py index c5820907..c740bd07 100644 --- a/src/deepgram/agent/v1/requests/agent_v1update_speak.py +++ b/src/deepgram/agent/v1/requests/agent_v1update_speak.py @@ -3,7 +3,7 @@ import typing import typing_extensions -from ....requests.speak_settings_v1 import SpeakSettingsV1Params +from .agent_v1update_speak_speak import AgentV1UpdateSpeakSpeakParams class AgentV1UpdateSpeakParams(typing_extensions.TypedDict): @@ -12,4 +12,4 @@ class AgentV1UpdateSpeakParams(typing_extensions.TypedDict): Message type identifier for updating the speak model """ - speak: SpeakSettingsV1Params + speak: AgentV1UpdateSpeakSpeakParams diff --git a/src/deepgram/agent/v1/requests/agent_v1update_speak_speak.py b/src/deepgram/agent/v1/requests/agent_v1update_speak_speak.py new file mode 100644 index 00000000..b37eea79 --- /dev/null +++ b/src/deepgram/agent/v1/requests/agent_v1update_speak_speak.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....requests.speak_settings_v1 import SpeakSettingsV1Params + +AgentV1UpdateSpeakSpeakParams = typing.Union[SpeakSettingsV1Params, typing.Sequence[SpeakSettingsV1Params]] diff --git a/src/deepgram/agent/v1/requests/agent_v1update_think.py b/src/deepgram/agent/v1/requests/agent_v1update_think.py new file mode 100644 index 00000000..41e6e13e --- /dev/null +++ b/src/deepgram/agent/v1/requests/agent_v1update_think.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import typing_extensions +from .agent_v1update_think_think import AgentV1UpdateThinkThinkParams + + +class AgentV1UpdateThinkParams(typing_extensions.TypedDict): + type: typing.Literal["UpdateThink"] + """ + Message type identifier for updating the think model + """ + + think: AgentV1UpdateThinkThinkParams diff --git a/src/deepgram/agent/v1/requests/agent_v1update_think_think.py b/src/deepgram/agent/v1/requests/agent_v1update_think_think.py new file mode 100644 index 00000000..ffa3ce71 --- /dev/null +++ b/src/deepgram/agent/v1/requests/agent_v1update_think_think.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....requests.think_settings_v1 import ThinkSettingsV1Params + +AgentV1UpdateThinkThinkParams = typing.Union[ThinkSettingsV1Params, typing.Sequence[ThinkSettingsV1Params]] diff --git a/src/deepgram/agent/v1/socket_client.py b/src/deepgram/agent/v1/socket_client.py index e486dc67..df973ee2 100644 --- a/src/deepgram/agent/v1/socket_client.py +++ b/src/deepgram/agent/v1/socket_client.py @@ -1,13 +1,14 @@ # This file was auto-generated by Fern from our API Definition. import json +import logging import typing from json.decoder import JSONDecodeError import websockets import websockets.sync.connection as websockets_sync_connection from ...core.events import EventEmitterMixin, EventType -from ...core.unchecked_base_model import construct_type +from ...core.pydantic_utilities import parse_obj_as from .types.agent_v1agent_audio_done import AgentV1AgentAudioDone from .types.agent_v1agent_started_speaking import AgentV1AgentStartedSpeaking from .types.agent_v1agent_thinking import AgentV1AgentThinking @@ -24,8 +25,10 @@ from .types.agent_v1settings import AgentV1Settings from .types.agent_v1settings_applied import AgentV1SettingsApplied from .types.agent_v1speak_updated import AgentV1SpeakUpdated +from .types.agent_v1think_updated import AgentV1ThinkUpdated from .types.agent_v1update_prompt import AgentV1UpdatePrompt from .types.agent_v1update_speak import AgentV1UpdateSpeak +from .types.agent_v1update_think import AgentV1UpdateThink from .types.agent_v1user_started_speaking import AgentV1UserStartedSpeaking from .types.agent_v1warning import AgentV1Warning from .types.agent_v1welcome import AgentV1Welcome @@ -35,30 +38,12 @@ except ImportError: from websockets import WebSocketClientProtocol # type: ignore -def _sanitize_numeric_types(obj: typing.Any) -> typing.Any: - """ - Recursively convert float values that are whole numbers to int. - - Workaround for Fern-generated models that type integer API fields - (like sample_rate) as float, causing JSON serialization to produce - values like 44100.0 instead of 44100. The Deepgram API rejects - float representations of integer fields. - - See: https://github.com/deepgram/internal-api-specs/issues/205 - """ - if isinstance(obj, dict): - return {k: _sanitize_numeric_types(v) for k, v in obj.items()} - elif isinstance(obj, list): - return [_sanitize_numeric_types(item) for item in obj] - elif isinstance(obj, float) and obj.is_integer(): - return int(obj) - return obj - - +_logger = logging.getLogger(__name__) V1SocketClientResponse = typing.Union[ AgentV1ReceiveFunctionCallResponse, AgentV1PromptUpdated, AgentV1SpeakUpdated, + AgentV1ThinkUpdated, AgentV1InjectionRefused, AgentV1Welcome, AgentV1SettingsApplied, @@ -84,7 +69,13 @@ async def __aiter__(self): if isinstance(message, bytes): yield message else: - yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore + try: + yield parse_obj_as(V1SocketClientResponse, json.loads(message)) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue async def start_listening(self): """ @@ -103,9 +94,15 @@ async def start_listening(self): parsed = raw_message else: json_data = json.loads(raw_message) - parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + try: + parsed = parse_obj_as(V1SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue await self._emit_async(EventType.MESSAGE, parsed) - except Exception as exc: + except (websockets.WebSocketException, JSONDecodeError) as exc: await self._emit_async(EventType.ERROR, exc) finally: await self._emit_async(EventType.CLOSE, None) @@ -124,6 +121,13 @@ async def send_update_speak(self, message: AgentV1UpdateSpeak) -> None: """ await self._send_model(message) + async def send_update_think(self, message: AgentV1UpdateThink) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1UpdateThink. + """ + await self._send_model(message) + async def send_inject_user_message(self, message: AgentV1InjectUserMessage) -> None: """ Send a message to the websocket connection. @@ -145,12 +149,12 @@ async def send_function_call_response(self, message: AgentV1SendFunctionCallResp """ await self._send_model(message) - async def send_keep_alive(self, message: typing.Optional[AgentV1KeepAlive] = None) -> None: + async def send_keep_alive(self, message: AgentV1KeepAlive) -> None: """ Send a message to the websocket connection. The message will be sent as a AgentV1KeepAlive. """ - await self._send_model(message or AgentV1KeepAlive()) + await self._send_model(message) async def send_update_prompt(self, message: AgentV1UpdatePrompt) -> None: """ @@ -174,7 +178,11 @@ async def recv(self) -> V1SocketClientResponse: if isinstance(data, bytes): return data # type: ignore json_data = json.loads(data) - return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + try: + return parse_obj_as(V1SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.") + return json_data # type: ignore async def _send(self, data: typing.Any) -> None: """ @@ -188,7 +196,7 @@ async def _send_model(self, data: typing.Any) -> None: """ Send a Pydantic model to the websocket connection. """ - await self._send(_sanitize_numeric_types(data.dict())) + await self._send(data.dict()) class V1SocketClient(EventEmitterMixin): @@ -201,7 +209,13 @@ def __iter__(self): if isinstance(message, bytes): yield message else: - yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore + try: + yield parse_obj_as(V1SocketClientResponse, json.loads(message)) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue def start_listening(self): """ @@ -220,9 +234,15 @@ def start_listening(self): parsed = raw_message else: json_data = json.loads(raw_message) - parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + try: + parsed = parse_obj_as(V1SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue self._emit(EventType.MESSAGE, parsed) - except Exception as exc: + except (websockets.WebSocketException, JSONDecodeError) as exc: self._emit(EventType.ERROR, exc) finally: self._emit(EventType.CLOSE, None) @@ -241,6 +261,13 @@ def send_update_speak(self, message: AgentV1UpdateSpeak) -> None: """ self._send_model(message) + def send_update_think(self, message: AgentV1UpdateThink) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1UpdateThink. + """ + self._send_model(message) + def send_inject_user_message(self, message: AgentV1InjectUserMessage) -> None: """ Send a message to the websocket connection. @@ -262,12 +289,12 @@ def send_function_call_response(self, message: AgentV1SendFunctionCallResponse) """ self._send_model(message) - def send_keep_alive(self, message: typing.Optional[AgentV1KeepAlive] = None) -> None: + def send_keep_alive(self, message: AgentV1KeepAlive) -> None: """ Send a message to the websocket connection. The message will be sent as a AgentV1KeepAlive. """ - self._send_model(message or AgentV1KeepAlive()) + self._send_model(message) def send_update_prompt(self, message: AgentV1UpdatePrompt) -> None: """ @@ -291,7 +318,11 @@ def recv(self) -> V1SocketClientResponse: if isinstance(data, bytes): return data # type: ignore json_data = json.loads(data) - return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + try: + return parse_obj_as(V1SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.") + return json_data # type: ignore def _send(self, data: typing.Any) -> None: """ @@ -305,4 +336,4 @@ def _send_model(self, data: typing.Any) -> None: """ Send a Pydantic model to the websocket connection. """ - self._send(_sanitize_numeric_types(data.dict())) + self._send(data.dict()) diff --git a/src/deepgram/agent/v1/socket_client.py.bak b/src/deepgram/agent/v1/socket_client.py.bak new file mode 100644 index 00000000..e486dc67 --- /dev/null +++ b/src/deepgram/agent/v1/socket_client.py.bak @@ -0,0 +1,308 @@ +# This file was auto-generated by Fern from our API Definition. + +import json +import typing +from json.decoder import JSONDecodeError + +import websockets +import websockets.sync.connection as websockets_sync_connection +from ...core.events import EventEmitterMixin, EventType +from ...core.unchecked_base_model import construct_type +from .types.agent_v1agent_audio_done import AgentV1AgentAudioDone +from .types.agent_v1agent_started_speaking import AgentV1AgentStartedSpeaking +from .types.agent_v1agent_thinking import AgentV1AgentThinking +from .types.agent_v1conversation_text import AgentV1ConversationText +from .types.agent_v1error import AgentV1Error +from .types.agent_v1function_call_request import AgentV1FunctionCallRequest +from .types.agent_v1inject_agent_message import AgentV1InjectAgentMessage +from .types.agent_v1inject_user_message import AgentV1InjectUserMessage +from .types.agent_v1injection_refused import AgentV1InjectionRefused +from .types.agent_v1keep_alive import AgentV1KeepAlive +from .types.agent_v1prompt_updated import AgentV1PromptUpdated +from .types.agent_v1receive_function_call_response import AgentV1ReceiveFunctionCallResponse +from .types.agent_v1send_function_call_response import AgentV1SendFunctionCallResponse +from .types.agent_v1settings import AgentV1Settings +from .types.agent_v1settings_applied import AgentV1SettingsApplied +from .types.agent_v1speak_updated import AgentV1SpeakUpdated +from .types.agent_v1update_prompt import AgentV1UpdatePrompt +from .types.agent_v1update_speak import AgentV1UpdateSpeak +from .types.agent_v1user_started_speaking import AgentV1UserStartedSpeaking +from .types.agent_v1warning import AgentV1Warning +from .types.agent_v1welcome import AgentV1Welcome + +try: + from websockets.legacy.client import WebSocketClientProtocol # type: ignore +except ImportError: + from websockets import WebSocketClientProtocol # type: ignore + +def _sanitize_numeric_types(obj: typing.Any) -> typing.Any: + """ + Recursively convert float values that are whole numbers to int. + + Workaround for Fern-generated models that type integer API fields + (like sample_rate) as float, causing JSON serialization to produce + values like 44100.0 instead of 44100. The Deepgram API rejects + float representations of integer fields. + + See: https://github.com/deepgram/internal-api-specs/issues/205 + """ + if isinstance(obj, dict): + return {k: _sanitize_numeric_types(v) for k, v in obj.items()} + elif isinstance(obj, list): + return [_sanitize_numeric_types(item) for item in obj] + elif isinstance(obj, float) and obj.is_integer(): + return int(obj) + return obj + + +V1SocketClientResponse = typing.Union[ + AgentV1ReceiveFunctionCallResponse, + AgentV1PromptUpdated, + AgentV1SpeakUpdated, + AgentV1InjectionRefused, + AgentV1Welcome, + AgentV1SettingsApplied, + AgentV1ConversationText, + AgentV1UserStartedSpeaking, + AgentV1AgentThinking, + AgentV1FunctionCallRequest, + AgentV1AgentStartedSpeaking, + AgentV1AgentAudioDone, + AgentV1Error, + AgentV1Warning, + bytes, +] + + +class AsyncV1SocketClient(EventEmitterMixin): + def __init__(self, *, websocket: WebSocketClientProtocol): + super().__init__() + self._websocket = websocket + + async def __aiter__(self): + async for message in self._websocket: + if isinstance(message, bytes): + yield message + else: + yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore + + async def start_listening(self): + """ + Start listening for messages on the websocket connection. + + Emits events in the following order: + - EventType.OPEN when connection is established + - EventType.MESSAGE for each message received + - EventType.ERROR if an error occurs + - EventType.CLOSE when connection is closed + """ + await self._emit_async(EventType.OPEN, None) + try: + async for raw_message in self._websocket: + if isinstance(raw_message, bytes): + parsed = raw_message + else: + json_data = json.loads(raw_message) + parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + await self._emit_async(EventType.MESSAGE, parsed) + except Exception as exc: + await self._emit_async(EventType.ERROR, exc) + finally: + await self._emit_async(EventType.CLOSE, None) + + async def send_settings(self, message: AgentV1Settings) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1Settings. + """ + await self._send_model(message) + + async def send_update_speak(self, message: AgentV1UpdateSpeak) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1UpdateSpeak. + """ + await self._send_model(message) + + async def send_inject_user_message(self, message: AgentV1InjectUserMessage) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1InjectUserMessage. + """ + await self._send_model(message) + + async def send_inject_agent_message(self, message: AgentV1InjectAgentMessage) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1InjectAgentMessage. + """ + await self._send_model(message) + + async def send_function_call_response(self, message: AgentV1SendFunctionCallResponse) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1SendFunctionCallResponse. + """ + await self._send_model(message) + + async def send_keep_alive(self, message: typing.Optional[AgentV1KeepAlive] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1KeepAlive. + """ + await self._send_model(message or AgentV1KeepAlive()) + + async def send_update_prompt(self, message: AgentV1UpdatePrompt) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1UpdatePrompt. + """ + await self._send_model(message) + + async def send_media(self, message: bytes) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a bytes. + """ + await self._send(message) + + async def recv(self) -> V1SocketClientResponse: + """ + Receive a message from the websocket connection. + """ + data = await self._websocket.recv() + if isinstance(data, bytes): + return data # type: ignore + json_data = json.loads(data) + return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + + async def _send(self, data: typing.Any) -> None: + """ + Send a message to the websocket connection. + """ + if isinstance(data, dict): + data = json.dumps(data) + await self._websocket.send(data) + + async def _send_model(self, data: typing.Any) -> None: + """ + Send a Pydantic model to the websocket connection. + """ + await self._send(_sanitize_numeric_types(data.dict())) + + +class V1SocketClient(EventEmitterMixin): + def __init__(self, *, websocket: websockets_sync_connection.Connection): + super().__init__() + self._websocket = websocket + + def __iter__(self): + for message in self._websocket: + if isinstance(message, bytes): + yield message + else: + yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore + + def start_listening(self): + """ + Start listening for messages on the websocket connection. + + Emits events in the following order: + - EventType.OPEN when connection is established + - EventType.MESSAGE for each message received + - EventType.ERROR if an error occurs + - EventType.CLOSE when connection is closed + """ + self._emit(EventType.OPEN, None) + try: + for raw_message in self._websocket: + if isinstance(raw_message, bytes): + parsed = raw_message + else: + json_data = json.loads(raw_message) + parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + self._emit(EventType.MESSAGE, parsed) + except Exception as exc: + self._emit(EventType.ERROR, exc) + finally: + self._emit(EventType.CLOSE, None) + + def send_settings(self, message: AgentV1Settings) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1Settings. + """ + self._send_model(message) + + def send_update_speak(self, message: AgentV1UpdateSpeak) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1UpdateSpeak. + """ + self._send_model(message) + + def send_inject_user_message(self, message: AgentV1InjectUserMessage) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1InjectUserMessage. + """ + self._send_model(message) + + def send_inject_agent_message(self, message: AgentV1InjectAgentMessage) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1InjectAgentMessage. + """ + self._send_model(message) + + def send_function_call_response(self, message: AgentV1SendFunctionCallResponse) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1SendFunctionCallResponse. + """ + self._send_model(message) + + def send_keep_alive(self, message: typing.Optional[AgentV1KeepAlive] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1KeepAlive. + """ + self._send_model(message or AgentV1KeepAlive()) + + def send_update_prompt(self, message: AgentV1UpdatePrompt) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a AgentV1UpdatePrompt. + """ + self._send_model(message) + + def send_media(self, message: bytes) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a bytes. + """ + self._send(message) + + def recv(self) -> V1SocketClientResponse: + """ + Receive a message from the websocket connection. + """ + data = self._websocket.recv() + if isinstance(data, bytes): + return data # type: ignore + json_data = json.loads(data) + return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + + def _send(self, data: typing.Any) -> None: + """ + Send a message to the websocket connection. + """ + if isinstance(data, dict): + data = json.dumps(data) + self._websocket.send(data) + + def _send_model(self, data: typing.Any) -> None: + """ + Send a Pydantic model to the websocket connection. + """ + self._send(_sanitize_numeric_types(data.dict())) diff --git a/src/deepgram/agent/v1/types/__init__.py b/src/deepgram/agent/v1/types/__init__.py index 6cc1e513..89a1ee1d 100644 --- a/src/deepgram/agent/v1/types/__init__.py +++ b/src/deepgram/agent/v1/types/__init__.py @@ -53,8 +53,12 @@ from .agent_v1settings_audio_output_encoding import AgentV1SettingsAudioOutputEncoding from .agent_v1settings_flags import AgentV1SettingsFlags from .agent_v1speak_updated import AgentV1SpeakUpdated + from .agent_v1think_updated import AgentV1ThinkUpdated from .agent_v1update_prompt import AgentV1UpdatePrompt from .agent_v1update_speak import AgentV1UpdateSpeak + from .agent_v1update_speak_speak import AgentV1UpdateSpeakSpeak + from .agent_v1update_think import AgentV1UpdateThink + from .agent_v1update_think_think import AgentV1UpdateThinkThink from .agent_v1user_started_speaking import AgentV1UserStartedSpeaking from .agent_v1warning import AgentV1Warning from .agent_v1welcome import AgentV1Welcome @@ -98,8 +102,12 @@ "AgentV1SettingsAudioOutputEncoding": ".agent_v1settings_audio_output_encoding", "AgentV1SettingsFlags": ".agent_v1settings_flags", "AgentV1SpeakUpdated": ".agent_v1speak_updated", + "AgentV1ThinkUpdated": ".agent_v1think_updated", "AgentV1UpdatePrompt": ".agent_v1update_prompt", "AgentV1UpdateSpeak": ".agent_v1update_speak", + "AgentV1UpdateSpeakSpeak": ".agent_v1update_speak_speak", + "AgentV1UpdateThink": ".agent_v1update_think", + "AgentV1UpdateThinkThink": ".agent_v1update_think_think", "AgentV1UserStartedSpeaking": ".agent_v1user_started_speaking", "AgentV1Warning": ".agent_v1warning", "AgentV1Welcome": ".agent_v1welcome", @@ -167,8 +175,12 @@ def __dir__(): "AgentV1SettingsAudioOutputEncoding", "AgentV1SettingsFlags", "AgentV1SpeakUpdated", + "AgentV1ThinkUpdated", "AgentV1UpdatePrompt", "AgentV1UpdateSpeak", + "AgentV1UpdateSpeakSpeak", + "AgentV1UpdateThink", + "AgentV1UpdateThinkThink", "AgentV1UserStartedSpeaking", "AgentV1Warning", "AgentV1Welcome", diff --git a/src/deepgram/agent/v1/types/agent_v1think_updated.py b/src/deepgram/agent/v1/types/agent_v1think_updated.py new file mode 100644 index 00000000..76274f06 --- /dev/null +++ b/src/deepgram/agent/v1/types/agent_v1think_updated.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class AgentV1ThinkUpdated(UncheckedBaseModel): + type: typing.Literal["ThinkUpdated"] = pydantic.Field(default="ThinkUpdated") + """ + Message type identifier for think update confirmation + """ + + 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/deepgram/agent/v1/types/agent_v1update_speak.py b/src/deepgram/agent/v1/types/agent_v1update_speak.py index 4afc8d37..4b045d69 100644 --- a/src/deepgram/agent/v1/types/agent_v1update_speak.py +++ b/src/deepgram/agent/v1/types/agent_v1update_speak.py @@ -5,7 +5,7 @@ import pydantic from ....core.pydantic_utilities import IS_PYDANTIC_V2 from ....core.unchecked_base_model import UncheckedBaseModel -from ....types.speak_settings_v1 import SpeakSettingsV1 +from .agent_v1update_speak_speak import AgentV1UpdateSpeakSpeak class AgentV1UpdateSpeak(UncheckedBaseModel): @@ -14,7 +14,7 @@ class AgentV1UpdateSpeak(UncheckedBaseModel): Message type identifier for updating the speak model """ - speak: SpeakSettingsV1 + speak: AgentV1UpdateSpeakSpeak if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/deepgram/agent/v1/types/agent_v1update_speak_speak.py b/src/deepgram/agent/v1/types/agent_v1update_speak_speak.py new file mode 100644 index 00000000..eb34f060 --- /dev/null +++ b/src/deepgram/agent/v1/types/agent_v1update_speak_speak.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....types.speak_settings_v1 import SpeakSettingsV1 + +AgentV1UpdateSpeakSpeak = typing.Union[SpeakSettingsV1, typing.List[SpeakSettingsV1]] diff --git a/src/deepgram/agent/v1/types/agent_v1update_think.py b/src/deepgram/agent/v1/types/agent_v1update_think.py new file mode 100644 index 00000000..4236c4b7 --- /dev/null +++ b/src/deepgram/agent/v1/types/agent_v1update_think.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .agent_v1update_think_think import AgentV1UpdateThinkThink + + +class AgentV1UpdateThink(UncheckedBaseModel): + type: typing.Literal["UpdateThink"] = pydantic.Field(default="UpdateThink") + """ + Message type identifier for updating the think model + """ + + think: AgentV1UpdateThinkThink + + 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/deepgram/agent/v1/types/agent_v1update_think_think.py b/src/deepgram/agent/v1/types/agent_v1update_think_think.py new file mode 100644 index 00000000..93e087b5 --- /dev/null +++ b/src/deepgram/agent/v1/types/agent_v1update_think_think.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....types.think_settings_v1 import ThinkSettingsV1 + +AgentV1UpdateThinkThink = typing.Union[ThinkSettingsV1, typing.List[ThinkSettingsV1]] diff --git a/src/deepgram/base_client.py b/src/deepgram/base_client.py index 34261bc6..7ee628e3 100644 --- a/src/deepgram/base_client.py +++ b/src/deepgram/base_client.py @@ -8,6 +8,7 @@ import httpx from .core.api_error import ApiError from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .core.logging import LogConfig, Logger from .environment import DeepgramClientEnvironment if typing.TYPE_CHECKING: @@ -48,6 +49,9 @@ class BaseClient: httpx_client : typing.Optional[httpx.Client] The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + logging : typing.Optional[typing.Union[LogConfig, Logger]] + Configure logging for the SDK. Accepts a LogConfig dict with 'level' (debug/info/warn/error), 'logger' (custom logger implementation), and 'silent' (boolean, defaults to True) fields. You can also pass a pre-configured Logger instance. + Examples -------- from deepgram import DeepgramClient @@ -66,6 +70,7 @@ def __init__( timeout: typing.Optional[float] = None, follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.Client] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): _defaulted_timeout = ( timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read @@ -84,6 +89,7 @@ def __init__( if follow_redirects is not None else httpx.Client(timeout=_defaulted_timeout), timeout=_defaulted_timeout, + logging=logging, ) self._agent: typing.Optional[AgentClient] = None self._auth: typing.Optional[AuthClient] = None @@ -178,6 +184,9 @@ class AsyncBaseClient: httpx_client : typing.Optional[httpx.AsyncClient] The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + logging : typing.Optional[typing.Union[LogConfig, Logger]] + Configure logging for the SDK. Accepts a LogConfig dict with 'level' (debug/info/warn/error), 'logger' (custom logger implementation), and 'silent' (boolean, defaults to True) fields. You can also pass a pre-configured Logger instance. + Examples -------- from deepgram import AsyncDeepgramClient @@ -196,6 +205,7 @@ def __init__( timeout: typing.Optional[float] = None, follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.AsyncClient] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): _defaulted_timeout = ( timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read @@ -214,6 +224,7 @@ def __init__( if follow_redirects is not None else httpx.AsyncClient(timeout=_defaulted_timeout), timeout=_defaulted_timeout, + logging=logging, ) self._agent: typing.Optional[AsyncAgentClient] = None self._auth: typing.Optional[AsyncAuthClient] = None diff --git a/src/deepgram/core/__init__.py b/src/deepgram/core/__init__.py index 8547a17c..d247a71c 100644 --- a/src/deepgram/core/__init__.py +++ b/src/deepgram/core/__init__.py @@ -8,12 +8,13 @@ if typing.TYPE_CHECKING: from .api_error import ApiError from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper - from .datetime_utils import serialize_datetime + from .datetime_utils import Rfc2822DateTime, parse_rfc2822_datetime, serialize_datetime from .events import EventEmitterMixin, EventType from .file import File, convert_file_dict_to_httpx_tuples, with_content_type from .http_client import AsyncHttpClient, HttpClient from .http_response import AsyncHttpResponse, HttpResponse from .jsonable_encoder import jsonable_encoder + from .logging import ConsoleLogger, ILogger, LogConfig, LogLevel, Logger, create_logger from .pydantic_utilities import ( IS_PYDANTIC_V2, UniversalBaseModel, @@ -35,15 +36,21 @@ "AsyncHttpClient": ".http_client", "AsyncHttpResponse": ".http_response", "BaseClientWrapper": ".client_wrapper", + "ConsoleLogger": ".logging", "EventEmitterMixin": ".events", "EventType": ".events", "FieldMetadata": ".serialization", "File": ".file", "HttpClient": ".http_client", "HttpResponse": ".http_response", + "ILogger": ".logging", "IS_PYDANTIC_V2": ".pydantic_utilities", "InvalidWebSocketStatus": ".websocket_compat", + "LogConfig": ".logging", + "LogLevel": ".logging", + "Logger": ".logging", "RequestOptions": ".request_options", + "Rfc2822DateTime": ".datetime_utils", "SyncClientWrapper": ".client_wrapper", "UncheckedBaseModel": ".unchecked_base_model", "UnionMetadata": ".unchecked_base_model", @@ -52,10 +59,12 @@ "construct_type": ".unchecked_base_model", "convert_and_respect_annotation_metadata": ".serialization", "convert_file_dict_to_httpx_tuples": ".file", + "create_logger": ".logging", "encode_query": ".query_encoder", "get_status_code": ".websocket_compat", "jsonable_encoder": ".jsonable_encoder", "parse_obj_as": ".pydantic_utilities", + "parse_rfc2822_datetime": ".datetime_utils", "remove_none_from_dict": ".remove_none_from_dict", "serialize_datetime": ".datetime_utils", "universal_field_validator": ".pydantic_utilities", @@ -92,15 +101,21 @@ def __dir__(): "AsyncHttpClient", "AsyncHttpResponse", "BaseClientWrapper", + "ConsoleLogger", "EventEmitterMixin", "EventType", "FieldMetadata", "File", "HttpClient", "HttpResponse", + "ILogger", "IS_PYDANTIC_V2", "InvalidWebSocketStatus", + "LogConfig", + "LogLevel", + "Logger", "RequestOptions", + "Rfc2822DateTime", "SyncClientWrapper", "UncheckedBaseModel", "UnionMetadata", @@ -109,10 +124,12 @@ def __dir__(): "construct_type", "convert_and_respect_annotation_metadata", "convert_file_dict_to_httpx_tuples", + "create_logger", "encode_query", "get_status_code", "jsonable_encoder", "parse_obj_as", + "parse_rfc2822_datetime", "remove_none_from_dict", "serialize_datetime", "universal_field_validator", diff --git a/src/deepgram/core/client_wrapper.py b/src/deepgram/core/client_wrapper.py index 33b53ce1..8da59cc6 100644 --- a/src/deepgram/core/client_wrapper.py +++ b/src/deepgram/core/client_wrapper.py @@ -5,6 +5,7 @@ import httpx from ..environment import DeepgramClientEnvironment from .http_client import AsyncHttpClient, HttpClient +from .logging import LogConfig, Logger class BaseClientWrapper: @@ -15,22 +16,24 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, environment: DeepgramClientEnvironment, timeout: typing.Optional[float] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): self.api_key = api_key self._headers = headers self._environment = environment self._timeout = timeout + self._logging = logging def get_headers(self) -> typing.Dict[str, str]: import platform headers: typing.Dict[str, str] = { - "User-Agent": "deepgram-sdk/6.0.1", + "User-Agent": "deepgram-sdk/6.1.0", "X-Fern-Language": "Python", "X-Fern-Runtime": f"python/{platform.python_version()}", "X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}", "X-Fern-SDK-Name": "deepgram-sdk", - "X-Fern-SDK-Version": "6.0.1", + "X-Fern-SDK-Version": "6.1.0", **(self.get_custom_headers() or {}), } headers["Authorization"] = f"Token {self.api_key}" @@ -54,11 +57,15 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, environment: DeepgramClientEnvironment, timeout: typing.Optional[float] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, httpx_client: httpx.Client, ): - super().__init__(api_key=api_key, headers=headers, environment=environment, timeout=timeout) + super().__init__(api_key=api_key, headers=headers, environment=environment, timeout=timeout, logging=logging) self.httpx_client = HttpClient( - httpx_client=httpx_client, base_headers=self.get_headers, base_timeout=self.get_timeout + httpx_client=httpx_client, + base_headers=self.get_headers, + base_timeout=self.get_timeout, + logging_config=self._logging, ) @@ -70,16 +77,18 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, environment: DeepgramClientEnvironment, timeout: typing.Optional[float] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, async_token: typing.Optional[typing.Callable[[], typing.Awaitable[str]]] = None, httpx_client: httpx.AsyncClient, ): - super().__init__(api_key=api_key, headers=headers, environment=environment, timeout=timeout) + super().__init__(api_key=api_key, headers=headers, environment=environment, timeout=timeout, logging=logging) self._async_token = async_token self.httpx_client = AsyncHttpClient( httpx_client=httpx_client, base_headers=self.get_headers, base_timeout=self.get_timeout, async_base_headers=self.async_get_headers, + logging_config=self._logging, ) async def async_get_headers(self) -> typing.Dict[str, str]: diff --git a/src/deepgram/core/datetime_utils.py b/src/deepgram/core/datetime_utils.py index 7c9864a9..a12b2ad0 100644 --- a/src/deepgram/core/datetime_utils.py +++ b/src/deepgram/core/datetime_utils.py @@ -1,6 +1,48 @@ # This file was auto-generated by Fern from our API Definition. import datetime as dt +from email.utils import parsedate_to_datetime +from typing import Any + +import pydantic + +IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") + + +def parse_rfc2822_datetime(v: Any) -> dt.datetime: + """ + Parse an RFC 2822 datetime string (e.g., "Wed, 02 Oct 2002 13:00:00 GMT") + into a datetime object. If the value is already a datetime, return it as-is. + Falls back to ISO 8601 parsing if RFC 2822 parsing fails. + """ + if isinstance(v, dt.datetime): + return v + if isinstance(v, str): + try: + return parsedate_to_datetime(v) + except Exception: + pass + # Fallback to ISO 8601 parsing + return dt.datetime.fromisoformat(v.replace("Z", "+00:00")) + raise ValueError(f"Expected str or datetime, got {type(v)}") + + +class Rfc2822DateTime(dt.datetime): + """A datetime subclass that parses RFC 2822 date strings. + + On Pydantic V1, uses __get_validators__ for pre-validation. + On Pydantic V2, uses __get_pydantic_core_schema__ for BeforeValidator-style parsing. + """ + + @classmethod + def __get_validators__(cls): # type: ignore[no-untyped-def] + yield parse_rfc2822_datetime + + @classmethod + def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> Any: # type: ignore[override] + from pydantic_core import core_schema + + return core_schema.no_info_before_validator_function(parse_rfc2822_datetime, core_schema.datetime_schema()) def serialize_datetime(v: dt.datetime) -> str: diff --git a/src/deepgram/core/http_client.py b/src/deepgram/core/http_client.py index 7c6c936f..ee937589 100644 --- a/src/deepgram/core/http_client.py +++ b/src/deepgram/core/http_client.py @@ -12,6 +12,7 @@ from .file import File, convert_file_dict_to_httpx_tuples from .force_multipart import FORCE_MULTIPART from .jsonable_encoder import jsonable_encoder +from .logging import LogConfig, Logger, create_logger from .query_encoder import encode_query from .remove_none_from_dict import remove_none_from_dict as remove_none_from_dict from .request_options import RequestOptions @@ -122,6 +123,32 @@ def _should_retry(response: httpx.Response) -> bool: return response.status_code >= 500 or response.status_code in retryable_400s +_SENSITIVE_HEADERS = frozenset( + { + "authorization", + "www-authenticate", + "x-api-key", + "api-key", + "apikey", + "x-api-token", + "x-auth-token", + "auth-token", + "cookie", + "set-cookie", + "proxy-authorization", + "proxy-authenticate", + "x-csrf-token", + "x-xsrf-token", + "x-session-token", + "x-access-token", + } +) + + +def _redact_headers(headers: typing.Dict[str, str]) -> typing.Dict[str, str]: + return {k: ("[REDACTED]" if k.lower() in _SENSITIVE_HEADERS else v) for k, v in headers.items()} + + def _build_url(base_url: str, path: typing.Optional[str]) -> str: """ Build a full URL by joining a base URL with a path. @@ -238,11 +265,13 @@ def __init__( base_timeout: typing.Callable[[], typing.Optional[float]], base_headers: typing.Callable[[], typing.Dict[str, str]], base_url: typing.Optional[typing.Callable[[], str]] = None, + logging_config: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): self.base_url = base_url self.base_timeout = base_timeout self.base_headers = base_headers self.httpx_client = httpx_client + self.logger = create_logger(logging_config) def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: base_url = maybe_base_url @@ -315,18 +344,30 @@ def request( ) ) + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + has_body=json_body is not None or data_body is not None, + ) + response = self.httpx_client.request( method=method, - url=_build_url(base_url, path), - headers=jsonable_encoder( - remove_none_from_dict( - { - **self.base_headers(), - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), - } - ) - ), + url=_request_url, + headers=_request_headers, params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, @@ -353,6 +394,24 @@ def request( omit=omit, ) + if self.logger.is_debug(): + if 200 <= response.status_code < 400: + self.logger.debug( + "HTTP request succeeded", + method=method, + url=_request_url, + status_code=response.status_code, + ) + + if self.logger.is_error(): + if response.status_code >= 400: + self.logger.error( + "HTTP request failed with error status", + method=method, + url=_request_url, + status_code=response.status_code, + ) + return response @contextmanager @@ -418,18 +477,29 @@ def stream( ) ) + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making streaming HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + ) + with self.httpx_client.stream( method=method, - url=_build_url(base_url, path), - headers=jsonable_encoder( - remove_none_from_dict( - { - **self.base_headers(), - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) if request_options is not None else {}), - } - ) - ), + url=_request_url, + headers=_request_headers, params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, @@ -449,12 +519,14 @@ def __init__( base_headers: typing.Callable[[], typing.Dict[str, str]], base_url: typing.Optional[typing.Callable[[], str]] = None, async_base_headers: typing.Optional[typing.Callable[[], typing.Awaitable[typing.Dict[str, str]]]] = None, + logging_config: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): self.base_url = base_url self.base_timeout = base_timeout self.base_headers = base_headers self.async_base_headers = async_base_headers self.httpx_client = httpx_client + self.logger = create_logger(logging_config) async def _get_headers(self) -> typing.Dict[str, str]: if self.async_base_headers is not None: @@ -535,19 +607,30 @@ async def request( ) ) - # Add the input to each of these and do None-safety checks + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + has_body=json_body is not None or data_body is not None, + ) + response = await self.httpx_client.request( method=method, - url=_build_url(base_url, path), - headers=jsonable_encoder( - remove_none_from_dict( - { - **_headers, - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), - } - ) - ), + url=_request_url, + headers=_request_headers, params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, @@ -573,6 +656,25 @@ async def request( retries=retries + 1, omit=omit, ) + + if self.logger.is_debug(): + if 200 <= response.status_code < 400: + self.logger.debug( + "HTTP request succeeded", + method=method, + url=_request_url, + status_code=response.status_code, + ) + + if self.logger.is_error(): + if response.status_code >= 400: + self.logger.error( + "HTTP request failed with error status", + method=method, + url=_request_url, + status_code=response.status_code, + ) + return response @asynccontextmanager @@ -641,18 +743,29 @@ async def stream( ) ) + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making streaming HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + ) + async with self.httpx_client.stream( method=method, - url=_build_url(base_url, path), - headers=jsonable_encoder( - remove_none_from_dict( - { - **_headers, - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) if request_options is not None else {}), - } - ) - ), + url=_request_url, + headers=_request_headers, params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, diff --git a/src/deepgram/core/logging.py b/src/deepgram/core/logging.py new file mode 100644 index 00000000..e5e57245 --- /dev/null +++ b/src/deepgram/core/logging.py @@ -0,0 +1,107 @@ +# This file was auto-generated by Fern from our API Definition. + +import logging +import typing + +LogLevel = typing.Literal["debug", "info", "warn", "error"] + +_LOG_LEVEL_MAP: typing.Dict[LogLevel, int] = { + "debug": 1, + "info": 2, + "warn": 3, + "error": 4, +} + + +class ILogger(typing.Protocol): + def debug(self, message: str, **kwargs: typing.Any) -> None: ... + def info(self, message: str, **kwargs: typing.Any) -> None: ... + def warn(self, message: str, **kwargs: typing.Any) -> None: ... + def error(self, message: str, **kwargs: typing.Any) -> None: ... + + +class ConsoleLogger: + _logger: logging.Logger + + def __init__(self) -> None: + self._logger = logging.getLogger("fern") + if not self._logger.handlers: + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter("%(levelname)s - %(message)s")) + self._logger.addHandler(handler) + self._logger.setLevel(logging.DEBUG) + + def debug(self, message: str, **kwargs: typing.Any) -> None: + self._logger.debug(message, extra=kwargs) + + def info(self, message: str, **kwargs: typing.Any) -> None: + self._logger.info(message, extra=kwargs) + + def warn(self, message: str, **kwargs: typing.Any) -> None: + self._logger.warning(message, extra=kwargs) + + def error(self, message: str, **kwargs: typing.Any) -> None: + self._logger.error(message, extra=kwargs) + + +class LogConfig(typing.TypedDict, total=False): + level: LogLevel + logger: ILogger + silent: bool + + +class Logger: + _level: int + _logger: ILogger + _silent: bool + + def __init__(self, *, level: LogLevel, logger: ILogger, silent: bool) -> None: + self._level = _LOG_LEVEL_MAP[level] + self._logger = logger + self._silent = silent + + def _should_log(self, level: LogLevel) -> bool: + return not self._silent and self._level <= _LOG_LEVEL_MAP[level] + + def is_debug(self) -> bool: + return self._should_log("debug") + + def is_info(self) -> bool: + return self._should_log("info") + + def is_warn(self) -> bool: + return self._should_log("warn") + + def is_error(self) -> bool: + return self._should_log("error") + + def debug(self, message: str, **kwargs: typing.Any) -> None: + if self.is_debug(): + self._logger.debug(message, **kwargs) + + def info(self, message: str, **kwargs: typing.Any) -> None: + if self.is_info(): + self._logger.info(message, **kwargs) + + def warn(self, message: str, **kwargs: typing.Any) -> None: + if self.is_warn(): + self._logger.warn(message, **kwargs) + + def error(self, message: str, **kwargs: typing.Any) -> None: + if self.is_error(): + self._logger.error(message, **kwargs) + + +_default_logger: Logger = Logger(level="info", logger=ConsoleLogger(), silent=True) + + +def create_logger(config: typing.Optional[typing.Union[LogConfig, Logger]] = None) -> Logger: + if config is None: + return _default_logger + if isinstance(config, Logger): + return config + return Logger( + level=config.get("level", "info"), + logger=config.get("logger", ConsoleLogger()), + silent=config.get("silent", True), + ) diff --git a/src/deepgram/environment.py b/src/deepgram/environment.py index 240d7ca4..e514ef69 100644 --- a/src/deepgram/environment.py +++ b/src/deepgram/environment.py @@ -7,15 +7,15 @@ class DeepgramClientEnvironment: PRODUCTION: DeepgramClientEnvironment AGENT: DeepgramClientEnvironment - def __init__(self, *, base: str, production: str, agent: str): + def __init__(self, *, base: str, agent: str, production: str): self.base = base - self.production = production self.agent = agent + self.production = production DeepgramClientEnvironment.PRODUCTION = DeepgramClientEnvironment( - base="https://api.deepgram.com", production="wss://api.deepgram.com", agent="wss://agent.deepgram.com" + base="https://api.deepgram.com", agent="wss://agent.deepgram.com", production="wss://api.deepgram.com" ) DeepgramClientEnvironment.AGENT = DeepgramClientEnvironment( - base="https://agent.deepgram.com", production="wss://api.deepgram.com", agent="wss://agent.deepgram.com" + base="https://agent.deepgram.com", agent="wss://agent.deepgram.com", production="wss://api.deepgram.com" ) diff --git a/src/deepgram/listen/__init__.py b/src/deepgram/listen/__init__.py index 42a8267f..6a178489 100644 --- a/src/deepgram/listen/__init__.py +++ b/src/deepgram/listen/__init__.py @@ -42,6 +42,16 @@ ListenV2CloseStream, ListenV2CloseStreamParams, ListenV2CloseStreamType, + ListenV2Configure, + ListenV2ConfigureFailure, + ListenV2ConfigureFailureParams, + ListenV2ConfigureParams, + ListenV2ConfigureSuccess, + ListenV2ConfigureSuccessParams, + ListenV2ConfigureSuccessThresholds, + ListenV2ConfigureSuccessThresholdsParams, + ListenV2ConfigureThresholds, + ListenV2ConfigureThresholdsParams, ListenV2Connected, ListenV2ConnectedParams, ListenV2FatalError, @@ -85,6 +95,16 @@ "ListenV2CloseStream": ".v2", "ListenV2CloseStreamParams": ".v2", "ListenV2CloseStreamType": ".v2", + "ListenV2Configure": ".v2", + "ListenV2ConfigureFailure": ".v2", + "ListenV2ConfigureFailureParams": ".v2", + "ListenV2ConfigureParams": ".v2", + "ListenV2ConfigureSuccess": ".v2", + "ListenV2ConfigureSuccessParams": ".v2", + "ListenV2ConfigureSuccessThresholds": ".v2", + "ListenV2ConfigureSuccessThresholdsParams": ".v2", + "ListenV2ConfigureThresholds": ".v2", + "ListenV2ConfigureThresholdsParams": ".v2", "ListenV2Connected": ".v2", "ListenV2ConnectedParams": ".v2", "ListenV2FatalError": ".v2", @@ -153,6 +173,16 @@ def __dir__(): "ListenV2CloseStream", "ListenV2CloseStreamParams", "ListenV2CloseStreamType", + "ListenV2Configure", + "ListenV2ConfigureFailure", + "ListenV2ConfigureFailureParams", + "ListenV2ConfigureParams", + "ListenV2ConfigureSuccess", + "ListenV2ConfigureSuccessParams", + "ListenV2ConfigureSuccessThresholds", + "ListenV2ConfigureSuccessThresholdsParams", + "ListenV2ConfigureThresholds", + "ListenV2ConfigureThresholdsParams", "ListenV2Connected", "ListenV2ConnectedParams", "ListenV2FatalError", diff --git a/src/deepgram/listen/v1/client.py b/src/deepgram/listen/v1/client.py index 7dd8baf3..74cb6f03 100644 --- a/src/deepgram/listen/v1/client.py +++ b/src/deepgram/listen/v1/client.py @@ -14,6 +14,34 @@ from ...core.remove_none_from_dict import remove_none_from_dict from ...core.request_options import RequestOptions from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code +from ...types.listen_v1callback import ListenV1Callback +from ...types.listen_v1callback_method import ListenV1CallbackMethod +from ...types.listen_v1channels import ListenV1Channels +from ...types.listen_v1detect_entities import ListenV1DetectEntities +from ...types.listen_v1diarize import ListenV1Diarize +from ...types.listen_v1dictation import ListenV1Dictation +from ...types.listen_v1encoding import ListenV1Encoding +from ...types.listen_v1endpointing import ListenV1Endpointing +from ...types.listen_v1extra import ListenV1Extra +from ...types.listen_v1interim_results import ListenV1InterimResults +from ...types.listen_v1keyterm import ListenV1Keyterm +from ...types.listen_v1keywords import ListenV1Keywords +from ...types.listen_v1language import ListenV1Language +from ...types.listen_v1mip_opt_out import ListenV1MipOptOut +from ...types.listen_v1model import ListenV1Model +from ...types.listen_v1multichannel import ListenV1Multichannel +from ...types.listen_v1numerals import ListenV1Numerals +from ...types.listen_v1profanity_filter import ListenV1ProfanityFilter +from ...types.listen_v1punctuate import ListenV1Punctuate +from ...types.listen_v1redact import ListenV1Redact +from ...types.listen_v1replace import ListenV1Replace +from ...types.listen_v1sample_rate import ListenV1SampleRate +from ...types.listen_v1search import ListenV1Search +from ...types.listen_v1smart_format import ListenV1SmartFormat +from ...types.listen_v1tag import ListenV1Tag +from ...types.listen_v1utterance_end_ms import ListenV1UtteranceEndMs +from ...types.listen_v1vad_events import ListenV1VadEvents +from ...types.listen_v1version import ListenV1Version from .raw_client import AsyncRawV1Client, RawV1Client from .socket_client import AsyncV1SocketClient, V1SocketClient @@ -47,34 +75,34 @@ def with_raw_response(self) -> RawV1Client: def connect( self, *, - callback: typing.Optional[str] = None, - callback_method: typing.Optional[str] = None, - channels: typing.Optional[str] = None, - detect_entities: typing.Optional[str] = None, - diarize: typing.Optional[str] = None, - dictation: typing.Optional[str] = None, - encoding: typing.Optional[str] = None, - endpointing: typing.Optional[str] = None, - extra: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - interim_results: typing.Optional[str] = None, - keyterm: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - keywords: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - language: typing.Optional[str] = None, - mip_opt_out: typing.Optional[str] = None, - model: str, - multichannel: typing.Optional[str] = None, - numerals: typing.Optional[str] = None, - profanity_filter: typing.Optional[str] = None, - punctuate: typing.Optional[str] = None, - redact: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - replace: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - sample_rate: typing.Optional[str] = None, - search: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - smart_format: typing.Optional[str] = None, - tag: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - utterance_end_ms: typing.Optional[str] = None, - vad_events: typing.Optional[str] = None, - version: typing.Optional[str] = None, + callback: typing.Optional[ListenV1Callback] = None, + callback_method: typing.Optional[ListenV1CallbackMethod] = None, + channels: typing.Optional[ListenV1Channels] = None, + detect_entities: typing.Optional[ListenV1DetectEntities] = None, + diarize: typing.Optional[ListenV1Diarize] = None, + dictation: typing.Optional[ListenV1Dictation] = None, + encoding: typing.Optional[ListenV1Encoding] = None, + endpointing: typing.Optional[ListenV1Endpointing] = None, + extra: typing.Optional[ListenV1Extra] = None, + interim_results: typing.Optional[ListenV1InterimResults] = None, + keyterm: typing.Optional[ListenV1Keyterm] = None, + keywords: typing.Optional[ListenV1Keywords] = None, + language: typing.Optional[ListenV1Language] = None, + mip_opt_out: typing.Optional[ListenV1MipOptOut] = None, + model: ListenV1Model, + multichannel: typing.Optional[ListenV1Multichannel] = None, + numerals: typing.Optional[ListenV1Numerals] = None, + profanity_filter: typing.Optional[ListenV1ProfanityFilter] = None, + punctuate: typing.Optional[ListenV1Punctuate] = None, + redact: typing.Optional[ListenV1Redact] = None, + replace: typing.Optional[ListenV1Replace] = None, + sample_rate: typing.Optional[ListenV1SampleRate] = None, + search: typing.Optional[ListenV1Search] = None, + smart_format: typing.Optional[ListenV1SmartFormat] = None, + tag: typing.Optional[ListenV1Tag] = None, + utterance_end_ms: typing.Optional[ListenV1UtteranceEndMs] = None, + vad_events: typing.Optional[ListenV1VadEvents] = None, + version: typing.Optional[ListenV1Version] = None, authorization: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> typing.Iterator[V1SocketClient]: @@ -83,62 +111,62 @@ def connect( Parameters ---------- - callback : typing.Optional[str] + callback : typing.Optional[ListenV1Callback] - callback_method : typing.Optional[str] + callback_method : typing.Optional[ListenV1CallbackMethod] - channels : typing.Optional[str] + channels : typing.Optional[ListenV1Channels] - detect_entities : typing.Optional[str] + detect_entities : typing.Optional[ListenV1DetectEntities] - diarize : typing.Optional[str] + diarize : typing.Optional[ListenV1Diarize] - dictation : typing.Optional[str] + dictation : typing.Optional[ListenV1Dictation] - encoding : typing.Optional[str] + encoding : typing.Optional[ListenV1Encoding] - endpointing : typing.Optional[str] + endpointing : typing.Optional[ListenV1Endpointing] - extra : typing.Optional[typing.Union[str, typing.Sequence[str]]] + extra : typing.Optional[ListenV1Extra] - interim_results : typing.Optional[str] + interim_results : typing.Optional[ListenV1InterimResults] - keyterm : typing.Optional[typing.Union[str, typing.Sequence[str]]] + keyterm : typing.Optional[ListenV1Keyterm] - keywords : typing.Optional[typing.Union[str, typing.Sequence[str]]] + keywords : typing.Optional[ListenV1Keywords] - language : typing.Optional[str] + language : typing.Optional[ListenV1Language] - mip_opt_out : typing.Optional[str] + mip_opt_out : typing.Optional[ListenV1MipOptOut] - model : str + model : ListenV1Model AI model to use for the transcription - multichannel : typing.Optional[str] + multichannel : typing.Optional[ListenV1Multichannel] - numerals : typing.Optional[str] + numerals : typing.Optional[ListenV1Numerals] - profanity_filter : typing.Optional[str] + profanity_filter : typing.Optional[ListenV1ProfanityFilter] - punctuate : typing.Optional[str] + punctuate : typing.Optional[ListenV1Punctuate] - redact : typing.Optional[typing.Union[str, typing.Sequence[str]]] + redact : typing.Optional[ListenV1Redact] - replace : typing.Optional[typing.Union[str, typing.Sequence[str]]] + replace : typing.Optional[ListenV1Replace] - sample_rate : typing.Optional[str] + sample_rate : typing.Optional[ListenV1SampleRate] - search : typing.Optional[typing.Union[str, typing.Sequence[str]]] + search : typing.Optional[ListenV1Search] - smart_format : typing.Optional[str] + smart_format : typing.Optional[ListenV1SmartFormat] - tag : typing.Optional[typing.Union[str, typing.Sequence[str]]] + tag : typing.Optional[ListenV1Tag] - utterance_end_ms : typing.Optional[str] + utterance_end_ms : typing.Optional[ListenV1UtteranceEndMs] - vad_events : typing.Optional[str] + vad_events : typing.Optional[ListenV1VadEvents] - version : typing.Optional[str] + version : typing.Optional[ListenV1Version] authorization : typing.Optional[str] Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. @@ -248,34 +276,34 @@ def with_raw_response(self) -> AsyncRawV1Client: async def connect( self, *, - callback: typing.Optional[str] = None, - callback_method: typing.Optional[str] = None, - channels: typing.Optional[str] = None, - detect_entities: typing.Optional[str] = None, - diarize: typing.Optional[str] = None, - dictation: typing.Optional[str] = None, - encoding: typing.Optional[str] = None, - endpointing: typing.Optional[str] = None, - extra: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - interim_results: typing.Optional[str] = None, - keyterm: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - keywords: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - language: typing.Optional[str] = None, - mip_opt_out: typing.Optional[str] = None, - model: str, - multichannel: typing.Optional[str] = None, - numerals: typing.Optional[str] = None, - profanity_filter: typing.Optional[str] = None, - punctuate: typing.Optional[str] = None, - redact: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - replace: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - sample_rate: typing.Optional[str] = None, - search: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - smart_format: typing.Optional[str] = None, - tag: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - utterance_end_ms: typing.Optional[str] = None, - vad_events: typing.Optional[str] = None, - version: typing.Optional[str] = None, + callback: typing.Optional[ListenV1Callback] = None, + callback_method: typing.Optional[ListenV1CallbackMethod] = None, + channels: typing.Optional[ListenV1Channels] = None, + detect_entities: typing.Optional[ListenV1DetectEntities] = None, + diarize: typing.Optional[ListenV1Diarize] = None, + dictation: typing.Optional[ListenV1Dictation] = None, + encoding: typing.Optional[ListenV1Encoding] = None, + endpointing: typing.Optional[ListenV1Endpointing] = None, + extra: typing.Optional[ListenV1Extra] = None, + interim_results: typing.Optional[ListenV1InterimResults] = None, + keyterm: typing.Optional[ListenV1Keyterm] = None, + keywords: typing.Optional[ListenV1Keywords] = None, + language: typing.Optional[ListenV1Language] = None, + mip_opt_out: typing.Optional[ListenV1MipOptOut] = None, + model: ListenV1Model, + multichannel: typing.Optional[ListenV1Multichannel] = None, + numerals: typing.Optional[ListenV1Numerals] = None, + profanity_filter: typing.Optional[ListenV1ProfanityFilter] = None, + punctuate: typing.Optional[ListenV1Punctuate] = None, + redact: typing.Optional[ListenV1Redact] = None, + replace: typing.Optional[ListenV1Replace] = None, + sample_rate: typing.Optional[ListenV1SampleRate] = None, + search: typing.Optional[ListenV1Search] = None, + smart_format: typing.Optional[ListenV1SmartFormat] = None, + tag: typing.Optional[ListenV1Tag] = None, + utterance_end_ms: typing.Optional[ListenV1UtteranceEndMs] = None, + vad_events: typing.Optional[ListenV1VadEvents] = None, + version: typing.Optional[ListenV1Version] = None, authorization: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> typing.AsyncIterator[AsyncV1SocketClient]: @@ -284,62 +312,62 @@ async def connect( Parameters ---------- - callback : typing.Optional[str] + callback : typing.Optional[ListenV1Callback] - callback_method : typing.Optional[str] + callback_method : typing.Optional[ListenV1CallbackMethod] - channels : typing.Optional[str] + channels : typing.Optional[ListenV1Channels] - detect_entities : typing.Optional[str] + detect_entities : typing.Optional[ListenV1DetectEntities] - diarize : typing.Optional[str] + diarize : typing.Optional[ListenV1Diarize] - dictation : typing.Optional[str] + dictation : typing.Optional[ListenV1Dictation] - encoding : typing.Optional[str] + encoding : typing.Optional[ListenV1Encoding] - endpointing : typing.Optional[str] + endpointing : typing.Optional[ListenV1Endpointing] - extra : typing.Optional[typing.Union[str, typing.Sequence[str]]] + extra : typing.Optional[ListenV1Extra] - interim_results : typing.Optional[str] + interim_results : typing.Optional[ListenV1InterimResults] - keyterm : typing.Optional[typing.Union[str, typing.Sequence[str]]] + keyterm : typing.Optional[ListenV1Keyterm] - keywords : typing.Optional[typing.Union[str, typing.Sequence[str]]] + keywords : typing.Optional[ListenV1Keywords] - language : typing.Optional[str] + language : typing.Optional[ListenV1Language] - mip_opt_out : typing.Optional[str] + mip_opt_out : typing.Optional[ListenV1MipOptOut] - model : str + model : ListenV1Model AI model to use for the transcription - multichannel : typing.Optional[str] + multichannel : typing.Optional[ListenV1Multichannel] - numerals : typing.Optional[str] + numerals : typing.Optional[ListenV1Numerals] - profanity_filter : typing.Optional[str] + profanity_filter : typing.Optional[ListenV1ProfanityFilter] - punctuate : typing.Optional[str] + punctuate : typing.Optional[ListenV1Punctuate] - redact : typing.Optional[typing.Union[str, typing.Sequence[str]]] + redact : typing.Optional[ListenV1Redact] - replace : typing.Optional[typing.Union[str, typing.Sequence[str]]] + replace : typing.Optional[ListenV1Replace] - sample_rate : typing.Optional[str] + sample_rate : typing.Optional[ListenV1SampleRate] - search : typing.Optional[typing.Union[str, typing.Sequence[str]]] + search : typing.Optional[ListenV1Search] - smart_format : typing.Optional[str] + smart_format : typing.Optional[ListenV1SmartFormat] - tag : typing.Optional[typing.Union[str, typing.Sequence[str]]] + tag : typing.Optional[ListenV1Tag] - utterance_end_ms : typing.Optional[str] + utterance_end_ms : typing.Optional[ListenV1UtteranceEndMs] - vad_events : typing.Optional[str] + vad_events : typing.Optional[ListenV1VadEvents] - version : typing.Optional[str] + version : typing.Optional[ListenV1Version] authorization : typing.Optional[str] Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. diff --git a/src/deepgram/listen/v1/client.py.bak b/src/deepgram/listen/v1/client.py.bak new file mode 100644 index 00000000..7dd8baf3 --- /dev/null +++ b/src/deepgram/listen/v1/client.py.bak @@ -0,0 +1,428 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing +import urllib.parse +from contextlib import asynccontextmanager, contextmanager + +import websockets.sync.client as websockets_sync_client +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.jsonable_encoder import jsonable_encoder +from ...core.query_encoder import encode_query +from ...core.remove_none_from_dict import remove_none_from_dict +from ...core.request_options import RequestOptions +from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code +from .raw_client import AsyncRawV1Client, RawV1Client +from .socket_client import AsyncV1SocketClient, V1SocketClient + +if typing.TYPE_CHECKING: + from .media.client import AsyncMediaClient, MediaClient + +try: + from websockets.legacy.client import connect as websockets_client_connect # type: ignore +except ImportError: + from websockets import connect as websockets_client_connect # type: ignore + + +class V1Client: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawV1Client(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._media: typing.Optional[MediaClient] = None + + @property + def with_raw_response(self) -> RawV1Client: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawV1Client + """ + return self._raw_client + + @contextmanager + def connect( + self, + *, + callback: typing.Optional[str] = None, + callback_method: typing.Optional[str] = None, + channels: typing.Optional[str] = None, + detect_entities: typing.Optional[str] = None, + diarize: typing.Optional[str] = None, + dictation: typing.Optional[str] = None, + encoding: typing.Optional[str] = None, + endpointing: typing.Optional[str] = None, + extra: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + interim_results: typing.Optional[str] = None, + keyterm: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + keywords: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + language: typing.Optional[str] = None, + mip_opt_out: typing.Optional[str] = None, + model: str, + multichannel: typing.Optional[str] = None, + numerals: typing.Optional[str] = None, + profanity_filter: typing.Optional[str] = None, + punctuate: typing.Optional[str] = None, + redact: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + replace: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + sample_rate: typing.Optional[str] = None, + search: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + smart_format: typing.Optional[str] = None, + tag: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + utterance_end_ms: typing.Optional[str] = None, + vad_events: typing.Optional[str] = None, + version: typing.Optional[str] = None, + authorization: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[V1SocketClient]: + """ + Transcribe audio and video using Deepgram's speech-to-text WebSocket + + Parameters + ---------- + callback : typing.Optional[str] + + callback_method : typing.Optional[str] + + channels : typing.Optional[str] + + detect_entities : typing.Optional[str] + + diarize : typing.Optional[str] + + dictation : typing.Optional[str] + + encoding : typing.Optional[str] + + endpointing : typing.Optional[str] + + extra : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + interim_results : typing.Optional[str] + + keyterm : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + keywords : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + language : typing.Optional[str] + + mip_opt_out : typing.Optional[str] + + model : str + AI model to use for the transcription + + multichannel : typing.Optional[str] + + numerals : typing.Optional[str] + + profanity_filter : typing.Optional[str] + + punctuate : typing.Optional[str] + + redact : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + replace : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + sample_rate : typing.Optional[str] + + search : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + smart_format : typing.Optional[str] + + tag : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + utterance_end_ms : typing.Optional[str] + + vad_events : typing.Optional[str] + + version : typing.Optional[str] + + authorization : typing.Optional[str] + Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. + + **Example:** `token %DEEPGRAM_API_KEY%` or `bearer %DEEPGRAM_TOKEN%` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + V1SocketClient + """ + ws_url = self._raw_client._client_wrapper.get_environment().production + "/v1/listen" + _encoded_query_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + { + "callback": callback, + "callback_method": callback_method, + "channels": channels, + "detect_entities": detect_entities, + "diarize": diarize, + "dictation": dictation, + "encoding": encoding, + "endpointing": endpointing, + "extra": extra, + "interim_results": interim_results, + "keyterm": keyterm, + "keywords": keywords, + "language": language, + "mip_opt_out": mip_opt_out, + "model": model, + "multichannel": multichannel, + "numerals": numerals, + "profanity_filter": profanity_filter, + "punctuate": punctuate, + "redact": redact, + "replace": replace, + "sample_rate": sample_rate, + "search": search, + "smart_format": smart_format, + "tag": tag, + "utterance_end_ms": utterance_end_ms, + "vad_events": vad_events, + "version": version, + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + } + ) + ) + ) + if _encoded_query_params: + ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params) + headers = self._raw_client._client_wrapper.get_headers() + if authorization is not None: + headers["Authorization"] = str(authorization) + if request_options and "additional_headers" in request_options: + headers.update(request_options["additional_headers"]) + try: + with websockets_sync_client.connect(ws_url, additional_headers=headers) as protocol: + yield V1SocketClient(websocket=protocol) + except InvalidWebSocketStatus as exc: + status_code: int = get_status_code(exc) + if status_code == 401: + raise ApiError( + status_code=status_code, + headers=dict(headers), + body="Websocket initialized with invalid credentials.", + ) + raise ApiError( + status_code=status_code, + headers=dict(headers), + body="Unexpected error when initializing websocket connection.", + ) + + @property + def media(self): + if self._media is None: + from .media.client import MediaClient # noqa: E402 + + self._media = MediaClient(client_wrapper=self._client_wrapper) + return self._media + + +class AsyncV1Client: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawV1Client(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._media: typing.Optional[AsyncMediaClient] = None + + @property + def with_raw_response(self) -> AsyncRawV1Client: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawV1Client + """ + return self._raw_client + + @asynccontextmanager + async def connect( + self, + *, + callback: typing.Optional[str] = None, + callback_method: typing.Optional[str] = None, + channels: typing.Optional[str] = None, + detect_entities: typing.Optional[str] = None, + diarize: typing.Optional[str] = None, + dictation: typing.Optional[str] = None, + encoding: typing.Optional[str] = None, + endpointing: typing.Optional[str] = None, + extra: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + interim_results: typing.Optional[str] = None, + keyterm: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + keywords: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + language: typing.Optional[str] = None, + mip_opt_out: typing.Optional[str] = None, + model: str, + multichannel: typing.Optional[str] = None, + numerals: typing.Optional[str] = None, + profanity_filter: typing.Optional[str] = None, + punctuate: typing.Optional[str] = None, + redact: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + replace: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + sample_rate: typing.Optional[str] = None, + search: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + smart_format: typing.Optional[str] = None, + tag: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + utterance_end_ms: typing.Optional[str] = None, + vad_events: typing.Optional[str] = None, + version: typing.Optional[str] = None, + authorization: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[AsyncV1SocketClient]: + """ + Transcribe audio and video using Deepgram's speech-to-text WebSocket + + Parameters + ---------- + callback : typing.Optional[str] + + callback_method : typing.Optional[str] + + channels : typing.Optional[str] + + detect_entities : typing.Optional[str] + + diarize : typing.Optional[str] + + dictation : typing.Optional[str] + + encoding : typing.Optional[str] + + endpointing : typing.Optional[str] + + extra : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + interim_results : typing.Optional[str] + + keyterm : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + keywords : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + language : typing.Optional[str] + + mip_opt_out : typing.Optional[str] + + model : str + AI model to use for the transcription + + multichannel : typing.Optional[str] + + numerals : typing.Optional[str] + + profanity_filter : typing.Optional[str] + + punctuate : typing.Optional[str] + + redact : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + replace : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + sample_rate : typing.Optional[str] + + search : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + smart_format : typing.Optional[str] + + tag : typing.Optional[typing.Union[str, typing.Sequence[str]]] + + utterance_end_ms : typing.Optional[str] + + vad_events : typing.Optional[str] + + version : typing.Optional[str] + + authorization : typing.Optional[str] + Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. + + **Example:** `token %DEEPGRAM_API_KEY%` or `bearer %DEEPGRAM_TOKEN%` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncV1SocketClient + """ + ws_url = self._raw_client._client_wrapper.get_environment().production + "/v1/listen" + _encoded_query_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + { + "callback": callback, + "callback_method": callback_method, + "channels": channels, + "detect_entities": detect_entities, + "diarize": diarize, + "dictation": dictation, + "encoding": encoding, + "endpointing": endpointing, + "extra": extra, + "interim_results": interim_results, + "keyterm": keyterm, + "keywords": keywords, + "language": language, + "mip_opt_out": mip_opt_out, + "model": model, + "multichannel": multichannel, + "numerals": numerals, + "profanity_filter": profanity_filter, + "punctuate": punctuate, + "redact": redact, + "replace": replace, + "sample_rate": sample_rate, + "search": search, + "smart_format": smart_format, + "tag": tag, + "utterance_end_ms": utterance_end_ms, + "vad_events": vad_events, + "version": version, + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + } + ) + ) + ) + if _encoded_query_params: + ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params) + headers = self._raw_client._client_wrapper.get_headers() + if authorization is not None: + headers["Authorization"] = str(authorization) + if request_options and "additional_headers" in request_options: + headers.update(request_options["additional_headers"]) + try: + async with websockets_client_connect(ws_url, extra_headers=headers) as protocol: + yield AsyncV1SocketClient(websocket=protocol) + except InvalidWebSocketStatus as exc: + status_code: int = get_status_code(exc) + if status_code == 401: + raise ApiError( + status_code=status_code, + headers=dict(headers), + body="Websocket initialized with invalid credentials.", + ) + raise ApiError( + status_code=status_code, + headers=dict(headers), + body="Unexpected error when initializing websocket connection.", + ) + + @property + def media(self): + if self._media is None: + from .media.client import AsyncMediaClient # noqa: E402 + + self._media = AsyncMediaClient(client_wrapper=self._client_wrapper) + return self._media diff --git a/src/deepgram/listen/v1/raw_client.py b/src/deepgram/listen/v1/raw_client.py index 7b2d6bd6..9f5862f3 100644 --- a/src/deepgram/listen/v1/raw_client.py +++ b/src/deepgram/listen/v1/raw_client.py @@ -12,6 +12,34 @@ from ...core.remove_none_from_dict import remove_none_from_dict from ...core.request_options import RequestOptions from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code +from ...types.listen_v1callback import ListenV1Callback +from ...types.listen_v1callback_method import ListenV1CallbackMethod +from ...types.listen_v1channels import ListenV1Channels +from ...types.listen_v1detect_entities import ListenV1DetectEntities +from ...types.listen_v1diarize import ListenV1Diarize +from ...types.listen_v1dictation import ListenV1Dictation +from ...types.listen_v1encoding import ListenV1Encoding +from ...types.listen_v1endpointing import ListenV1Endpointing +from ...types.listen_v1extra import ListenV1Extra +from ...types.listen_v1interim_results import ListenV1InterimResults +from ...types.listen_v1keyterm import ListenV1Keyterm +from ...types.listen_v1keywords import ListenV1Keywords +from ...types.listen_v1language import ListenV1Language +from ...types.listen_v1mip_opt_out import ListenV1MipOptOut +from ...types.listen_v1model import ListenV1Model +from ...types.listen_v1multichannel import ListenV1Multichannel +from ...types.listen_v1numerals import ListenV1Numerals +from ...types.listen_v1profanity_filter import ListenV1ProfanityFilter +from ...types.listen_v1punctuate import ListenV1Punctuate +from ...types.listen_v1redact import ListenV1Redact +from ...types.listen_v1replace import ListenV1Replace +from ...types.listen_v1sample_rate import ListenV1SampleRate +from ...types.listen_v1search import ListenV1Search +from ...types.listen_v1smart_format import ListenV1SmartFormat +from ...types.listen_v1tag import ListenV1Tag +from ...types.listen_v1utterance_end_ms import ListenV1UtteranceEndMs +from ...types.listen_v1vad_events import ListenV1VadEvents +from ...types.listen_v1version import ListenV1Version from .socket_client import AsyncV1SocketClient, V1SocketClient try: @@ -28,34 +56,34 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): def connect( self, *, - callback: typing.Optional[str] = None, - callback_method: typing.Optional[str] = None, - channels: typing.Optional[str] = None, - detect_entities: typing.Optional[str] = None, - diarize: typing.Optional[str] = None, - dictation: typing.Optional[str] = None, - encoding: typing.Optional[str] = None, - endpointing: typing.Optional[str] = None, - extra: typing.Optional[str] = None, - interim_results: typing.Optional[str] = None, - keyterm: typing.Optional[str] = None, - keywords: typing.Optional[str] = None, - language: typing.Optional[str] = None, - mip_opt_out: typing.Optional[str] = None, - model: str, - multichannel: typing.Optional[str] = None, - numerals: typing.Optional[str] = None, - profanity_filter: typing.Optional[str] = None, - punctuate: typing.Optional[str] = None, - redact: typing.Optional[str] = None, - replace: typing.Optional[str] = None, - sample_rate: typing.Optional[str] = None, - search: typing.Optional[str] = None, - smart_format: typing.Optional[str] = None, - tag: typing.Optional[str] = None, - utterance_end_ms: typing.Optional[str] = None, - vad_events: typing.Optional[str] = None, - version: typing.Optional[str] = None, + callback: typing.Optional[ListenV1Callback] = None, + callback_method: typing.Optional[ListenV1CallbackMethod] = None, + channels: typing.Optional[ListenV1Channels] = None, + detect_entities: typing.Optional[ListenV1DetectEntities] = None, + diarize: typing.Optional[ListenV1Diarize] = None, + dictation: typing.Optional[ListenV1Dictation] = None, + encoding: typing.Optional[ListenV1Encoding] = None, + endpointing: typing.Optional[ListenV1Endpointing] = None, + extra: typing.Optional[ListenV1Extra] = None, + interim_results: typing.Optional[ListenV1InterimResults] = None, + keyterm: typing.Optional[ListenV1Keyterm] = None, + keywords: typing.Optional[ListenV1Keywords] = None, + language: typing.Optional[ListenV1Language] = None, + mip_opt_out: typing.Optional[ListenV1MipOptOut] = None, + model: ListenV1Model, + multichannel: typing.Optional[ListenV1Multichannel] = None, + numerals: typing.Optional[ListenV1Numerals] = None, + profanity_filter: typing.Optional[ListenV1ProfanityFilter] = None, + punctuate: typing.Optional[ListenV1Punctuate] = None, + redact: typing.Optional[ListenV1Redact] = None, + replace: typing.Optional[ListenV1Replace] = None, + sample_rate: typing.Optional[ListenV1SampleRate] = None, + search: typing.Optional[ListenV1Search] = None, + smart_format: typing.Optional[ListenV1SmartFormat] = None, + tag: typing.Optional[ListenV1Tag] = None, + utterance_end_ms: typing.Optional[ListenV1UtteranceEndMs] = None, + vad_events: typing.Optional[ListenV1VadEvents] = None, + version: typing.Optional[ListenV1Version] = None, authorization: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> typing.Iterator[V1SocketClient]: @@ -64,62 +92,62 @@ def connect( Parameters ---------- - callback : typing.Optional[str] + callback : typing.Optional[ListenV1Callback] - callback_method : typing.Optional[str] + callback_method : typing.Optional[ListenV1CallbackMethod] - channels : typing.Optional[str] + channels : typing.Optional[ListenV1Channels] - detect_entities : typing.Optional[str] + detect_entities : typing.Optional[ListenV1DetectEntities] - diarize : typing.Optional[str] + diarize : typing.Optional[ListenV1Diarize] - dictation : typing.Optional[str] + dictation : typing.Optional[ListenV1Dictation] - encoding : typing.Optional[str] + encoding : typing.Optional[ListenV1Encoding] - endpointing : typing.Optional[str] + endpointing : typing.Optional[ListenV1Endpointing] - extra : typing.Optional[str] + extra : typing.Optional[ListenV1Extra] - interim_results : typing.Optional[str] + interim_results : typing.Optional[ListenV1InterimResults] - keyterm : typing.Optional[str] + keyterm : typing.Optional[ListenV1Keyterm] - keywords : typing.Optional[str] + keywords : typing.Optional[ListenV1Keywords] - language : typing.Optional[str] + language : typing.Optional[ListenV1Language] - mip_opt_out : typing.Optional[str] + mip_opt_out : typing.Optional[ListenV1MipOptOut] - model : str + model : ListenV1Model AI model to use for the transcription - multichannel : typing.Optional[str] + multichannel : typing.Optional[ListenV1Multichannel] - numerals : typing.Optional[str] + numerals : typing.Optional[ListenV1Numerals] - profanity_filter : typing.Optional[str] + profanity_filter : typing.Optional[ListenV1ProfanityFilter] - punctuate : typing.Optional[str] + punctuate : typing.Optional[ListenV1Punctuate] - redact : typing.Optional[str] + redact : typing.Optional[ListenV1Redact] - replace : typing.Optional[str] + replace : typing.Optional[ListenV1Replace] - sample_rate : typing.Optional[str] + sample_rate : typing.Optional[ListenV1SampleRate] - search : typing.Optional[str] + search : typing.Optional[ListenV1Search] - smart_format : typing.Optional[str] + smart_format : typing.Optional[ListenV1SmartFormat] - tag : typing.Optional[str] + tag : typing.Optional[ListenV1Tag] - utterance_end_ms : typing.Optional[str] + utterance_end_ms : typing.Optional[ListenV1UtteranceEndMs] - vad_events : typing.Optional[str] + vad_events : typing.Optional[ListenV1VadEvents] - version : typing.Optional[str] + version : typing.Optional[ListenV1Version] authorization : typing.Optional[str] Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. @@ -208,34 +236,34 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): async def connect( self, *, - callback: typing.Optional[str] = None, - callback_method: typing.Optional[str] = None, - channels: typing.Optional[str] = None, - detect_entities: typing.Optional[str] = None, - diarize: typing.Optional[str] = None, - dictation: typing.Optional[str] = None, - encoding: typing.Optional[str] = None, - endpointing: typing.Optional[str] = None, - extra: typing.Optional[str] = None, - interim_results: typing.Optional[str] = None, - keyterm: typing.Optional[str] = None, - keywords: typing.Optional[str] = None, - language: typing.Optional[str] = None, - mip_opt_out: typing.Optional[str] = None, - model: str, - multichannel: typing.Optional[str] = None, - numerals: typing.Optional[str] = None, - profanity_filter: typing.Optional[str] = None, - punctuate: typing.Optional[str] = None, - redact: typing.Optional[str] = None, - replace: typing.Optional[str] = None, - sample_rate: typing.Optional[str] = None, - search: typing.Optional[str] = None, - smart_format: typing.Optional[str] = None, - tag: typing.Optional[str] = None, - utterance_end_ms: typing.Optional[str] = None, - vad_events: typing.Optional[str] = None, - version: typing.Optional[str] = None, + callback: typing.Optional[ListenV1Callback] = None, + callback_method: typing.Optional[ListenV1CallbackMethod] = None, + channels: typing.Optional[ListenV1Channels] = None, + detect_entities: typing.Optional[ListenV1DetectEntities] = None, + diarize: typing.Optional[ListenV1Diarize] = None, + dictation: typing.Optional[ListenV1Dictation] = None, + encoding: typing.Optional[ListenV1Encoding] = None, + endpointing: typing.Optional[ListenV1Endpointing] = None, + extra: typing.Optional[ListenV1Extra] = None, + interim_results: typing.Optional[ListenV1InterimResults] = None, + keyterm: typing.Optional[ListenV1Keyterm] = None, + keywords: typing.Optional[ListenV1Keywords] = None, + language: typing.Optional[ListenV1Language] = None, + mip_opt_out: typing.Optional[ListenV1MipOptOut] = None, + model: ListenV1Model, + multichannel: typing.Optional[ListenV1Multichannel] = None, + numerals: typing.Optional[ListenV1Numerals] = None, + profanity_filter: typing.Optional[ListenV1ProfanityFilter] = None, + punctuate: typing.Optional[ListenV1Punctuate] = None, + redact: typing.Optional[ListenV1Redact] = None, + replace: typing.Optional[ListenV1Replace] = None, + sample_rate: typing.Optional[ListenV1SampleRate] = None, + search: typing.Optional[ListenV1Search] = None, + smart_format: typing.Optional[ListenV1SmartFormat] = None, + tag: typing.Optional[ListenV1Tag] = None, + utterance_end_ms: typing.Optional[ListenV1UtteranceEndMs] = None, + vad_events: typing.Optional[ListenV1VadEvents] = None, + version: typing.Optional[ListenV1Version] = None, authorization: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> typing.AsyncIterator[AsyncV1SocketClient]: @@ -244,62 +272,62 @@ async def connect( Parameters ---------- - callback : typing.Optional[str] + callback : typing.Optional[ListenV1Callback] - callback_method : typing.Optional[str] + callback_method : typing.Optional[ListenV1CallbackMethod] - channels : typing.Optional[str] + channels : typing.Optional[ListenV1Channels] - detect_entities : typing.Optional[str] + detect_entities : typing.Optional[ListenV1DetectEntities] - diarize : typing.Optional[str] + diarize : typing.Optional[ListenV1Diarize] - dictation : typing.Optional[str] + dictation : typing.Optional[ListenV1Dictation] - encoding : typing.Optional[str] + encoding : typing.Optional[ListenV1Encoding] - endpointing : typing.Optional[str] + endpointing : typing.Optional[ListenV1Endpointing] - extra : typing.Optional[str] + extra : typing.Optional[ListenV1Extra] - interim_results : typing.Optional[str] + interim_results : typing.Optional[ListenV1InterimResults] - keyterm : typing.Optional[str] + keyterm : typing.Optional[ListenV1Keyterm] - keywords : typing.Optional[str] + keywords : typing.Optional[ListenV1Keywords] - language : typing.Optional[str] + language : typing.Optional[ListenV1Language] - mip_opt_out : typing.Optional[str] + mip_opt_out : typing.Optional[ListenV1MipOptOut] - model : str + model : ListenV1Model AI model to use for the transcription - multichannel : typing.Optional[str] + multichannel : typing.Optional[ListenV1Multichannel] - numerals : typing.Optional[str] + numerals : typing.Optional[ListenV1Numerals] - profanity_filter : typing.Optional[str] + profanity_filter : typing.Optional[ListenV1ProfanityFilter] - punctuate : typing.Optional[str] + punctuate : typing.Optional[ListenV1Punctuate] - redact : typing.Optional[str] + redact : typing.Optional[ListenV1Redact] - replace : typing.Optional[str] + replace : typing.Optional[ListenV1Replace] - sample_rate : typing.Optional[str] + sample_rate : typing.Optional[ListenV1SampleRate] - search : typing.Optional[str] + search : typing.Optional[ListenV1Search] - smart_format : typing.Optional[str] + smart_format : typing.Optional[ListenV1SmartFormat] - tag : typing.Optional[str] + tag : typing.Optional[ListenV1Tag] - utterance_end_ms : typing.Optional[str] + utterance_end_ms : typing.Optional[ListenV1UtteranceEndMs] - vad_events : typing.Optional[str] + vad_events : typing.Optional[ListenV1VadEvents] - version : typing.Optional[str] + version : typing.Optional[ListenV1Version] authorization : typing.Optional[str] Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. diff --git a/src/deepgram/listen/v1/socket_client.py b/src/deepgram/listen/v1/socket_client.py index c16839d3..974e4c4a 100644 --- a/src/deepgram/listen/v1/socket_client.py +++ b/src/deepgram/listen/v1/socket_client.py @@ -1,13 +1,14 @@ # This file was auto-generated by Fern from our API Definition. import json +import logging import typing from json.decoder import JSONDecodeError import websockets import websockets.sync.connection as websockets_sync_connection from ...core.events import EventEmitterMixin, EventType -from ...core.unchecked_base_model import construct_type +from ...core.pydantic_utilities import parse_obj_as from .types.listen_v1close_stream import ListenV1CloseStream from .types.listen_v1finalize import ListenV1Finalize from .types.listen_v1keep_alive import ListenV1KeepAlive @@ -21,6 +22,7 @@ except ImportError: from websockets import WebSocketClientProtocol # type: ignore +_logger = logging.getLogger(__name__) V1SocketClientResponse = typing.Union[ListenV1Results, ListenV1Metadata, ListenV1UtteranceEnd, ListenV1SpeechStarted] @@ -34,7 +36,13 @@ async def __aiter__(self): if isinstance(message, bytes): yield message else: - yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore + try: + yield parse_obj_as(V1SocketClientResponse, json.loads(message)) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue async def start_listening(self): """ @@ -53,9 +61,15 @@ async def start_listening(self): parsed = raw_message else: json_data = json.loads(raw_message) - parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + try: + parsed = parse_obj_as(V1SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue await self._emit_async(EventType.MESSAGE, parsed) - except Exception as exc: + except (websockets.WebSocketException, JSONDecodeError) as exc: await self._emit_async(EventType.ERROR, exc) finally: await self._emit_async(EventType.CLOSE, None) @@ -67,26 +81,26 @@ async def send_media(self, message: bytes) -> None: """ await self._send(message) - async def send_finalize(self, message: typing.Optional[ListenV1Finalize] = None) -> None: + async def send_finalize(self, message: ListenV1Finalize) -> None: """ Send a message to the websocket connection. The message will be sent as a ListenV1Finalize. """ - await self._send_model(message or ListenV1Finalize(type="Finalize")) + await self._send_model(message) - async def send_close_stream(self, message: typing.Optional[ListenV1CloseStream] = None) -> None: + async def send_close_stream(self, message: ListenV1CloseStream) -> None: """ Send a message to the websocket connection. The message will be sent as a ListenV1CloseStream. """ - await self._send_model(message or ListenV1CloseStream(type="CloseStream")) + await self._send_model(message) - async def send_keep_alive(self, message: typing.Optional[ListenV1KeepAlive] = None) -> None: + async def send_keep_alive(self, message: ListenV1KeepAlive) -> None: """ Send a message to the websocket connection. The message will be sent as a ListenV1KeepAlive. """ - await self._send_model(message or ListenV1KeepAlive(type="KeepAlive")) + await self._send_model(message) async def recv(self) -> V1SocketClientResponse: """ @@ -96,7 +110,11 @@ async def recv(self) -> V1SocketClientResponse: if isinstance(data, bytes): return data # type: ignore json_data = json.loads(data) - return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + try: + return parse_obj_as(V1SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.") + return json_data # type: ignore async def _send(self, data: typing.Any) -> None: """ @@ -123,7 +141,13 @@ def __iter__(self): if isinstance(message, bytes): yield message else: - yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore + try: + yield parse_obj_as(V1SocketClientResponse, json.loads(message)) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue def start_listening(self): """ @@ -142,9 +166,15 @@ def start_listening(self): parsed = raw_message else: json_data = json.loads(raw_message) - parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + try: + parsed = parse_obj_as(V1SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue self._emit(EventType.MESSAGE, parsed) - except Exception as exc: + except (websockets.WebSocketException, JSONDecodeError) as exc: self._emit(EventType.ERROR, exc) finally: self._emit(EventType.CLOSE, None) @@ -156,26 +186,26 @@ def send_media(self, message: bytes) -> None: """ self._send(message) - def send_finalize(self, message: typing.Optional[ListenV1Finalize] = None) -> None: + def send_finalize(self, message: ListenV1Finalize) -> None: """ Send a message to the websocket connection. The message will be sent as a ListenV1Finalize. """ - self._send_model(message or ListenV1Finalize(type="Finalize")) + self._send_model(message) - def send_close_stream(self, message: typing.Optional[ListenV1CloseStream] = None) -> None: + def send_close_stream(self, message: ListenV1CloseStream) -> None: """ Send a message to the websocket connection. The message will be sent as a ListenV1CloseStream. """ - self._send_model(message or ListenV1CloseStream(type="CloseStream")) + self._send_model(message) - def send_keep_alive(self, message: typing.Optional[ListenV1KeepAlive] = None) -> None: + def send_keep_alive(self, message: ListenV1KeepAlive) -> None: """ Send a message to the websocket connection. The message will be sent as a ListenV1KeepAlive. """ - self._send_model(message or ListenV1KeepAlive(type="KeepAlive")) + self._send_model(message) def recv(self) -> V1SocketClientResponse: """ @@ -185,7 +215,11 @@ def recv(self) -> V1SocketClientResponse: if isinstance(data, bytes): return data # type: ignore json_data = json.loads(data) - return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + try: + return parse_obj_as(V1SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.") + return json_data # type: ignore def _send(self, data: typing.Any) -> None: """ diff --git a/src/deepgram/listen/v1/socket_client.py.bak b/src/deepgram/listen/v1/socket_client.py.bak new file mode 100644 index 00000000..c16839d3 --- /dev/null +++ b/src/deepgram/listen/v1/socket_client.py.bak @@ -0,0 +1,202 @@ +# This file was auto-generated by Fern from our API Definition. + +import json +import typing +from json.decoder import JSONDecodeError + +import websockets +import websockets.sync.connection as websockets_sync_connection +from ...core.events import EventEmitterMixin, EventType +from ...core.unchecked_base_model import construct_type +from .types.listen_v1close_stream import ListenV1CloseStream +from .types.listen_v1finalize import ListenV1Finalize +from .types.listen_v1keep_alive import ListenV1KeepAlive +from .types.listen_v1metadata import ListenV1Metadata +from .types.listen_v1results import ListenV1Results +from .types.listen_v1speech_started import ListenV1SpeechStarted +from .types.listen_v1utterance_end import ListenV1UtteranceEnd + +try: + from websockets.legacy.client import WebSocketClientProtocol # type: ignore +except ImportError: + from websockets import WebSocketClientProtocol # type: ignore + +V1SocketClientResponse = typing.Union[ListenV1Results, ListenV1Metadata, ListenV1UtteranceEnd, ListenV1SpeechStarted] + + +class AsyncV1SocketClient(EventEmitterMixin): + def __init__(self, *, websocket: WebSocketClientProtocol): + super().__init__() + self._websocket = websocket + + async def __aiter__(self): + async for message in self._websocket: + if isinstance(message, bytes): + yield message + else: + yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore + + async def start_listening(self): + """ + Start listening for messages on the websocket connection. + + Emits events in the following order: + - EventType.OPEN when connection is established + - EventType.MESSAGE for each message received + - EventType.ERROR if an error occurs + - EventType.CLOSE when connection is closed + """ + await self._emit_async(EventType.OPEN, None) + try: + async for raw_message in self._websocket: + if isinstance(raw_message, bytes): + parsed = raw_message + else: + json_data = json.loads(raw_message) + parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + await self._emit_async(EventType.MESSAGE, parsed) + except Exception as exc: + await self._emit_async(EventType.ERROR, exc) + finally: + await self._emit_async(EventType.CLOSE, None) + + async def send_media(self, message: bytes) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a bytes. + """ + await self._send(message) + + async def send_finalize(self, message: typing.Optional[ListenV1Finalize] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a ListenV1Finalize. + """ + await self._send_model(message or ListenV1Finalize(type="Finalize")) + + async def send_close_stream(self, message: typing.Optional[ListenV1CloseStream] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a ListenV1CloseStream. + """ + await self._send_model(message or ListenV1CloseStream(type="CloseStream")) + + async def send_keep_alive(self, message: typing.Optional[ListenV1KeepAlive] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a ListenV1KeepAlive. + """ + await self._send_model(message or ListenV1KeepAlive(type="KeepAlive")) + + async def recv(self) -> V1SocketClientResponse: + """ + Receive a message from the websocket connection. + """ + data = await self._websocket.recv() + if isinstance(data, bytes): + return data # type: ignore + json_data = json.loads(data) + return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + + async def _send(self, data: typing.Any) -> None: + """ + Send a message to the websocket connection. + """ + if isinstance(data, dict): + data = json.dumps(data) + await self._websocket.send(data) + + async def _send_model(self, data: typing.Any) -> None: + """ + Send a Pydantic model to the websocket connection. + """ + await self._send(data.dict()) + + +class V1SocketClient(EventEmitterMixin): + def __init__(self, *, websocket: websockets_sync_connection.Connection): + super().__init__() + self._websocket = websocket + + def __iter__(self): + for message in self._websocket: + if isinstance(message, bytes): + yield message + else: + yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore + + def start_listening(self): + """ + Start listening for messages on the websocket connection. + + Emits events in the following order: + - EventType.OPEN when connection is established + - EventType.MESSAGE for each message received + - EventType.ERROR if an error occurs + - EventType.CLOSE when connection is closed + """ + self._emit(EventType.OPEN, None) + try: + for raw_message in self._websocket: + if isinstance(raw_message, bytes): + parsed = raw_message + else: + json_data = json.loads(raw_message) + parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + self._emit(EventType.MESSAGE, parsed) + except Exception as exc: + self._emit(EventType.ERROR, exc) + finally: + self._emit(EventType.CLOSE, None) + + def send_media(self, message: bytes) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a bytes. + """ + self._send(message) + + def send_finalize(self, message: typing.Optional[ListenV1Finalize] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a ListenV1Finalize. + """ + self._send_model(message or ListenV1Finalize(type="Finalize")) + + def send_close_stream(self, message: typing.Optional[ListenV1CloseStream] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a ListenV1CloseStream. + """ + self._send_model(message or ListenV1CloseStream(type="CloseStream")) + + def send_keep_alive(self, message: typing.Optional[ListenV1KeepAlive] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a ListenV1KeepAlive. + """ + self._send_model(message or ListenV1KeepAlive(type="KeepAlive")) + + def recv(self) -> V1SocketClientResponse: + """ + Receive a message from the websocket connection. + """ + data = self._websocket.recv() + if isinstance(data, bytes): + return data # type: ignore + json_data = json.loads(data) + return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + + def _send(self, data: typing.Any) -> None: + """ + Send a message to the websocket connection. + """ + if isinstance(data, dict): + data = json.dumps(data) + self._websocket.send(data) + + def _send_model(self, data: typing.Any) -> None: + """ + Send a Pydantic model to the websocket connection. + """ + self._send(data.dict()) diff --git a/src/deepgram/listen/v2/__init__.py b/src/deepgram/listen/v2/__init__.py index db7c724c..ca134a12 100644 --- a/src/deepgram/listen/v2/__init__.py +++ b/src/deepgram/listen/v2/__init__.py @@ -9,6 +9,11 @@ from .types import ( ListenV2CloseStream, ListenV2CloseStreamType, + ListenV2Configure, + ListenV2ConfigureFailure, + ListenV2ConfigureSuccess, + ListenV2ConfigureSuccessThresholds, + ListenV2ConfigureThresholds, ListenV2Connected, ListenV2FatalError, ListenV2TurnInfo, @@ -17,6 +22,11 @@ ) from .requests import ( ListenV2CloseStreamParams, + ListenV2ConfigureFailureParams, + ListenV2ConfigureParams, + ListenV2ConfigureSuccessParams, + ListenV2ConfigureSuccessThresholdsParams, + ListenV2ConfigureThresholdsParams, ListenV2ConnectedParams, ListenV2FatalErrorParams, ListenV2TurnInfoParams, @@ -26,6 +36,16 @@ "ListenV2CloseStream": ".types", "ListenV2CloseStreamParams": ".requests", "ListenV2CloseStreamType": ".types", + "ListenV2Configure": ".types", + "ListenV2ConfigureFailure": ".types", + "ListenV2ConfigureFailureParams": ".requests", + "ListenV2ConfigureParams": ".requests", + "ListenV2ConfigureSuccess": ".types", + "ListenV2ConfigureSuccessParams": ".requests", + "ListenV2ConfigureSuccessThresholds": ".types", + "ListenV2ConfigureSuccessThresholdsParams": ".requests", + "ListenV2ConfigureThresholds": ".types", + "ListenV2ConfigureThresholdsParams": ".requests", "ListenV2Connected": ".types", "ListenV2ConnectedParams": ".requests", "ListenV2FatalError": ".types", @@ -63,6 +83,16 @@ def __dir__(): "ListenV2CloseStream", "ListenV2CloseStreamParams", "ListenV2CloseStreamType", + "ListenV2Configure", + "ListenV2ConfigureFailure", + "ListenV2ConfigureFailureParams", + "ListenV2ConfigureParams", + "ListenV2ConfigureSuccess", + "ListenV2ConfigureSuccessParams", + "ListenV2ConfigureSuccessThresholds", + "ListenV2ConfigureSuccessThresholdsParams", + "ListenV2ConfigureThresholds", + "ListenV2ConfigureThresholdsParams", "ListenV2Connected", "ListenV2ConnectedParams", "ListenV2FatalError", diff --git a/src/deepgram/listen/v2/client.py b/src/deepgram/listen/v2/client.py index 949f0c75..86ab8f5d 100644 --- a/src/deepgram/listen/v2/client.py +++ b/src/deepgram/listen/v2/client.py @@ -11,7 +11,16 @@ from ...core.query_encoder import encode_query from ...core.remove_none_from_dict import remove_none_from_dict from ...core.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code +from ...requests.listen_v2keyterm import ListenV2KeytermParams +from ...types.listen_v2eager_eot_threshold import ListenV2EagerEotThreshold +from ...types.listen_v2encoding import ListenV2Encoding +from ...types.listen_v2eot_threshold import ListenV2EotThreshold +from ...types.listen_v2eot_timeout_ms import ListenV2EotTimeoutMs +from ...types.listen_v2mip_opt_out import ListenV2MipOptOut +from ...types.listen_v2sample_rate import ListenV2SampleRate +from ...types.listen_v2tag import ListenV2Tag from .raw_client import AsyncRawV2Client, RawV2Client from .socket_client import AsyncV2SocketClient, V2SocketClient @@ -40,15 +49,14 @@ def with_raw_response(self) -> RawV2Client: def connect( self, *, - model: str, - encoding: typing.Optional[str] = None, - sample_rate: typing.Optional[str] = None, - eager_eot_threshold: typing.Optional[str] = None, - eot_threshold: typing.Optional[str] = None, - eot_timeout_ms: typing.Optional[str] = None, - keyterm: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - mip_opt_out: typing.Optional[str] = None, - tag: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + encoding: typing.Optional[ListenV2Encoding] = None, + sample_rate: typing.Optional[ListenV2SampleRate] = None, + eager_eot_threshold: typing.Optional[ListenV2EagerEotThreshold] = None, + eot_threshold: typing.Optional[ListenV2EotThreshold] = None, + eot_timeout_ms: typing.Optional[ListenV2EotTimeoutMs] = None, + keyterm: typing.Optional[ListenV2KeytermParams] = None, + mip_opt_out: typing.Optional[ListenV2MipOptOut] = None, + tag: typing.Optional[ListenV2Tag] = None, authorization: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> typing.Iterator[V2SocketClient]: @@ -58,25 +66,21 @@ def connect( Parameters ---------- - model : str + encoding : typing.Optional[ListenV2Encoding] - encoding : typing.Optional[str] + sample_rate : typing.Optional[ListenV2SampleRate] - sample_rate : typing.Optional[str] + eager_eot_threshold : typing.Optional[ListenV2EagerEotThreshold] - eager_eot_threshold : typing.Optional[str] + eot_threshold : typing.Optional[ListenV2EotThreshold] - eot_threshold : typing.Optional[str] + eot_timeout_ms : typing.Optional[ListenV2EotTimeoutMs] - eot_timeout_ms : typing.Optional[str] + keyterm : typing.Optional[ListenV2KeytermParams] - keyterm : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Keyterm prompting can improve recognition of specialized terminology. Pass a single string or a list of strings. + mip_opt_out : typing.Optional[ListenV2MipOptOut] - mip_opt_out : typing.Optional[str] - - tag : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Label your requests for the purpose of identification during usage reporting. Pass a single string or a list of strings. + tag : typing.Optional[ListenV2Tag] authorization : typing.Optional[str] Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. @@ -95,13 +99,19 @@ def connect( jsonable_encoder( remove_none_from_dict( { - "model": model, + "model": "flux-general-en", "encoding": encoding, "sample_rate": sample_rate, "eager_eot_threshold": eager_eot_threshold, "eot_threshold": eot_threshold, "eot_timeout_ms": eot_timeout_ms, - "keyterm": keyterm, + "keyterm": convert_and_respect_annotation_metadata( + object_=convert_and_respect_annotation_metadata( + object_=keyterm, annotation=ListenV2KeytermParams, direction="write" + ), + annotation=ListenV2KeytermParams, + direction="write", + ), "mip_opt_out": mip_opt_out, "tag": tag, **( @@ -157,15 +167,14 @@ def with_raw_response(self) -> AsyncRawV2Client: async def connect( self, *, - model: str, - encoding: typing.Optional[str] = None, - sample_rate: typing.Optional[str] = None, - eager_eot_threshold: typing.Optional[str] = None, - eot_threshold: typing.Optional[str] = None, - eot_timeout_ms: typing.Optional[str] = None, - keyterm: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - mip_opt_out: typing.Optional[str] = None, - tag: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + encoding: typing.Optional[ListenV2Encoding] = None, + sample_rate: typing.Optional[ListenV2SampleRate] = None, + eager_eot_threshold: typing.Optional[ListenV2EagerEotThreshold] = None, + eot_threshold: typing.Optional[ListenV2EotThreshold] = None, + eot_timeout_ms: typing.Optional[ListenV2EotTimeoutMs] = None, + keyterm: typing.Optional[ListenV2KeytermParams] = None, + mip_opt_out: typing.Optional[ListenV2MipOptOut] = None, + tag: typing.Optional[ListenV2Tag] = None, authorization: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> typing.AsyncIterator[AsyncV2SocketClient]: @@ -175,25 +184,21 @@ async def connect( Parameters ---------- - model : str + encoding : typing.Optional[ListenV2Encoding] - encoding : typing.Optional[str] + sample_rate : typing.Optional[ListenV2SampleRate] - sample_rate : typing.Optional[str] + eager_eot_threshold : typing.Optional[ListenV2EagerEotThreshold] - eager_eot_threshold : typing.Optional[str] + eot_threshold : typing.Optional[ListenV2EotThreshold] - eot_threshold : typing.Optional[str] + eot_timeout_ms : typing.Optional[ListenV2EotTimeoutMs] - eot_timeout_ms : typing.Optional[str] + keyterm : typing.Optional[ListenV2KeytermParams] - keyterm : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Keyterm prompting can improve recognition of specialized terminology. Pass a single string or a list of strings. + mip_opt_out : typing.Optional[ListenV2MipOptOut] - mip_opt_out : typing.Optional[str] - - tag : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Label your requests for the purpose of identification during usage reporting. Pass a single string or a list of strings. + tag : typing.Optional[ListenV2Tag] authorization : typing.Optional[str] Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. @@ -212,13 +217,19 @@ async def connect( jsonable_encoder( remove_none_from_dict( { - "model": model, + "model": "flux-general-en", "encoding": encoding, "sample_rate": sample_rate, "eager_eot_threshold": eager_eot_threshold, "eot_threshold": eot_threshold, "eot_timeout_ms": eot_timeout_ms, - "keyterm": keyterm, + "keyterm": convert_and_respect_annotation_metadata( + object_=convert_and_respect_annotation_metadata( + object_=keyterm, annotation=ListenV2KeytermParams, direction="write" + ), + annotation=ListenV2KeytermParams, + direction="write", + ), "mip_opt_out": mip_opt_out, "tag": tag, **( diff --git a/src/deepgram/listen/v2/client.py.bak b/src/deepgram/listen/v2/client.py.bak new file mode 100644 index 00000000..949f0c75 --- /dev/null +++ b/src/deepgram/listen/v2/client.py.bak @@ -0,0 +1,255 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +import urllib.parse +from contextlib import asynccontextmanager, contextmanager + +import websockets.sync.client as websockets_sync_client +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.jsonable_encoder import jsonable_encoder +from ...core.query_encoder import encode_query +from ...core.remove_none_from_dict import remove_none_from_dict +from ...core.request_options import RequestOptions +from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code +from .raw_client import AsyncRawV2Client, RawV2Client +from .socket_client import AsyncV2SocketClient, V2SocketClient + +try: + from websockets.legacy.client import connect as websockets_client_connect # type: ignore +except ImportError: + from websockets import connect as websockets_client_connect # type: ignore + + +class V2Client: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawV2Client(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawV2Client: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawV2Client + """ + return self._raw_client + + @contextmanager + def connect( + self, + *, + model: str, + encoding: typing.Optional[str] = None, + sample_rate: typing.Optional[str] = None, + eager_eot_threshold: typing.Optional[str] = None, + eot_threshold: typing.Optional[str] = None, + eot_timeout_ms: typing.Optional[str] = None, + keyterm: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + mip_opt_out: typing.Optional[str] = None, + tag: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + authorization: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[V2SocketClient]: + """ + Real-time conversational speech recognition with contextual turn detection + for natural voice conversations + + Parameters + ---------- + model : str + + encoding : typing.Optional[str] + + sample_rate : typing.Optional[str] + + eager_eot_threshold : typing.Optional[str] + + eot_threshold : typing.Optional[str] + + eot_timeout_ms : typing.Optional[str] + + keyterm : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Keyterm prompting can improve recognition of specialized terminology. Pass a single string or a list of strings. + + mip_opt_out : typing.Optional[str] + + tag : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Label your requests for the purpose of identification during usage reporting. Pass a single string or a list of strings. + + authorization : typing.Optional[str] + Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. + + **Example:** `token %DEEPGRAM_API_KEY%` or `bearer %DEEPGRAM_TOKEN%` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + V2SocketClient + """ + ws_url = self._raw_client._client_wrapper.get_environment().production + "/v2/listen" + _encoded_query_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + { + "model": model, + "encoding": encoding, + "sample_rate": sample_rate, + "eager_eot_threshold": eager_eot_threshold, + "eot_threshold": eot_threshold, + "eot_timeout_ms": eot_timeout_ms, + "keyterm": keyterm, + "mip_opt_out": mip_opt_out, + "tag": tag, + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + } + ) + ) + ) + if _encoded_query_params: + ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params) + headers = self._raw_client._client_wrapper.get_headers() + if authorization is not None: + headers["Authorization"] = str(authorization) + if request_options and "additional_headers" in request_options: + headers.update(request_options["additional_headers"]) + try: + with websockets_sync_client.connect(ws_url, additional_headers=headers) as protocol: + yield V2SocketClient(websocket=protocol) + except InvalidWebSocketStatus as exc: + status_code: int = get_status_code(exc) + if status_code == 401: + raise ApiError( + status_code=status_code, + headers=dict(headers), + body="Websocket initialized with invalid credentials.", + ) + raise ApiError( + status_code=status_code, + headers=dict(headers), + body="Unexpected error when initializing websocket connection.", + ) + + +class AsyncV2Client: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawV2Client(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawV2Client: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawV2Client + """ + return self._raw_client + + @asynccontextmanager + async def connect( + self, + *, + model: str, + encoding: typing.Optional[str] = None, + sample_rate: typing.Optional[str] = None, + eager_eot_threshold: typing.Optional[str] = None, + eot_threshold: typing.Optional[str] = None, + eot_timeout_ms: typing.Optional[str] = None, + keyterm: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + mip_opt_out: typing.Optional[str] = None, + tag: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + authorization: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[AsyncV2SocketClient]: + """ + Real-time conversational speech recognition with contextual turn detection + for natural voice conversations + + Parameters + ---------- + model : str + + encoding : typing.Optional[str] + + sample_rate : typing.Optional[str] + + eager_eot_threshold : typing.Optional[str] + + eot_threshold : typing.Optional[str] + + eot_timeout_ms : typing.Optional[str] + + keyterm : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Keyterm prompting can improve recognition of specialized terminology. Pass a single string or a list of strings. + + mip_opt_out : typing.Optional[str] + + tag : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Label your requests for the purpose of identification during usage reporting. Pass a single string or a list of strings. + + authorization : typing.Optional[str] + Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. + + **Example:** `token %DEEPGRAM_API_KEY%` or `bearer %DEEPGRAM_TOKEN%` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncV2SocketClient + """ + ws_url = self._raw_client._client_wrapper.get_environment().production + "/v2/listen" + _encoded_query_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + { + "model": model, + "encoding": encoding, + "sample_rate": sample_rate, + "eager_eot_threshold": eager_eot_threshold, + "eot_threshold": eot_threshold, + "eot_timeout_ms": eot_timeout_ms, + "keyterm": keyterm, + "mip_opt_out": mip_opt_out, + "tag": tag, + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + } + ) + ) + ) + if _encoded_query_params: + ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params) + headers = self._raw_client._client_wrapper.get_headers() + if authorization is not None: + headers["Authorization"] = str(authorization) + if request_options and "additional_headers" in request_options: + headers.update(request_options["additional_headers"]) + try: + async with websockets_client_connect(ws_url, extra_headers=headers) as protocol: + yield AsyncV2SocketClient(websocket=protocol) + except InvalidWebSocketStatus as exc: + status_code: int = get_status_code(exc) + if status_code == 401: + raise ApiError( + status_code=status_code, + headers=dict(headers), + body="Websocket initialized with invalid credentials.", + ) + raise ApiError( + status_code=status_code, + headers=dict(headers), + body="Unexpected error when initializing websocket connection.", + ) diff --git a/src/deepgram/listen/v2/raw_client.py b/src/deepgram/listen/v2/raw_client.py index 5b3819c9..2bba77a2 100644 --- a/src/deepgram/listen/v2/raw_client.py +++ b/src/deepgram/listen/v2/raw_client.py @@ -11,7 +11,16 @@ from ...core.query_encoder import encode_query from ...core.remove_none_from_dict import remove_none_from_dict from ...core.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code +from ...requests.listen_v2keyterm import ListenV2KeytermParams +from ...types.listen_v2eager_eot_threshold import ListenV2EagerEotThreshold +from ...types.listen_v2encoding import ListenV2Encoding +from ...types.listen_v2eot_threshold import ListenV2EotThreshold +from ...types.listen_v2eot_timeout_ms import ListenV2EotTimeoutMs +from ...types.listen_v2mip_opt_out import ListenV2MipOptOut +from ...types.listen_v2sample_rate import ListenV2SampleRate +from ...types.listen_v2tag import ListenV2Tag from .socket_client import AsyncV2SocketClient, V2SocketClient try: @@ -28,15 +37,14 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): def connect( self, *, - model: str, - encoding: typing.Optional[str] = None, - sample_rate: typing.Optional[str] = None, - eager_eot_threshold: typing.Optional[str] = None, - eot_threshold: typing.Optional[str] = None, - eot_timeout_ms: typing.Optional[str] = None, - keyterm: typing.Optional[str] = None, - mip_opt_out: typing.Optional[str] = None, - tag: typing.Optional[str] = None, + encoding: typing.Optional[ListenV2Encoding] = None, + sample_rate: typing.Optional[ListenV2SampleRate] = None, + eager_eot_threshold: typing.Optional[ListenV2EagerEotThreshold] = None, + eot_threshold: typing.Optional[ListenV2EotThreshold] = None, + eot_timeout_ms: typing.Optional[ListenV2EotTimeoutMs] = None, + keyterm: typing.Optional[ListenV2KeytermParams] = None, + mip_opt_out: typing.Optional[ListenV2MipOptOut] = None, + tag: typing.Optional[ListenV2Tag] = None, authorization: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> typing.Iterator[V2SocketClient]: @@ -46,23 +54,21 @@ def connect( Parameters ---------- - model : str + encoding : typing.Optional[ListenV2Encoding] - encoding : typing.Optional[str] + sample_rate : typing.Optional[ListenV2SampleRate] - sample_rate : typing.Optional[str] + eager_eot_threshold : typing.Optional[ListenV2EagerEotThreshold] - eager_eot_threshold : typing.Optional[str] + eot_threshold : typing.Optional[ListenV2EotThreshold] - eot_threshold : typing.Optional[str] + eot_timeout_ms : typing.Optional[ListenV2EotTimeoutMs] - eot_timeout_ms : typing.Optional[str] + keyterm : typing.Optional[ListenV2KeytermParams] - keyterm : typing.Optional[str] + mip_opt_out : typing.Optional[ListenV2MipOptOut] - mip_opt_out : typing.Optional[str] - - tag : typing.Optional[str] + tag : typing.Optional[ListenV2Tag] authorization : typing.Optional[str] Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. @@ -81,13 +87,19 @@ def connect( jsonable_encoder( remove_none_from_dict( { - "model": model, + "model": "flux-general-en", "encoding": encoding, "sample_rate": sample_rate, "eager_eot_threshold": eager_eot_threshold, "eot_threshold": eot_threshold, "eot_timeout_ms": eot_timeout_ms, - "keyterm": keyterm, + "keyterm": convert_and_respect_annotation_metadata( + object_=convert_and_respect_annotation_metadata( + object_=keyterm, annotation=ListenV2KeytermParams, direction="write" + ), + annotation=ListenV2KeytermParams, + direction="write", + ), "mip_opt_out": mip_opt_out, "tag": tag, **( @@ -132,15 +144,14 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): async def connect( self, *, - model: str, - encoding: typing.Optional[str] = None, - sample_rate: typing.Optional[str] = None, - eager_eot_threshold: typing.Optional[str] = None, - eot_threshold: typing.Optional[str] = None, - eot_timeout_ms: typing.Optional[str] = None, - keyterm: typing.Optional[str] = None, - mip_opt_out: typing.Optional[str] = None, - tag: typing.Optional[str] = None, + encoding: typing.Optional[ListenV2Encoding] = None, + sample_rate: typing.Optional[ListenV2SampleRate] = None, + eager_eot_threshold: typing.Optional[ListenV2EagerEotThreshold] = None, + eot_threshold: typing.Optional[ListenV2EotThreshold] = None, + eot_timeout_ms: typing.Optional[ListenV2EotTimeoutMs] = None, + keyterm: typing.Optional[ListenV2KeytermParams] = None, + mip_opt_out: typing.Optional[ListenV2MipOptOut] = None, + tag: typing.Optional[ListenV2Tag] = None, authorization: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> typing.AsyncIterator[AsyncV2SocketClient]: @@ -150,23 +161,21 @@ async def connect( Parameters ---------- - model : str + encoding : typing.Optional[ListenV2Encoding] - encoding : typing.Optional[str] + sample_rate : typing.Optional[ListenV2SampleRate] - sample_rate : typing.Optional[str] + eager_eot_threshold : typing.Optional[ListenV2EagerEotThreshold] - eager_eot_threshold : typing.Optional[str] + eot_threshold : typing.Optional[ListenV2EotThreshold] - eot_threshold : typing.Optional[str] + eot_timeout_ms : typing.Optional[ListenV2EotTimeoutMs] - eot_timeout_ms : typing.Optional[str] + keyterm : typing.Optional[ListenV2KeytermParams] - keyterm : typing.Optional[str] + mip_opt_out : typing.Optional[ListenV2MipOptOut] - mip_opt_out : typing.Optional[str] - - tag : typing.Optional[str] + tag : typing.Optional[ListenV2Tag] authorization : typing.Optional[str] Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. @@ -185,13 +194,19 @@ async def connect( jsonable_encoder( remove_none_from_dict( { - "model": model, + "model": "flux-general-en", "encoding": encoding, "sample_rate": sample_rate, "eager_eot_threshold": eager_eot_threshold, "eot_threshold": eot_threshold, "eot_timeout_ms": eot_timeout_ms, - "keyterm": keyterm, + "keyterm": convert_and_respect_annotation_metadata( + object_=convert_and_respect_annotation_metadata( + object_=keyterm, annotation=ListenV2KeytermParams, direction="write" + ), + annotation=ListenV2KeytermParams, + direction="write", + ), "mip_opt_out": mip_opt_out, "tag": tag, **( diff --git a/src/deepgram/listen/v2/requests/__init__.py b/src/deepgram/listen/v2/requests/__init__.py index 96ce5ece..84e08faa 100644 --- a/src/deepgram/listen/v2/requests/__init__.py +++ b/src/deepgram/listen/v2/requests/__init__.py @@ -7,12 +7,22 @@ if typing.TYPE_CHECKING: from .listen_v2close_stream import ListenV2CloseStreamParams + from .listen_v2configure import ListenV2ConfigureParams + from .listen_v2configure_failure import ListenV2ConfigureFailureParams + from .listen_v2configure_success import ListenV2ConfigureSuccessParams + from .listen_v2configure_success_thresholds import ListenV2ConfigureSuccessThresholdsParams + from .listen_v2configure_thresholds import ListenV2ConfigureThresholdsParams from .listen_v2connected import ListenV2ConnectedParams from .listen_v2fatal_error import ListenV2FatalErrorParams from .listen_v2turn_info import ListenV2TurnInfoParams from .listen_v2turn_info_words_item import ListenV2TurnInfoWordsItemParams _dynamic_imports: typing.Dict[str, str] = { "ListenV2CloseStreamParams": ".listen_v2close_stream", + "ListenV2ConfigureFailureParams": ".listen_v2configure_failure", + "ListenV2ConfigureParams": ".listen_v2configure", + "ListenV2ConfigureSuccessParams": ".listen_v2configure_success", + "ListenV2ConfigureSuccessThresholdsParams": ".listen_v2configure_success_thresholds", + "ListenV2ConfigureThresholdsParams": ".listen_v2configure_thresholds", "ListenV2ConnectedParams": ".listen_v2connected", "ListenV2FatalErrorParams": ".listen_v2fatal_error", "ListenV2TurnInfoParams": ".listen_v2turn_info", @@ -43,6 +53,11 @@ def __dir__(): __all__ = [ "ListenV2CloseStreamParams", + "ListenV2ConfigureFailureParams", + "ListenV2ConfigureParams", + "ListenV2ConfigureSuccessParams", + "ListenV2ConfigureSuccessThresholdsParams", + "ListenV2ConfigureThresholdsParams", "ListenV2ConnectedParams", "ListenV2FatalErrorParams", "ListenV2TurnInfoParams", diff --git a/src/deepgram/listen/v2/requests/listen_v2configure.py b/src/deepgram/listen/v2/requests/listen_v2configure.py new file mode 100644 index 00000000..2685339a --- /dev/null +++ b/src/deepgram/listen/v2/requests/listen_v2configure.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import typing_extensions +from ....requests.listen_v2keyterm import ListenV2KeytermParams +from .listen_v2configure_thresholds import ListenV2ConfigureThresholdsParams + + +class ListenV2ConfigureParams(typing_extensions.TypedDict): + type: typing.Literal["Configure"] + """ + Message type identifier + """ + + thresholds: typing_extensions.NotRequired[ListenV2ConfigureThresholdsParams] + """ + Updates each parameter, if it is supplied. If a particular threshold parameter + is not supplied, the configuration continues using the currently configured value. + """ + + keyterms: typing_extensions.NotRequired[ListenV2KeytermParams] diff --git a/src/deepgram/listen/v2/requests/listen_v2configure_failure.py b/src/deepgram/listen/v2/requests/listen_v2configure_failure.py new file mode 100644 index 00000000..bfe53f86 --- /dev/null +++ b/src/deepgram/listen/v2/requests/listen_v2configure_failure.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import typing_extensions + + +class ListenV2ConfigureFailureParams(typing_extensions.TypedDict): + type: typing.Literal["ConfigureFailure"] + """ + Message type identifier + """ + + request_id: str + """ + The unique identifier of the request + """ + + sequence_id: float + """ + Starts at `0` and increments for each message the server sends + to the client. This includes messages of other types, like + `TurnInfo` messages. + """ diff --git a/src/deepgram/listen/v2/requests/listen_v2configure_success.py b/src/deepgram/listen/v2/requests/listen_v2configure_success.py new file mode 100644 index 00000000..92706d17 --- /dev/null +++ b/src/deepgram/listen/v2/requests/listen_v2configure_success.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import typing_extensions +from ....requests.listen_v2keyterm import ListenV2KeytermParams +from .listen_v2configure_success_thresholds import ListenV2ConfigureSuccessThresholdsParams + + +class ListenV2ConfigureSuccessParams(typing_extensions.TypedDict): + type: typing.Literal["ConfigureSuccess"] + """ + Message type identifier + """ + + request_id: str + """ + The unique identifier of the request + """ + + thresholds: ListenV2ConfigureSuccessThresholdsParams + """ + Updates each parameter, if it is supplied. If a particular threshold parameter + is not supplied, the configuration continues using the currently configured value. + """ + + keyterms: ListenV2KeytermParams + sequence_id: float + """ + Starts at `0` and increments for each message the server sends + to the client. This includes messages of other types, like + `TurnInfo` messages. + """ diff --git a/src/deepgram/listen/v2/requests/listen_v2configure_success_thresholds.py b/src/deepgram/listen/v2/requests/listen_v2configure_success_thresholds.py new file mode 100644 index 00000000..7a16cdf6 --- /dev/null +++ b/src/deepgram/listen/v2/requests/listen_v2configure_success_thresholds.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing_extensions +from ....types.listen_v2eager_eot_threshold import ListenV2EagerEotThreshold +from ....types.listen_v2eot_threshold import ListenV2EotThreshold +from ....types.listen_v2eot_timeout_ms import ListenV2EotTimeoutMs + + +class ListenV2ConfigureSuccessThresholdsParams(typing_extensions.TypedDict): + """ + Updates each parameter, if it is supplied. If a particular threshold parameter + is not supplied, the configuration continues using the currently configured value. + """ + + eager_eot_threshold: typing_extensions.NotRequired[ListenV2EagerEotThreshold] + eot_threshold: typing_extensions.NotRequired[ListenV2EotThreshold] + eot_timeout_ms: typing_extensions.NotRequired[ListenV2EotTimeoutMs] diff --git a/src/deepgram/listen/v2/requests/listen_v2configure_thresholds.py b/src/deepgram/listen/v2/requests/listen_v2configure_thresholds.py new file mode 100644 index 00000000..4f81602f --- /dev/null +++ b/src/deepgram/listen/v2/requests/listen_v2configure_thresholds.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing_extensions +from ....types.listen_v2eager_eot_threshold import ListenV2EagerEotThreshold +from ....types.listen_v2eot_threshold import ListenV2EotThreshold +from ....types.listen_v2eot_timeout_ms import ListenV2EotTimeoutMs + + +class ListenV2ConfigureThresholdsParams(typing_extensions.TypedDict): + """ + Updates each parameter, if it is supplied. If a particular threshold parameter + is not supplied, the configuration continues using the currently configured value. + """ + + eager_eot_threshold: typing_extensions.NotRequired[ListenV2EagerEotThreshold] + eot_threshold: typing_extensions.NotRequired[ListenV2EotThreshold] + eot_timeout_ms: typing_extensions.NotRequired[ListenV2EotTimeoutMs] diff --git a/src/deepgram/listen/v2/socket_client.py b/src/deepgram/listen/v2/socket_client.py index 136f7e74..e22cfe2d 100644 --- a/src/deepgram/listen/v2/socket_client.py +++ b/src/deepgram/listen/v2/socket_client.py @@ -1,14 +1,18 @@ # This file was auto-generated by Fern from our API Definition. import json +import logging import typing from json.decoder import JSONDecodeError import websockets import websockets.sync.connection as websockets_sync_connection from ...core.events import EventEmitterMixin, EventType -from ...core.unchecked_base_model import construct_type +from ...core.pydantic_utilities import parse_obj_as from .types.listen_v2close_stream import ListenV2CloseStream +from .types.listen_v2configure import ListenV2Configure +from .types.listen_v2configure_failure import ListenV2ConfigureFailure +from .types.listen_v2configure_success import ListenV2ConfigureSuccess from .types.listen_v2connected import ListenV2Connected from .types.listen_v2fatal_error import ListenV2FatalError from .types.listen_v2turn_info import ListenV2TurnInfo @@ -18,7 +22,10 @@ except ImportError: from websockets import WebSocketClientProtocol # type: ignore -V2SocketClientResponse = typing.Union[ListenV2Connected, ListenV2TurnInfo, ListenV2FatalError] +_logger = logging.getLogger(__name__) +V2SocketClientResponse = typing.Union[ + ListenV2Connected, ListenV2TurnInfo, ListenV2ConfigureSuccess, ListenV2ConfigureFailure, ListenV2FatalError +] class AsyncV2SocketClient(EventEmitterMixin): @@ -31,7 +38,13 @@ async def __aiter__(self): if isinstance(message, bytes): yield message else: - yield construct_type(type_=V2SocketClientResponse, object_=json.loads(message)) # type: ignore + try: + yield parse_obj_as(V2SocketClientResponse, json.loads(message)) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue async def start_listening(self): """ @@ -50,9 +63,15 @@ async def start_listening(self): parsed = raw_message else: json_data = json.loads(raw_message) - parsed = construct_type(type_=V2SocketClientResponse, object_=json_data) # type: ignore + try: + parsed = parse_obj_as(V2SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue await self._emit_async(EventType.MESSAGE, parsed) - except Exception as exc: + except (websockets.WebSocketException, JSONDecodeError) as exc: await self._emit_async(EventType.ERROR, exc) finally: await self._emit_async(EventType.CLOSE, None) @@ -64,12 +83,19 @@ async def send_media(self, message: bytes) -> None: """ await self._send(message) - async def send_close_stream(self, message: typing.Optional[ListenV2CloseStream] = None) -> None: + async def send_close_stream(self, message: ListenV2CloseStream) -> None: """ Send a message to the websocket connection. The message will be sent as a ListenV2CloseStream. """ - await self._send_model(message or ListenV2CloseStream(type="CloseStream")) + await self._send_model(message) + + async def send_listen_v_2_configure(self, message: ListenV2Configure) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a ListenV2Configure. + """ + await self._send_model(message) async def recv(self) -> V2SocketClientResponse: """ @@ -79,7 +105,11 @@ async def recv(self) -> V2SocketClientResponse: if isinstance(data, bytes): return data # type: ignore json_data = json.loads(data) - return construct_type(type_=V2SocketClientResponse, object_=json_data) # type: ignore + try: + return parse_obj_as(V2SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.") + return json_data # type: ignore async def _send(self, data: typing.Any) -> None: """ @@ -106,7 +136,13 @@ def __iter__(self): if isinstance(message, bytes): yield message else: - yield construct_type(type_=V2SocketClientResponse, object_=json.loads(message)) # type: ignore + try: + yield parse_obj_as(V2SocketClientResponse, json.loads(message)) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue def start_listening(self): """ @@ -125,9 +161,15 @@ def start_listening(self): parsed = raw_message else: json_data = json.loads(raw_message) - parsed = construct_type(type_=V2SocketClientResponse, object_=json_data) # type: ignore + try: + parsed = parse_obj_as(V2SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue self._emit(EventType.MESSAGE, parsed) - except Exception as exc: + except (websockets.WebSocketException, JSONDecodeError) as exc: self._emit(EventType.ERROR, exc) finally: self._emit(EventType.CLOSE, None) @@ -139,12 +181,19 @@ def send_media(self, message: bytes) -> None: """ self._send(message) - def send_close_stream(self, message: typing.Optional[ListenV2CloseStream] = None) -> None: + def send_close_stream(self, message: ListenV2CloseStream) -> None: """ Send a message to the websocket connection. The message will be sent as a ListenV2CloseStream. """ - self._send_model(message or ListenV2CloseStream(type="CloseStream")) + self._send_model(message) + + def send_listen_v_2_configure(self, message: ListenV2Configure) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a ListenV2Configure. + """ + self._send_model(message) def recv(self) -> V2SocketClientResponse: """ @@ -154,7 +203,11 @@ def recv(self) -> V2SocketClientResponse: if isinstance(data, bytes): return data # type: ignore json_data = json.loads(data) - return construct_type(type_=V2SocketClientResponse, object_=json_data) # type: ignore + try: + return parse_obj_as(V2SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.") + return json_data # type: ignore def _send(self, data: typing.Any) -> None: """ diff --git a/src/deepgram/listen/v2/socket_client.py.bak b/src/deepgram/listen/v2/socket_client.py.bak new file mode 100644 index 00000000..136f7e74 --- /dev/null +++ b/src/deepgram/listen/v2/socket_client.py.bak @@ -0,0 +1,171 @@ +# This file was auto-generated by Fern from our API Definition. + +import json +import typing +from json.decoder import JSONDecodeError + +import websockets +import websockets.sync.connection as websockets_sync_connection +from ...core.events import EventEmitterMixin, EventType +from ...core.unchecked_base_model import construct_type +from .types.listen_v2close_stream import ListenV2CloseStream +from .types.listen_v2connected import ListenV2Connected +from .types.listen_v2fatal_error import ListenV2FatalError +from .types.listen_v2turn_info import ListenV2TurnInfo + +try: + from websockets.legacy.client import WebSocketClientProtocol # type: ignore +except ImportError: + from websockets import WebSocketClientProtocol # type: ignore + +V2SocketClientResponse = typing.Union[ListenV2Connected, ListenV2TurnInfo, ListenV2FatalError] + + +class AsyncV2SocketClient(EventEmitterMixin): + def __init__(self, *, websocket: WebSocketClientProtocol): + super().__init__() + self._websocket = websocket + + async def __aiter__(self): + async for message in self._websocket: + if isinstance(message, bytes): + yield message + else: + yield construct_type(type_=V2SocketClientResponse, object_=json.loads(message)) # type: ignore + + async def start_listening(self): + """ + Start listening for messages on the websocket connection. + + Emits events in the following order: + - EventType.OPEN when connection is established + - EventType.MESSAGE for each message received + - EventType.ERROR if an error occurs + - EventType.CLOSE when connection is closed + """ + await self._emit_async(EventType.OPEN, None) + try: + async for raw_message in self._websocket: + if isinstance(raw_message, bytes): + parsed = raw_message + else: + json_data = json.loads(raw_message) + parsed = construct_type(type_=V2SocketClientResponse, object_=json_data) # type: ignore + await self._emit_async(EventType.MESSAGE, parsed) + except Exception as exc: + await self._emit_async(EventType.ERROR, exc) + finally: + await self._emit_async(EventType.CLOSE, None) + + async def send_media(self, message: bytes) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a bytes. + """ + await self._send(message) + + async def send_close_stream(self, message: typing.Optional[ListenV2CloseStream] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a ListenV2CloseStream. + """ + await self._send_model(message or ListenV2CloseStream(type="CloseStream")) + + async def recv(self) -> V2SocketClientResponse: + """ + Receive a message from the websocket connection. + """ + data = await self._websocket.recv() + if isinstance(data, bytes): + return data # type: ignore + json_data = json.loads(data) + return construct_type(type_=V2SocketClientResponse, object_=json_data) # type: ignore + + async def _send(self, data: typing.Any) -> None: + """ + Send a message to the websocket connection. + """ + if isinstance(data, dict): + data = json.dumps(data) + await self._websocket.send(data) + + async def _send_model(self, data: typing.Any) -> None: + """ + Send a Pydantic model to the websocket connection. + """ + await self._send(data.dict()) + + +class V2SocketClient(EventEmitterMixin): + def __init__(self, *, websocket: websockets_sync_connection.Connection): + super().__init__() + self._websocket = websocket + + def __iter__(self): + for message in self._websocket: + if isinstance(message, bytes): + yield message + else: + yield construct_type(type_=V2SocketClientResponse, object_=json.loads(message)) # type: ignore + + def start_listening(self): + """ + Start listening for messages on the websocket connection. + + Emits events in the following order: + - EventType.OPEN when connection is established + - EventType.MESSAGE for each message received + - EventType.ERROR if an error occurs + - EventType.CLOSE when connection is closed + """ + self._emit(EventType.OPEN, None) + try: + for raw_message in self._websocket: + if isinstance(raw_message, bytes): + parsed = raw_message + else: + json_data = json.loads(raw_message) + parsed = construct_type(type_=V2SocketClientResponse, object_=json_data) # type: ignore + self._emit(EventType.MESSAGE, parsed) + except Exception as exc: + self._emit(EventType.ERROR, exc) + finally: + self._emit(EventType.CLOSE, None) + + def send_media(self, message: bytes) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a bytes. + """ + self._send(message) + + def send_close_stream(self, message: typing.Optional[ListenV2CloseStream] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a ListenV2CloseStream. + """ + self._send_model(message or ListenV2CloseStream(type="CloseStream")) + + def recv(self) -> V2SocketClientResponse: + """ + Receive a message from the websocket connection. + """ + data = self._websocket.recv() + if isinstance(data, bytes): + return data # type: ignore + json_data = json.loads(data) + return construct_type(type_=V2SocketClientResponse, object_=json_data) # type: ignore + + def _send(self, data: typing.Any) -> None: + """ + Send a message to the websocket connection. + """ + if isinstance(data, dict): + data = json.dumps(data) + self._websocket.send(data) + + def _send_model(self, data: typing.Any) -> None: + """ + Send a Pydantic model to the websocket connection. + """ + self._send(data.dict()) diff --git a/src/deepgram/listen/v2/types/__init__.py b/src/deepgram/listen/v2/types/__init__.py index 229417bf..15cc6fba 100644 --- a/src/deepgram/listen/v2/types/__init__.py +++ b/src/deepgram/listen/v2/types/__init__.py @@ -8,6 +8,11 @@ if typing.TYPE_CHECKING: from .listen_v2close_stream import ListenV2CloseStream from .listen_v2close_stream_type import ListenV2CloseStreamType + from .listen_v2configure import ListenV2Configure + from .listen_v2configure_failure import ListenV2ConfigureFailure + from .listen_v2configure_success import ListenV2ConfigureSuccess + from .listen_v2configure_success_thresholds import ListenV2ConfigureSuccessThresholds + from .listen_v2configure_thresholds import ListenV2ConfigureThresholds from .listen_v2connected import ListenV2Connected from .listen_v2fatal_error import ListenV2FatalError from .listen_v2turn_info import ListenV2TurnInfo @@ -16,6 +21,11 @@ _dynamic_imports: typing.Dict[str, str] = { "ListenV2CloseStream": ".listen_v2close_stream", "ListenV2CloseStreamType": ".listen_v2close_stream_type", + "ListenV2Configure": ".listen_v2configure", + "ListenV2ConfigureFailure": ".listen_v2configure_failure", + "ListenV2ConfigureSuccess": ".listen_v2configure_success", + "ListenV2ConfigureSuccessThresholds": ".listen_v2configure_success_thresholds", + "ListenV2ConfigureThresholds": ".listen_v2configure_thresholds", "ListenV2Connected": ".listen_v2connected", "ListenV2FatalError": ".listen_v2fatal_error", "ListenV2TurnInfo": ".listen_v2turn_info", @@ -48,6 +58,11 @@ def __dir__(): __all__ = [ "ListenV2CloseStream", "ListenV2CloseStreamType", + "ListenV2Configure", + "ListenV2ConfigureFailure", + "ListenV2ConfigureSuccess", + "ListenV2ConfigureSuccessThresholds", + "ListenV2ConfigureThresholds", "ListenV2Connected", "ListenV2FatalError", "ListenV2TurnInfo", diff --git a/src/deepgram/listen/v2/types/listen_v2configure.py b/src/deepgram/listen/v2/types/listen_v2configure.py new file mode 100644 index 00000000..e79207eb --- /dev/null +++ b/src/deepgram/listen/v2/types/listen_v2configure.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ....types.listen_v2keyterm import ListenV2Keyterm +from .listen_v2configure_thresholds import ListenV2ConfigureThresholds + + +class ListenV2Configure(UncheckedBaseModel): + type: typing.Literal["Configure"] = pydantic.Field(default="Configure") + """ + Message type identifier + """ + + thresholds: typing.Optional[ListenV2ConfigureThresholds] = pydantic.Field(default=None) + """ + Updates each parameter, if it is supplied. If a particular threshold parameter + is not supplied, the configuration continues using the currently configured value. + """ + + keyterms: typing.Optional[ListenV2Keyterm] = 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/deepgram/listen/v2/types/listen_v2configure_failure.py b/src/deepgram/listen/v2/types/listen_v2configure_failure.py new file mode 100644 index 00000000..b00fc6b1 --- /dev/null +++ b/src/deepgram/listen/v2/types/listen_v2configure_failure.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class ListenV2ConfigureFailure(UncheckedBaseModel): + type: typing.Literal["ConfigureFailure"] = pydantic.Field(default="ConfigureFailure") + """ + Message type identifier + """ + + request_id: str = pydantic.Field() + """ + The unique identifier of the request + """ + + sequence_id: float = pydantic.Field() + """ + Starts at `0` and increments for each message the server sends + to the client. This includes messages of other types, like + `TurnInfo` messages. + """ + + 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/deepgram/listen/v2/types/listen_v2configure_success.py b/src/deepgram/listen/v2/types/listen_v2configure_success.py new file mode 100644 index 00000000..757f1a32 --- /dev/null +++ b/src/deepgram/listen/v2/types/listen_v2configure_success.py @@ -0,0 +1,44 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ....types.listen_v2keyterm import ListenV2Keyterm +from .listen_v2configure_success_thresholds import ListenV2ConfigureSuccessThresholds + + +class ListenV2ConfigureSuccess(UncheckedBaseModel): + type: typing.Literal["ConfigureSuccess"] = pydantic.Field(default="ConfigureSuccess") + """ + Message type identifier + """ + + request_id: str = pydantic.Field() + """ + The unique identifier of the request + """ + + thresholds: ListenV2ConfigureSuccessThresholds = pydantic.Field() + """ + Updates each parameter, if it is supplied. If a particular threshold parameter + is not supplied, the configuration continues using the currently configured value. + """ + + keyterms: ListenV2Keyterm + sequence_id: float = pydantic.Field() + """ + Starts at `0` and increments for each message the server sends + to the client. This includes messages of other types, like + `TurnInfo` messages. + """ + + 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/deepgram/listen/v2/types/listen_v2configure_success_thresholds.py b/src/deepgram/listen/v2/types/listen_v2configure_success_thresholds.py new file mode 100644 index 00000000..6c719233 --- /dev/null +++ b/src/deepgram/listen/v2/types/listen_v2configure_success_thresholds.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ....types.listen_v2eager_eot_threshold import ListenV2EagerEotThreshold +from ....types.listen_v2eot_threshold import ListenV2EotThreshold +from ....types.listen_v2eot_timeout_ms import ListenV2EotTimeoutMs + + +class ListenV2ConfigureSuccessThresholds(UncheckedBaseModel): + """ + Updates each parameter, if it is supplied. If a particular threshold parameter + is not supplied, the configuration continues using the currently configured value. + """ + + eager_eot_threshold: typing.Optional[ListenV2EagerEotThreshold] = None + eot_threshold: typing.Optional[ListenV2EotThreshold] = None + eot_timeout_ms: typing.Optional[ListenV2EotTimeoutMs] = 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/deepgram/listen/v2/types/listen_v2configure_thresholds.py b/src/deepgram/listen/v2/types/listen_v2configure_thresholds.py new file mode 100644 index 00000000..b992c5d8 --- /dev/null +++ b/src/deepgram/listen/v2/types/listen_v2configure_thresholds.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ....types.listen_v2eager_eot_threshold import ListenV2EagerEotThreshold +from ....types.listen_v2eot_threshold import ListenV2EotThreshold +from ....types.listen_v2eot_timeout_ms import ListenV2EotTimeoutMs + + +class ListenV2ConfigureThresholds(UncheckedBaseModel): + """ + Updates each parameter, if it is supplied. If a particular threshold parameter + is not supplied, the configuration continues using the currently configured value. + """ + + eager_eot_threshold: typing.Optional[ListenV2EagerEotThreshold] = None + eot_threshold: typing.Optional[ListenV2EotThreshold] = None + eot_timeout_ms: typing.Optional[ListenV2EotTimeoutMs] = 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/deepgram/speak/v1/client.py b/src/deepgram/speak/v1/client.py index 1780af66..2323204d 100644 --- a/src/deepgram/speak/v1/client.py +++ b/src/deepgram/speak/v1/client.py @@ -14,6 +14,10 @@ from ...core.remove_none_from_dict import remove_none_from_dict from ...core.request_options import RequestOptions from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code +from ...types.speak_v1encoding import SpeakV1Encoding +from ...types.speak_v1mip_opt_out import SpeakV1MipOptOut +from ...types.speak_v1model import SpeakV1Model +from ...types.speak_v1sample_rate import SpeakV1SampleRate from .raw_client import AsyncRawV1Client, RawV1Client from .socket_client import AsyncV1SocketClient, V1SocketClient @@ -47,10 +51,10 @@ def with_raw_response(self) -> RawV1Client: def connect( self, *, - encoding: typing.Optional[str] = None, - mip_opt_out: typing.Optional[str] = None, - model: typing.Optional[str] = None, - sample_rate: typing.Optional[str] = None, + encoding: typing.Optional[SpeakV1Encoding] = None, + mip_opt_out: typing.Optional[SpeakV1MipOptOut] = None, + model: typing.Optional[SpeakV1Model] = None, + sample_rate: typing.Optional[SpeakV1SampleRate] = None, authorization: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> typing.Iterator[V1SocketClient]: @@ -59,13 +63,13 @@ def connect( Parameters ---------- - encoding : typing.Optional[str] + encoding : typing.Optional[SpeakV1Encoding] - mip_opt_out : typing.Optional[str] + mip_opt_out : typing.Optional[SpeakV1MipOptOut] - model : typing.Optional[str] + model : typing.Optional[SpeakV1Model] - sample_rate : typing.Optional[str] + sample_rate : typing.Optional[SpeakV1SampleRate] authorization : typing.Optional[str] Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. @@ -151,10 +155,10 @@ def with_raw_response(self) -> AsyncRawV1Client: async def connect( self, *, - encoding: typing.Optional[str] = None, - mip_opt_out: typing.Optional[str] = None, - model: typing.Optional[str] = None, - sample_rate: typing.Optional[str] = None, + encoding: typing.Optional[SpeakV1Encoding] = None, + mip_opt_out: typing.Optional[SpeakV1MipOptOut] = None, + model: typing.Optional[SpeakV1Model] = None, + sample_rate: typing.Optional[SpeakV1SampleRate] = None, authorization: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> typing.AsyncIterator[AsyncV1SocketClient]: @@ -163,13 +167,13 @@ async def connect( Parameters ---------- - encoding : typing.Optional[str] + encoding : typing.Optional[SpeakV1Encoding] - mip_opt_out : typing.Optional[str] + mip_opt_out : typing.Optional[SpeakV1MipOptOut] - model : typing.Optional[str] + model : typing.Optional[SpeakV1Model] - sample_rate : typing.Optional[str] + sample_rate : typing.Optional[SpeakV1SampleRate] authorization : typing.Optional[str] Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. diff --git a/src/deepgram/speak/v1/raw_client.py b/src/deepgram/speak/v1/raw_client.py index 0ade1093..5cf2e2d0 100644 --- a/src/deepgram/speak/v1/raw_client.py +++ b/src/deepgram/speak/v1/raw_client.py @@ -12,6 +12,10 @@ from ...core.remove_none_from_dict import remove_none_from_dict from ...core.request_options import RequestOptions from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code +from ...types.speak_v1encoding import SpeakV1Encoding +from ...types.speak_v1mip_opt_out import SpeakV1MipOptOut +from ...types.speak_v1model import SpeakV1Model +from ...types.speak_v1sample_rate import SpeakV1SampleRate from .socket_client import AsyncV1SocketClient, V1SocketClient try: @@ -28,10 +32,10 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): def connect( self, *, - encoding: typing.Optional[str] = None, - mip_opt_out: typing.Optional[str] = None, - model: typing.Optional[str] = None, - sample_rate: typing.Optional[str] = None, + encoding: typing.Optional[SpeakV1Encoding] = None, + mip_opt_out: typing.Optional[SpeakV1MipOptOut] = None, + model: typing.Optional[SpeakV1Model] = None, + sample_rate: typing.Optional[SpeakV1SampleRate] = None, authorization: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> typing.Iterator[V1SocketClient]: @@ -40,13 +44,13 @@ def connect( Parameters ---------- - encoding : typing.Optional[str] + encoding : typing.Optional[SpeakV1Encoding] - mip_opt_out : typing.Optional[str] + mip_opt_out : typing.Optional[SpeakV1MipOptOut] - model : typing.Optional[str] + model : typing.Optional[SpeakV1Model] - sample_rate : typing.Optional[str] + sample_rate : typing.Optional[SpeakV1SampleRate] authorization : typing.Optional[str] Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. @@ -111,10 +115,10 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): async def connect( self, *, - encoding: typing.Optional[str] = None, - mip_opt_out: typing.Optional[str] = None, - model: typing.Optional[str] = None, - sample_rate: typing.Optional[str] = None, + encoding: typing.Optional[SpeakV1Encoding] = None, + mip_opt_out: typing.Optional[SpeakV1MipOptOut] = None, + model: typing.Optional[SpeakV1Model] = None, + sample_rate: typing.Optional[SpeakV1SampleRate] = None, authorization: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> typing.AsyncIterator[AsyncV1SocketClient]: @@ -123,13 +127,13 @@ async def connect( Parameters ---------- - encoding : typing.Optional[str] + encoding : typing.Optional[SpeakV1Encoding] - mip_opt_out : typing.Optional[str] + mip_opt_out : typing.Optional[SpeakV1MipOptOut] - model : typing.Optional[str] + model : typing.Optional[SpeakV1Model] - sample_rate : typing.Optional[str] + sample_rate : typing.Optional[SpeakV1SampleRate] authorization : typing.Optional[str] Use your API key for authentication, or alternatively generate a [temporary token](/guides/fundamentals/token-based-authentication) and pass it via the `token` query parameter. diff --git a/src/deepgram/speak/v1/requests/speak_v1metadata.py b/src/deepgram/speak/v1/requests/speak_v1metadata.py index 89fb6809..08553952 100644 --- a/src/deepgram/speak/v1/requests/speak_v1metadata.py +++ b/src/deepgram/speak/v1/requests/speak_v1metadata.py @@ -23,10 +23,15 @@ class SpeakV1MetadataParams(typing_extensions.TypedDict): model_version: str """ - Version of the model being used + Version of the primary model being used """ model_uuid: str """ - Unique identifier for the model + Unique identifier for the primary model used + """ + + additional_model_uuids: typing_extensions.NotRequired[typing.Sequence[str]] + """ + List of unique identifiers for any additional models used to serve the request """ diff --git a/src/deepgram/speak/v1/socket_client.py b/src/deepgram/speak/v1/socket_client.py index 9f640575..436db61b 100644 --- a/src/deepgram/speak/v1/socket_client.py +++ b/src/deepgram/speak/v1/socket_client.py @@ -1,13 +1,14 @@ # This file was auto-generated by Fern from our API Definition. import json +import logging import typing from json.decoder import JSONDecodeError import websockets import websockets.sync.connection as websockets_sync_connection from ...core.events import EventEmitterMixin, EventType -from ...core.unchecked_base_model import construct_type +from ...core.pydantic_utilities import parse_obj_as from .types.speak_v1clear import SpeakV1Clear from .types.speak_v1cleared import SpeakV1Cleared from .types.speak_v1close import SpeakV1Close @@ -22,6 +23,7 @@ except ImportError: from websockets import WebSocketClientProtocol # type: ignore +_logger = logging.getLogger(__name__) V1SocketClientResponse = typing.Union[bytes, SpeakV1Metadata, SpeakV1Flushed, SpeakV1Cleared, SpeakV1Warning] @@ -35,7 +37,13 @@ async def __aiter__(self): if isinstance(message, bytes): yield message else: - yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore + try: + yield parse_obj_as(V1SocketClientResponse, json.loads(message)) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue async def start_listening(self): """ @@ -54,9 +62,15 @@ async def start_listening(self): parsed = raw_message else: json_data = json.loads(raw_message) - parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + try: + parsed = parse_obj_as(V1SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue await self._emit_async(EventType.MESSAGE, parsed) - except Exception as exc: + except (websockets.WebSocketException, JSONDecodeError) as exc: await self._emit_async(EventType.ERROR, exc) finally: await self._emit_async(EventType.CLOSE, None) @@ -68,26 +82,26 @@ async def send_text(self, message: SpeakV1Text) -> None: """ await self._send_model(message) - async def send_flush(self, message: typing.Optional[SpeakV1Flush] = None) -> None: + async def send_flush(self, message: SpeakV1Flush) -> None: """ Send a message to the websocket connection. The message will be sent as a SpeakV1Flush. """ - await self._send_model(message or SpeakV1Flush(type="Flush")) + await self._send_model(message) - async def send_clear(self, message: typing.Optional[SpeakV1Clear] = None) -> None: + async def send_clear(self, message: SpeakV1Clear) -> None: """ Send a message to the websocket connection. The message will be sent as a SpeakV1Clear. """ - await self._send_model(message or SpeakV1Clear(type="Clear")) + await self._send_model(message) - async def send_close(self, message: typing.Optional[SpeakV1Close] = None) -> None: + async def send_close(self, message: SpeakV1Close) -> None: """ Send a message to the websocket connection. The message will be sent as a SpeakV1Close. """ - await self._send_model(message or SpeakV1Close(type="Close")) + await self._send_model(message) async def recv(self) -> V1SocketClientResponse: """ @@ -97,7 +111,11 @@ async def recv(self) -> V1SocketClientResponse: if isinstance(data, bytes): return data # type: ignore json_data = json.loads(data) - return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + try: + return parse_obj_as(V1SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.") + return json_data # type: ignore async def _send(self, data: typing.Any) -> None: """ @@ -124,7 +142,13 @@ def __iter__(self): if isinstance(message, bytes): yield message else: - yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore + try: + yield parse_obj_as(V1SocketClientResponse, json.loads(message)) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue def start_listening(self): """ @@ -143,9 +167,15 @@ def start_listening(self): parsed = raw_message else: json_data = json.loads(raw_message) - parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + try: + parsed = parse_obj_as(V1SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning( + "Skipping unknown WebSocket message; update your SDK version to support new message types." + ) + continue self._emit(EventType.MESSAGE, parsed) - except Exception as exc: + except (websockets.WebSocketException, JSONDecodeError) as exc: self._emit(EventType.ERROR, exc) finally: self._emit(EventType.CLOSE, None) @@ -157,26 +187,26 @@ def send_text(self, message: SpeakV1Text) -> None: """ self._send_model(message) - def send_flush(self, message: typing.Optional[SpeakV1Flush] = None) -> None: + def send_flush(self, message: SpeakV1Flush) -> None: """ Send a message to the websocket connection. The message will be sent as a SpeakV1Flush. """ - self._send_model(message or SpeakV1Flush(type="Flush")) + self._send_model(message) - def send_clear(self, message: typing.Optional[SpeakV1Clear] = None) -> None: + def send_clear(self, message: SpeakV1Clear) -> None: """ Send a message to the websocket connection. The message will be sent as a SpeakV1Clear. """ - self._send_model(message or SpeakV1Clear(type="Clear")) + self._send_model(message) - def send_close(self, message: typing.Optional[SpeakV1Close] = None) -> None: + def send_close(self, message: SpeakV1Close) -> None: """ Send a message to the websocket connection. The message will be sent as a SpeakV1Close. """ - self._send_model(message or SpeakV1Close(type="Close")) + self._send_model(message) def recv(self) -> V1SocketClientResponse: """ @@ -186,7 +216,11 @@ def recv(self) -> V1SocketClientResponse: if isinstance(data, bytes): return data # type: ignore json_data = json.loads(data) - return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + try: + return parse_obj_as(V1SocketClientResponse, json_data) # type: ignore + except Exception: + _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.") + return json_data # type: ignore def _send(self, data: typing.Any) -> None: """ diff --git a/src/deepgram/speak/v1/socket_client.py.bak b/src/deepgram/speak/v1/socket_client.py.bak new file mode 100644 index 00000000..9f640575 --- /dev/null +++ b/src/deepgram/speak/v1/socket_client.py.bak @@ -0,0 +1,203 @@ +# This file was auto-generated by Fern from our API Definition. + +import json +import typing +from json.decoder import JSONDecodeError + +import websockets +import websockets.sync.connection as websockets_sync_connection +from ...core.events import EventEmitterMixin, EventType +from ...core.unchecked_base_model import construct_type +from .types.speak_v1clear import SpeakV1Clear +from .types.speak_v1cleared import SpeakV1Cleared +from .types.speak_v1close import SpeakV1Close +from .types.speak_v1flush import SpeakV1Flush +from .types.speak_v1flushed import SpeakV1Flushed +from .types.speak_v1metadata import SpeakV1Metadata +from .types.speak_v1text import SpeakV1Text +from .types.speak_v1warning import SpeakV1Warning + +try: + from websockets.legacy.client import WebSocketClientProtocol # type: ignore +except ImportError: + from websockets import WebSocketClientProtocol # type: ignore + +V1SocketClientResponse = typing.Union[bytes, SpeakV1Metadata, SpeakV1Flushed, SpeakV1Cleared, SpeakV1Warning] + + +class AsyncV1SocketClient(EventEmitterMixin): + def __init__(self, *, websocket: WebSocketClientProtocol): + super().__init__() + self._websocket = websocket + + async def __aiter__(self): + async for message in self._websocket: + if isinstance(message, bytes): + yield message + else: + yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore + + async def start_listening(self): + """ + Start listening for messages on the websocket connection. + + Emits events in the following order: + - EventType.OPEN when connection is established + - EventType.MESSAGE for each message received + - EventType.ERROR if an error occurs + - EventType.CLOSE when connection is closed + """ + await self._emit_async(EventType.OPEN, None) + try: + async for raw_message in self._websocket: + if isinstance(raw_message, bytes): + parsed = raw_message + else: + json_data = json.loads(raw_message) + parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + await self._emit_async(EventType.MESSAGE, parsed) + except Exception as exc: + await self._emit_async(EventType.ERROR, exc) + finally: + await self._emit_async(EventType.CLOSE, None) + + async def send_text(self, message: SpeakV1Text) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a SpeakV1Text. + """ + await self._send_model(message) + + async def send_flush(self, message: typing.Optional[SpeakV1Flush] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a SpeakV1Flush. + """ + await self._send_model(message or SpeakV1Flush(type="Flush")) + + async def send_clear(self, message: typing.Optional[SpeakV1Clear] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a SpeakV1Clear. + """ + await self._send_model(message or SpeakV1Clear(type="Clear")) + + async def send_close(self, message: typing.Optional[SpeakV1Close] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a SpeakV1Close. + """ + await self._send_model(message or SpeakV1Close(type="Close")) + + async def recv(self) -> V1SocketClientResponse: + """ + Receive a message from the websocket connection. + """ + data = await self._websocket.recv() + if isinstance(data, bytes): + return data # type: ignore + json_data = json.loads(data) + return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + + async def _send(self, data: typing.Any) -> None: + """ + Send a message to the websocket connection. + """ + if isinstance(data, dict): + data = json.dumps(data) + await self._websocket.send(data) + + async def _send_model(self, data: typing.Any) -> None: + """ + Send a Pydantic model to the websocket connection. + """ + await self._send(data.dict()) + + +class V1SocketClient(EventEmitterMixin): + def __init__(self, *, websocket: websockets_sync_connection.Connection): + super().__init__() + self._websocket = websocket + + def __iter__(self): + for message in self._websocket: + if isinstance(message, bytes): + yield message + else: + yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore + + def start_listening(self): + """ + Start listening for messages on the websocket connection. + + Emits events in the following order: + - EventType.OPEN when connection is established + - EventType.MESSAGE for each message received + - EventType.ERROR if an error occurs + - EventType.CLOSE when connection is closed + """ + self._emit(EventType.OPEN, None) + try: + for raw_message in self._websocket: + if isinstance(raw_message, bytes): + parsed = raw_message + else: + json_data = json.loads(raw_message) + parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + self._emit(EventType.MESSAGE, parsed) + except Exception as exc: + self._emit(EventType.ERROR, exc) + finally: + self._emit(EventType.CLOSE, None) + + def send_text(self, message: SpeakV1Text) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a SpeakV1Text. + """ + self._send_model(message) + + def send_flush(self, message: typing.Optional[SpeakV1Flush] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a SpeakV1Flush. + """ + self._send_model(message or SpeakV1Flush(type="Flush")) + + def send_clear(self, message: typing.Optional[SpeakV1Clear] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a SpeakV1Clear. + """ + self._send_model(message or SpeakV1Clear(type="Clear")) + + def send_close(self, message: typing.Optional[SpeakV1Close] = None) -> None: + """ + Send a message to the websocket connection. + The message will be sent as a SpeakV1Close. + """ + self._send_model(message or SpeakV1Close(type="Close")) + + def recv(self) -> V1SocketClientResponse: + """ + Receive a message from the websocket connection. + """ + data = self._websocket.recv() + if isinstance(data, bytes): + return data # type: ignore + json_data = json.loads(data) + return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore + + def _send(self, data: typing.Any) -> None: + """ + Send a message to the websocket connection. + """ + if isinstance(data, dict): + data = json.dumps(data) + self._websocket.send(data) + + def _send_model(self, data: typing.Any) -> None: + """ + Send a Pydantic model to the websocket connection. + """ + self._send(data.dict()) diff --git a/src/deepgram/speak/v1/types/speak_v1metadata.py b/src/deepgram/speak/v1/types/speak_v1metadata.py index e009a764..4bb780fc 100644 --- a/src/deepgram/speak/v1/types/speak_v1metadata.py +++ b/src/deepgram/speak/v1/types/speak_v1metadata.py @@ -25,12 +25,17 @@ class SpeakV1Metadata(UncheckedBaseModel): model_version: str = pydantic.Field() """ - Version of the model being used + Version of the primary model being used """ model_uuid: str = pydantic.Field() """ - Unique identifier for the model + Unique identifier for the primary model used + """ + + additional_model_uuids: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + List of unique identifiers for any additional models used to serve the request """ if IS_PYDANTIC_V2: diff --git a/src/deepgram/types/listen_v1redact.py b/src/deepgram/types/listen_v1redact.py index 9187906c..7bf572f3 100644 --- a/src/deepgram/types/listen_v1redact.py +++ b/src/deepgram/types/listen_v1redact.py @@ -2,10 +2,6 @@ import typing -ListenV1Redact = typing.Optional[ - typing.Union[ - typing.Literal["true", "false", "pci", "numbers", "aggressive_numbers", "ssn"], - str, - typing.Sequence[str], - ] +ListenV1Redact = typing.Union[ + typing.Literal["true", "false", "pci", "numbers", "aggressive_numbers", "ssn"], typing.Any ] diff --git a/src/deepgram/types/listen_v1redact.py.bak b/src/deepgram/types/listen_v1redact.py.bak new file mode 100644 index 00000000..9187906c --- /dev/null +++ b/src/deepgram/types/listen_v1redact.py.bak @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ListenV1Redact = typing.Optional[ + typing.Union[ + typing.Literal["true", "false", "pci", "numbers", "aggressive_numbers", "ssn"], + str, + typing.Sequence[str], + ] +] diff --git a/src/deepgram/types/listen_v1response_results_channels_item_alternatives_item_paragraphs_paragraphs_item.py b/src/deepgram/types/listen_v1response_results_channels_item_alternatives_item_paragraphs_paragraphs_item.py index eec3866b..a622d4ae 100644 --- a/src/deepgram/types/listen_v1response_results_channels_item_alternatives_item_paragraphs_paragraphs_item.py +++ b/src/deepgram/types/listen_v1response_results_channels_item_alternatives_item_paragraphs_paragraphs_item.py @@ -14,8 +14,8 @@ class ListenV1ResponseResultsChannelsItemAlternativesItemParagraphsParagraphsIte sentences: typing.Optional[ typing.List[ListenV1ResponseResultsChannelsItemAlternativesItemParagraphsParagraphsItemSentencesItem] ] = None - speaker: typing.Optional[int] = None - num_words: typing.Optional[int] = None + speaker: typing.Optional[float] = None + num_words: typing.Optional[float] = None start: typing.Optional[float] = None end: typing.Optional[float] = None diff --git a/src/deepgram/types/listen_v1response_results_channels_item_alternatives_item_paragraphs_paragraphs_item.py.bak b/src/deepgram/types/listen_v1response_results_channels_item_alternatives_item_paragraphs_paragraphs_item.py.bak new file mode 100644 index 00000000..eec3866b --- /dev/null +++ b/src/deepgram/types/listen_v1response_results_channels_item_alternatives_item_paragraphs_paragraphs_item.py.bak @@ -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 +from ..core.unchecked_base_model import UncheckedBaseModel +from .listen_v1response_results_channels_item_alternatives_item_paragraphs_paragraphs_item_sentences_item import ( + ListenV1ResponseResultsChannelsItemAlternativesItemParagraphsParagraphsItemSentencesItem, +) + + +class ListenV1ResponseResultsChannelsItemAlternativesItemParagraphsParagraphsItem(UncheckedBaseModel): + sentences: typing.Optional[ + typing.List[ListenV1ResponseResultsChannelsItemAlternativesItemParagraphsParagraphsItemSentencesItem] + ] = None + speaker: typing.Optional[int] = None + num_words: typing.Optional[int] = None + start: typing.Optional[float] = None + end: 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/deepgram/types/listen_v1response_results_utterances_item.py b/src/deepgram/types/listen_v1response_results_utterances_item.py index 0947d9f5..ed8a8ddd 100644 --- a/src/deepgram/types/listen_v1response_results_utterances_item.py +++ b/src/deepgram/types/listen_v1response_results_utterances_item.py @@ -12,10 +12,10 @@ class ListenV1ResponseResultsUtterancesItem(UncheckedBaseModel): start: typing.Optional[float] = None end: typing.Optional[float] = None confidence: typing.Optional[float] = None - channel: typing.Optional[int] = None + channel: typing.Optional[float] = None transcript: typing.Optional[str] = None words: typing.Optional[typing.List[ListenV1ResponseResultsUtterancesItemWordsItem]] = None - speaker: typing.Optional[int] = None + speaker: typing.Optional[float] = None id: typing.Optional[str] = None if IS_PYDANTIC_V2: diff --git a/src/deepgram/types/listen_v1response_results_utterances_item.py.bak b/src/deepgram/types/listen_v1response_results_utterances_item.py.bak new file mode 100644 index 00000000..0947d9f5 --- /dev/null +++ b/src/deepgram/types/listen_v1response_results_utterances_item.py.bak @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .listen_v1response_results_utterances_item_words_item import ListenV1ResponseResultsUtterancesItemWordsItem + + +class ListenV1ResponseResultsUtterancesItem(UncheckedBaseModel): + start: typing.Optional[float] = None + end: typing.Optional[float] = None + confidence: typing.Optional[float] = None + channel: typing.Optional[int] = None + transcript: typing.Optional[str] = None + words: typing.Optional[typing.List[ListenV1ResponseResultsUtterancesItemWordsItem]] = None + speaker: typing.Optional[int] = None + id: typing.Optional[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/deepgram/types/listen_v1response_results_utterances_item_words_item.py b/src/deepgram/types/listen_v1response_results_utterances_item_words_item.py index 6cd1313a..716f2e2a 100644 --- a/src/deepgram/types/listen_v1response_results_utterances_item_words_item.py +++ b/src/deepgram/types/listen_v1response_results_utterances_item_words_item.py @@ -12,7 +12,7 @@ class ListenV1ResponseResultsUtterancesItemWordsItem(UncheckedBaseModel): start: typing.Optional[float] = None end: typing.Optional[float] = None confidence: typing.Optional[float] = None - speaker: typing.Optional[int] = None + speaker: typing.Optional[float] = None speaker_confidence: typing.Optional[float] = None punctuated_word: typing.Optional[str] = None diff --git a/src/deepgram/types/listen_v1response_results_utterances_item_words_item.py.bak b/src/deepgram/types/listen_v1response_results_utterances_item_words_item.py.bak new file mode 100644 index 00000000..6cd1313a --- /dev/null +++ b/src/deepgram/types/listen_v1response_results_utterances_item_words_item.py.bak @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ListenV1ResponseResultsUtterancesItemWordsItem(UncheckedBaseModel): + word: typing.Optional[str] = None + start: typing.Optional[float] = None + end: typing.Optional[float] = None + confidence: typing.Optional[float] = None + speaker: typing.Optional[int] = None + speaker_confidence: typing.Optional[float] = None + punctuated_word: typing.Optional[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/tests/conftest.py b/tests/conftest.py index ebaf0125..36a95eaa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,7 +15,7 @@ import pytest _STARTED: bool = False -_WIREMOCK_PORT: str = "8080" # Default, will be updated after container starts +_WIREMOCK_URL: str = "http://localhost:8080" # Default, will be updated after container starts _PROJECT_NAME: str = "deepgram-api" # This file lives at tests/conftest.py, so the project root is one level up. @@ -41,7 +41,7 @@ def _get_wiremock_port() -> str: def _start_wiremock() -> None: """Starts the WireMock container using docker-compose.""" - global _STARTED, _WIREMOCK_PORT + global _STARTED, _WIREMOCK_URL if _STARTED: return @@ -54,8 +54,9 @@ def _start_wiremock() -> None: text=True, ) _WIREMOCK_PORT = _get_wiremock_port() - os.environ["WIREMOCK_PORT"] = _WIREMOCK_PORT - print(f"WireMock container is ready on port {_WIREMOCK_PORT}") + _WIREMOCK_URL = f"http://localhost:{_WIREMOCK_PORT}" + os.environ["WIREMOCK_URL"] = _WIREMOCK_URL + print(f"WireMock container is ready at {_WIREMOCK_URL}") _STARTED = True except subprocess.CalledProcessError as e: print(f"Failed to start WireMock: {e.stderr}") diff --git a/tests/wire/conftest.py b/tests/wire/conftest.py index 9d7c5e1d..d138641f 100644 --- a/tests/wire/conftest.py +++ b/tests/wire/conftest.py @@ -26,9 +26,8 @@ def _get_wiremock_base_url() -> str: - """Returns the WireMock base URL using the dynamically assigned port.""" - port = os.environ.get("WIREMOCK_PORT", "8080") - return f"http://localhost:{port}" + """Returns the WireMock base URL from the WIREMOCK_URL environment variable.""" + return os.environ.get("WIREMOCK_URL", "http://localhost:8080") def get_client(test_id: str) -> DeepgramClient: @@ -46,13 +45,13 @@ def get_client(test_id: str) -> DeepgramClient: if _CLIENT_SUPPORTS_HEADERS: return DeepgramClient( - environment=DeepgramClientEnvironment(base=base_url, production=base_url, agent=base_url), + environment=DeepgramClientEnvironment(base=base_url, agent=base_url, production=base_url), headers=test_headers, api_key="test_api_key", ) return DeepgramClient( - environment=DeepgramClientEnvironment(base=base_url, production=base_url, agent=base_url), + environment=DeepgramClientEnvironment(base=base_url, agent=base_url, production=base_url), httpx_client=httpx.Client(headers=test_headers), api_key="test_api_key", ) diff --git a/tests/wire/test_listen_v1_media.py b/tests/wire/test_listen_v1_media.py index d7943355..8c08f1ab 100644 --- a/tests/wire/test_listen_v1_media.py +++ b/tests/wire/test_listen_v1_media.py @@ -86,11 +86,3 @@ def test_listen_v1_media_transcribe_url() -> None: }, 1, ) - - -def test_listen_v1_media_transcribe_file() -> None: - """Test transcribeFile endpoint with WireMock""" - test_id = "listen.v1.media.transcribe_file.0" - client = get_client(test_id) - client.listen.v1.media.transcribe_file(request=b"test audio data") - verify_request_count(test_id, "POST", "/v1/listen", None, 1) diff --git a/tests/wire/test_listen_v1_media.py.bak b/tests/wire/test_listen_v1_media.py.bak new file mode 100644 index 00000000..d7943355 --- /dev/null +++ b/tests/wire/test_listen_v1_media.py.bak @@ -0,0 +1,96 @@ +from .conftest import get_client, verify_request_count + + +def test_listen_v1_media_transcribe_url() -> None: + """Test transcribeUrl endpoint with WireMock""" + test_id = "listen.v1.media.transcribe_url.0" + client = get_client(test_id) + client.listen.v1.media.transcribe_url( + callback="callback", + callback_method="POST", + extra=["extra"], + sentiment=True, + summarize="v2", + tag=["tag"], + topics=True, + custom_topic=["custom_topic"], + custom_topic_mode="extended", + intents=True, + custom_intent=["custom_intent"], + custom_intent_mode="extended", + detect_entities=True, + detect_language=True, + diarize=True, + dictation=True, + encoding="linear16", + filler_words=True, + keywords=["keywords"], + language="language", + measurements=True, + model="nova-3", + multichannel=True, + numerals=True, + paragraphs=True, + profanity_filter=True, + punctuate=True, + redact="redact", + replace=["replace"], + search=["search"], + smart_format=True, + utterances=True, + utt_split=1.1, + version="latest", + mip_opt_out=True, + url="https://dpgr.am/spacewalk.wav", + ) + verify_request_count( + test_id, + "POST", + "/v1/listen", + { + "callback": "callback", + "callback_method": "POST", + "extra": "extra", + "sentiment": "true", + "summarize": "v2", + "tag": "tag", + "topics": "true", + "custom_topic": "custom_topic", + "custom_topic_mode": "extended", + "intents": "true", + "custom_intent": "custom_intent", + "custom_intent_mode": "extended", + "detect_entities": "true", + "detect_language": "true", + "diarize": "true", + "dictation": "true", + "encoding": "linear16", + "filler_words": "true", + "keywords": "keywords", + "language": "language", + "measurements": "true", + "model": "nova-3", + "multichannel": "true", + "numerals": "true", + "paragraphs": "true", + "profanity_filter": "true", + "punctuate": "true", + "redact": "redact", + "replace": "replace", + "search": "search", + "smart_format": "true", + "utterances": "true", + "utt_split": "1.1", + "version": "latest", + "mip_opt_out": "true", + }, + 1, + ) + + +def test_listen_v1_media_transcribe_file() -> None: + """Test transcribeFile endpoint with WireMock""" + test_id = "listen.v1.media.transcribe_file.0" + client = get_client(test_id) + client.listen.v1.media.transcribe_file(request=b"test audio data") + verify_request_count(test_id, "POST", "/v1/listen", None, 1) diff --git a/wiremock/wiremock-mappings.json b/wiremock/wiremock-mappings.json index 384ee619..d79d9b09 100644 --- a/wiremock/wiremock-mappings.json +++ b/wiremock/wiremock-mappings.json @@ -1 +1 @@ -{"mappings":[{"id":"533b5d52-ab21-4763-aaae-87cf52f49aa5","name":"List Agent Think Models - default","request":{"urlPathTemplate":"/v1/agent/settings/think/models","method":"GET"},"response":{"status":200,"body":"{\n \"models\": [\n {\n \"id\": \"gpt-5\",\n \"name\": \"name\",\n \"provider\": \"open_ai\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"533b5d52-ab21-4763-aaae-87cf52f49aa5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"20e1029e-8bb9-4092-a809-b943e60822ef","name":"Token-based Authentication - default","request":{"urlPathTemplate":"/v1/auth/grant","method":"POST"},"response":{"status":200,"body":"{\n \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U\",\n \"expires_in\": 30\n}","headers":{"Content-Type":"application/json"}},"uuid":"20e1029e-8bb9-4092-a809-b943e60822ef","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"49d8d51a-7f01-4598-804f-b6f54cdc22da","name":"Transcribe and analyze pre-recorded audio and video - default","request":{"urlPathTemplate":"/v1/listen","method":"POST"},"response":{"status":200,"body":"{\n \"metadata\": {\n \"request_id\": \"a847f427-4ad5-4d67-9b95-db801e58251c\",\n \"sha256\": \"154e291ecfa8be6ab8343560bcc109008fa7853eb5372533e8efdefc9b504c33\",\n \"created\": \"2024-05-12T18:57:13Z\",\n \"duration\": 25.933313,\n \"channels\": 1,\n \"models\": [\n \"30089e05-99d1-4376-b32e-c263170674af\"\n ],\n \"model_info\": {\n \"30089e05-99d1-4376-b32e-c263170674af\": {\n \"name\": \"2-general-nova\",\n \"version\": \"2024-01-09.29447\",\n \"arch\": \"nova-2\"\n }\n },\n \"summary_info\": {\n \"model_uuid\": \"67875a7f-c9c4-48a0-aa55-5bdb8a91c34a\",\n \"input_tokens\": 95,\n \"output_tokens\": 63\n },\n \"sentiment_info\": {\n \"model_uuid\": \"80ab3179-d113-4254-bd6b-4a2f96498695\",\n \"input_tokens\": 105,\n \"output_tokens\": 105\n },\n \"topics_info\": {\n \"model_uuid\": \"80ab3179-d113-4254-bd6b-4a2f96498695\",\n \"input_tokens\": 105,\n \"output_tokens\": 7\n },\n \"intents_info\": {\n \"model_uuid\": \"80ab3179-d113-4254-bd6b-4a2f96498695\",\n \"input_tokens\": 105,\n \"output_tokens\": 4\n },\n \"tags\": [\n \"test\"\n ]\n },\n \"results\": {\n \"channels\": [\n {}\n ],\n \"utterances\": [\n {}\n ],\n \"summary\": {\n \"result\": \"success\",\n \"short\": \"Speaker 0 discusses the significance of the first all-female spacewalk with an all-female team, stating that it is a tribute to the skilled and qualified women who were denied opportunities in the past.\"\n },\n \"sentiments\": {\n \"segments\": [\n {\n \"text\": \"Yeah. As as much as, um, it's worth celebrating, uh, the first, uh, spacewalk, um, with an all-female team, I think many of us are looking forward to it just being normal. And, um, I think if it signifies anything, it is, uh, to honor the the women who came before us who, um, were skilled and qualified, um, and didn't get the the same opportunities that we have today.\",\n \"start_word\": 0,\n \"end_word\": 69,\n \"sentiment\": \"positive\",\n \"sentiment_score\": 0.5810546875\n }\n ],\n \"average\": {\n \"sentiment\": \"positive\",\n \"sentiment_score\": 0.5810185185185185\n }\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"49d8d51a-7f01-4598-804f-b6f54cdc22da","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0a0be61b-f024-4120-9c54-23bca3e07c93","name":"List Models - default","request":{"urlPathTemplate":"/v1/models","method":"GET"},"response":{"status":200,"body":"{\n \"stt\": [\n {\n \"name\": \"nova-3\",\n \"canonical_name\": \"nova-3\",\n \"architecture\": \"base\",\n \"languages\": [\n \"en\",\n \"en-us\"\n ],\n \"version\": \"2021-11-10.1\",\n \"uuid\": \"6b28e919-8427-4f32-9847-492e2efd7daf\",\n \"batch\": true,\n \"streaming\": true,\n \"formatted_output\": true\n }\n ],\n \"tts\": [\n {\n \"name\": \"zeus\",\n \"canonical_name\": \"aura-2-zeus-en\",\n \"architecture\": \"aura-2\",\n \"languages\": [\n \"en\",\n \"en-US\"\n ],\n \"version\": \"2025-04-07.0\",\n \"uuid\": \"2baf189d-91ac-481d-b6d1-750888667b31\",\n \"metadata\": {\n \"accent\": \"American\",\n \"age\": \"Adult\",\n \"color\": \"#C58DFF\",\n \"image\": \"https://static.deepgram.com/examples/avatars/zeus.jpg\",\n \"sample\": \"https://static.deepgram.com/examples/Aura-2-zeus.wav\",\n \"tags\": [\n \"masculine\",\n \"deep\",\n \"trustworthy\",\n \"smooth\"\n ],\n \"use_cases\": [\n \"IVR\"\n ]\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"0a0be61b-f024-4120-9c54-23bca3e07c93","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"0f94d3ea-43b6-4a1a-bce4-ab05b85440ae","name":"Get a specific Model - default","request":{"urlPathTemplate":"/v1/models/{model_id}","method":"GET","pathParameters":{"model_id":{"equalTo":"af6e9977-99f6-4d8f-b6f5-dfdf6fb6e291"}}},"response":{"status":200,"body":"{\n \"name\": \"general\",\n \"canonical_name\": \"enhanced-general\",\n \"architecture\": \"polaris\",\n \"languages\": [\n \"en\",\n \"en-us\"\n ],\n \"version\": \"2022-05-18.1\",\n \"uuid\": \"c7226e9e-ae1c-4057-ae2a-a71a6b0dc588\",\n \"batch\": true,\n \"streaming\": true,\n \"formatted_output\": false\n}","headers":{"Content-Type":"application/json"}},"uuid":"0f94d3ea-43b6-4a1a-bce4-ab05b85440ae","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d08cac56-bc4d-4756-8fd1-d508914334d5","name":"List Projects - default","request":{"urlPathTemplate":"/v1/projects","method":"GET"},"response":{"status":200,"body":"{\n \"projects\": [\n {\n \"project_id\": \"project_id\",\n \"name\": \"name\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"d08cac56-bc4d-4756-8fd1-d508914334d5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"6f0163a8-530c-4e25-bbe0-9ca86b9525dc","name":"Get a Project - default","request":{"urlPathTemplate":"/v1/projects/{project_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"project_id\": \"project_id\",\n \"mip_opt_out\": true,\n \"name\": \"name\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"6f0163a8-530c-4e25-bbe0-9ca86b9525dc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"68918577-6401-4439-8533-356257ff7bcf","name":"Delete a Project - default","request":{"urlPathTemplate":"/v1/projects/{project_id}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"68918577-6401-4439-8533-356257ff7bcf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"659fc38b-3934-4e43-93bf-d331f547449e","name":"Update a Project - default","request":{"urlPathTemplate":"/v1/projects/{project_id}","method":"PATCH","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"message\": \"Successfully updated project info.\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"659fc38b-3934-4e43-93bf-d331f547449e","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"43ae6622-ad2f-4c81-9bc9-a8bbe17ef9d8","name":"Leave a Project - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/leave","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"43ae6622-ad2f-4c81-9bc9-a8bbe17ef9d8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"032d9b1c-3b87-40fb-bfab-8c5be92a5d71","name":"List Project Keys - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/keys","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"api_keys\": [\n {\n \"member\": {\n \"member_id\": \"1000-2000-3000-4000\",\n \"email\": \"john@test.com\"\n },\n \"api_key\": {\n \"api_key_id\": \"1234567890abcdef1234567890abcdef\",\n \"comment\": \"A comment\",\n \"scopes\": [\n \"admin\"\n ],\n \"created\": \"2021-01-01T00:00:00Z\"\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"032d9b1c-3b87-40fb-bfab-8c5be92a5d71","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0167c735-0b6f-4715-8df8-32300d4dae72","name":"Create a Project Key - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/keys","method":"POST","pathParameters":{"project_id":{"equalTo":"project_id"}}},"response":{"status":200,"body":"{\n \"api_key_id\": \"api_key_id\",\n \"key\": \"key\",\n \"comment\": \"comment\",\n \"scopes\": [\n \"scopes\",\n \"scopes\"\n ],\n \"tags\": [\n \"tags\",\n \"tags\"\n ],\n \"expiration_date\": \"2024-01-15T09:30:00Z\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"0167c735-0b6f-4715-8df8-32300d4dae72","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c9812dd3-f87e-4798-aec3-af0933330dd5","name":"Get a Project Key - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/keys/{key_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"key_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"item\": {\n \"member\": {\n \"member_id\": \"1000-2000-3000-4000\",\n \"email\": \"john@test.com\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\",\n \"api_key\": {\n \"api_key_id\": \"1000-2000-3000-4000\",\n \"comment\": \"A comment\",\n \"scopes\": [\n \"admin\"\n ],\n \"tags\": [\n \"prod\",\n \"west-region\"\n ],\n \"expiration_date\": \"2021-01-01T00:00:00Z\",\n \"created\": \"2021-01-01T00:00:00Z\"\n }\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"c9812dd3-f87e-4798-aec3-af0933330dd5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"3d2fbc7c-7bac-436f-a6ac-abe1b2c2caac","name":"Delete a Project Key - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/keys/{key_id}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"key_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"3d2fbc7c-7bac-436f-a6ac-abe1b2c2caac","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"91e103d5-72f7-463d-840d-310069e33de9","name":"List Project Members - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/members","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"members\": [\n {\n \"member_id\": \"member_id\",\n \"email\": \"email\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"91e103d5-72f7-463d-840d-310069e33de9","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"515c6f7e-09c3-43ea-ad6c-65bc11d20f46","name":"Delete a Project Member - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/members/{member_id}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"member_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"515c6f7e-09c3-43ea-ad6c-65bc11d20f46","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"a920ad0e-2796-4361-ac16-ac83fb75e32a","name":"List Project Models - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/models","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"stt\": [\n {\n \"name\": \"nova-3\",\n \"canonical_name\": \"nova-3\",\n \"architecture\": \"base\",\n \"languages\": [\n \"en\",\n \"en-us\"\n ],\n \"version\": \"2021-11-10.1\",\n \"uuid\": \"6b28e919-8427-4f32-9847-492e2efd7daf\",\n \"batch\": true,\n \"streaming\": true,\n \"formatted_output\": true\n }\n ],\n \"tts\": [\n {\n \"name\": \"zeus\",\n \"canonical_name\": \"aura-2-zeus-en\",\n \"architecture\": \"aura-2\",\n \"languages\": [\n \"en\",\n \"en-US\"\n ],\n \"version\": \"2025-04-07.0\",\n \"uuid\": \"2baf189d-91ac-481d-b6d1-750888667b31\",\n \"metadata\": {\n \"accent\": \"American\",\n \"age\": \"Adult\",\n \"color\": \"#C58DFF\",\n \"image\": \"https://static.deepgram.com/examples/avatars/zeus.jpg\",\n \"sample\": \"https://static.deepgram.com/examples/Aura-2-zeus.wav\",\n \"tags\": [\n \"masculine\",\n \"deep\",\n \"trustworthy\",\n \"smooth\"\n ],\n \"use_cases\": [\n \"IVR\"\n ]\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"a920ad0e-2796-4361-ac16-ac83fb75e32a","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"9f8c6bf2-ebee-4956-b39f-0291b9d64b6e","name":"Get a Project Model - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/models/{model_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"model_id":{"equalTo":"af6e9977-99f6-4d8f-b6f5-dfdf6fb6e291"}}},"response":{"status":200,"body":"{\n \"name\": \"general\",\n \"canonical_name\": \"enhanced-general\",\n \"architecture\": \"polaris\",\n \"languages\": [\n \"en\",\n \"en-us\"\n ],\n \"version\": \"2022-05-18.1\",\n \"uuid\": \"c7226e9e-ae1c-4057-ae2a-a71a6b0dc588\",\n \"batch\": true,\n \"streaming\": true,\n \"formatted_output\": false\n}","headers":{"Content-Type":"application/json"}},"uuid":"9f8c6bf2-ebee-4956-b39f-0291b9d64b6e","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d6a14959-05fa-4aec-9f0c-ba2a817c66e5","name":"List Project Requests - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/requests","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"page\": 1.1,\n \"limit\": 1.1,\n \"requests\": [\n {\n \"request_id\": \"request_id\",\n \"project_uuid\": \"project_uuid\",\n \"created\": \"2024-01-15T09:30:00Z\",\n \"path\": \"path\",\n \"api_key_id\": \"api_key_id\",\n \"response\": {\n \"key\": \"value\"\n },\n \"code\": 1.1,\n \"deployment\": \"deployment\",\n \"callback\": \"callback\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"d6a14959-05fa-4aec-9f0c-ba2a817c66e5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"733e39aa-d3ef-4ea7-8062-af080c6288c4","name":"Get a Project Request - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/requests/{request_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"request_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"request\": {\n \"request_id\": \"request_id\",\n \"project_uuid\": \"project_uuid\",\n \"created\": \"2024-01-15T09:30:00Z\",\n \"path\": \"path\",\n \"api_key_id\": \"api_key_id\",\n \"response\": {\n \"key\": \"value\"\n },\n \"code\": 1.1,\n \"deployment\": \"deployment\",\n \"callback\": \"callback\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"733e39aa-d3ef-4ea7-8062-af080c6288c4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6309dd55-c993-4ce1-b0b2-01a41c9f08d6","name":"Get Project Usage - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/usage","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"start\": \"2024-10-16\",\n \"end\": \"2024-10-23\",\n \"resolution\": {\n \"units\": \"day\",\n \"amount\": 1\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"6309dd55-c993-4ce1-b0b2-01a41c9f08d6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b132121b-4efe-42ad-a268-8acac35c189b","name":"Get Project Balances - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/balances","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"balances\": [\n {\n \"balance_id\": \"balance_id\",\n \"amount\": 1.1,\n \"units\": \"units\",\n \"purchase_order_id\": \"purchase_order_id\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"b132121b-4efe-42ad-a268-8acac35c189b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4019c244-52d3-4d57-902c-af837631650a","name":"Get a Project Balance - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/balances/{balance_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"balance_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"balance_id\": \"balance_id\",\n \"amount\": 1.1,\n \"units\": \"units\",\n \"purchase_order_id\": \"purchase_order_id\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"4019c244-52d3-4d57-902c-af837631650a","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"555b6751-587f-400c-bf5e-400e108ad6b4","name":"Get Project Billing Breakdown - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/billing/breakdown","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"start\": \"2025-01-16\",\n \"end\": \"2025-01-23\",\n \"resolution\": {\n \"units\": \"day\",\n \"amount\": 1\n },\n \"results\": [\n {\n \"dollars\": 0.25,\n \"grouping\": {\n \"start\": \"2025-01-16\",\n \"end\": \"2025-01-16\",\n \"accessor\": \"123456789012345678901234\",\n \"deployment\": \"hosted\",\n \"line_item\": \"streaming::nova-3\",\n \"tags\": [\n \"tag1\",\n \"tag2\"\n ]\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"555b6751-587f-400c-bf5e-400e108ad6b4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"a61ae38c-e41f-4726-a55c-88f2135897be","name":"List Project Billing Fields - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/billing/fields","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"accessors\": [\n \"12345678-1234-1234-1234-123456789012\",\n \"87654321-4321-4321-4321-210987654321\"\n ],\n \"deployments\": [\n \"hosted\",\n \"self-hosted\"\n ],\n \"tags\": [\n \"dev\",\n \"production\"\n ],\n \"line_items\": {\n \"streaming::nova-3\": \"Nova - 3 (Stream)\",\n \"sync::aura-2\": \"Aura -2 (Sync)\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"a61ae38c-e41f-4726-a55c-88f2135897be","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"85b4373c-ba39-41b1-84e8-ae1ee6b180ca","name":"List Project Purchases - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/purchases","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"orders\": [\n {\n \"order_id\": \"025e19ba-b6d9-4a04-9f99-4fe715aca5f1\",\n \"expiration\": \"2026-03-04T00:00:00Z\",\n \"created\": \"2023-02-21T21:13:40Z\",\n \"amount\": 150,\n \"units\": \"usd\",\n \"order_type\": \"promotional\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"85b4373c-ba39-41b1-84e8-ae1ee6b180ca","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"2dd14c67-ed4e-4d97-9636-0a712899deb8","name":"List Project Invites - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/invites","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"invites\": [\n {\n \"email\": \"email\",\n \"scope\": \"scope\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"2dd14c67-ed4e-4d97-9636-0a712899deb8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"7c109496-adfe-4e85-b007-a6f799ee95cb","name":"Create a Project Invite - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/invites","method":"POST","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"7c109496-adfe-4e85-b007-a6f799ee95cb","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d6d268d0-d91e-4a65-80e0-339621173db9","name":"Delete a Project Invite - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/invites/{email}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"email":{"equalTo":"john.doe@example.com"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"d6d268d0-d91e-4a65-80e0-339621173db9","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"1b965d71-c930-4a0b-90f3-2289f80f3634","name":"List Project Member Scopes - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/members/{member_id}/scopes","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"member_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"scopes\": [\n \"scopes\"\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"1b965d71-c930-4a0b-90f3-2289f80f3634","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"eb2f5de2-b887-47be-abd5-7cb702aca55d","name":"Update Project Member Scopes - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/members/{member_id}/scopes","method":"PUT","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"member_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"eb2f5de2-b887-47be-abd5-7cb702aca55d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"9bdf51a4-1e10-41b8-8de2-2df650562db3","name":"Get Project Usage Breakdown - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/usage/breakdown","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"start\": \"2025-01-16\",\n \"end\": \"2025-01-23\",\n \"resolution\": {\n \"units\": \"day\",\n \"amount\": 1\n },\n \"results\": [\n {\n \"hours\": 1619.7242069444444,\n \"total_hours\": 1621.7395791666668,\n \"agent_hours\": 41.33564388888889,\n \"tokens_in\": 0,\n \"tokens_out\": 0,\n \"tts_characters\": 9158866,\n \"requests\": 373381,\n \"grouping\": {\n \"start\": \"2025-01-16\",\n \"end\": \"2025-01-16\",\n \"accessor\": \"123456789012345678901234\",\n \"endpoint\": \"listen\",\n \"feature_set\": \"punctuate\",\n \"models\": \"Nova-2\",\n \"method\": \"async\",\n \"tags\": \"tag1\",\n \"deployment\": \"self-hosted\"\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"9bdf51a4-1e10-41b8-8de2-2df650562db3","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"bc9fcd54-076e-48dc-a2dd-d71a8bf8bd4e","name":"List Project Usage Fields - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/usage/fields","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"tags\": [\n \"tag=dev\",\n \"tag=production\"\n ],\n \"models\": [\n {\n \"name\": \"2-medical-nova\",\n \"language\": \"en-MY\",\n \"version\": \"2024-05-31.13574\",\n \"model_id\": \"1234567890-12345-67890\"\n }\n ],\n \"processing_methods\": [\n \"sync\",\n \"streaming\"\n ],\n \"features\": [\n \"alternatives\",\n \"detect_entities\",\n \"detect_language\"\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"bc9fcd54-076e-48dc-a2dd-d71a8bf8bd4e","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b5ac3651-d3b7-4cd7-b0b8-e1a917a16f3b","name":"Analyze text content - default","request":{"urlPathTemplate":"/v1/read","method":"POST"},"response":{"status":200,"body":"{\n \"metadata\": {\n \"metadata\": {\n \"request_id\": \"d04af392-db11-4c1d-83e1-20e34f0b8999\",\n \"created\": \"2024-11-18T23:47:44Z\",\n \"language\": \"en\"\n }\n },\n \"results\": {\n \"sentiments\": {\n \"segments\": [\n {\n \"text\": \"Yeah. As as much as, um, it's worth celebrating, uh, the first, uh, spacewalk, um, with an all-female team, I think many of us are looking forward to it just being normal. And, um, I think if it signifies anything, it is, uh, to honor the the women who came before us who, um, were skilled and qualified, um, and didn't get the the same opportunities that we have today.\",\n \"start_word\": 0,\n \"end_word\": 69,\n \"sentiment\": \"positive\",\n \"sentiment_score\": 0.5810546875\n }\n ],\n \"average\": {\n \"sentiment\": \"positive\",\n \"sentiment_score\": 0.5810185185185185\n }\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"b5ac3651-d3b7-4cd7-b0b8-e1a917a16f3b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4110cb96-50e2-4fe6-b8ae-5d69120cee89","name":"List Project Self-Hosted Distribution Credentials - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/self-hosted/distribution/credentials","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"distribution_credentials\": [\n {\n \"member\": {\n \"member_id\": \"3376abcd-8e5e-49d3-92d4-876d3a4f0363\",\n \"email\": \"email@example.com\"\n },\n \"distribution_credentials\": {\n \"distribution_credentials_id\": \"8b36cfd0-472f-4a21-833f-2d6343c3a2f3\",\n \"provider\": \"quay\",\n \"comment\": \"My Self-Hosted Distribution Credentials\",\n \"scopes\": [\n \"self-hosted:product:api\",\n \"self-hosted:product:engine\"\n ],\n \"created\": \"2023-06-28T15:36:59Z\"\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"4110cb96-50e2-4fe6-b8ae-5d69120cee89","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"9c12eea9-6ba6-4d70-bb14-a2742cebc114","name":"Create a Project Self-Hosted Distribution Credential - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/self-hosted/distribution/credentials","method":"POST","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"member\": {\n \"member_id\": \"c7b9b131-73f3-11d9-8665-0b00d2e44b83\",\n \"email\": \"email@example.com\"\n },\n \"distribution_credentials\": {\n \"distribution_credentials_id\": \"82c32c10-53b2-4d23-993f-864b3d44502a\",\n \"provider\": \"quay\",\n \"comment\": \"My Self-Hosted Distribution Credentials\",\n \"scopes\": [\n \"self-hosted:product:api\",\n \"self-hosted:product:engine\"\n ],\n \"created\": \"2023-06-28T15:36:59Z\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"9c12eea9-6ba6-4d70-bb14-a2742cebc114","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"a47cd13f-2314-4190-b2c7-20436ccffbd2","name":"Get a Project Self-Hosted Distribution Credential - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/self-hosted/distribution/credentials/{distribution_credentials_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"distribution_credentials_id":{"equalTo":"8b36cfd0-472f-4a21-833f-2d6343c3a2f3"}}},"response":{"status":200,"body":"{\n \"member\": {\n \"member_id\": \"c7b9b131-73f3-11d9-8665-0b00d2e44b83\",\n \"email\": \"email@example.com\"\n },\n \"distribution_credentials\": {\n \"distribution_credentials_id\": \"82c32c10-53b2-4d23-993f-864b3d44502a\",\n \"provider\": \"quay\",\n \"comment\": \"My Self-Hosted Distribution Credentials\",\n \"scopes\": [\n \"self-hosted:product:api\",\n \"self-hosted:product:engine\"\n ],\n \"created\": \"2023-06-28T15:36:59Z\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"a47cd13f-2314-4190-b2c7-20436ccffbd2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8bd46091-0e57-4b3d-9485-a86e6f1eaf17","name":"Delete a Project Self-Hosted Distribution Credential - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/self-hosted/distribution/credentials/{distribution_credentials_id}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"distribution_credentials_id":{"equalTo":"8b36cfd0-472f-4a21-833f-2d6343c3a2f3"}}},"response":{"status":200,"body":"{\n \"member\": {\n \"member_id\": \"c7b9b131-73f3-11d9-8665-0b00d2e44b83\",\n \"email\": \"email@example.com\"\n },\n \"distribution_credentials\": {\n \"distribution_credentials_id\": \"82c32c10-53b2-4d23-993f-864b3d44502a\",\n \"provider\": \"quay\",\n \"comment\": \"My Self-Hosted Distribution Credentials\",\n \"scopes\": [\n \"self-hosted:product:api\",\n \"self-hosted:product:engine\"\n ],\n \"created\": \"2023-06-28T15:36:59Z\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"8bd46091-0e57-4b3d-9485-a86e6f1eaf17","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b06ec977-02ed-41e2-8fff-2bc45cd2166b","name":"Text to Speech transformation - default","request":{"urlPathTemplate":"/v1/speak","method":"POST"},"response":{"status":200,"body":"{\n \"key\": \"value\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"b06ec977-02ed-41e2-8fff-2bc45cd2166b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}}],"meta":{"total":40}} \ No newline at end of file +{"mappings":[{"id":"533b5d52-ab21-4763-aaae-87cf52f49aa5","name":"List Agent Think Models - default","request":{"urlPathTemplate":"/v1/agent/settings/think/models","method":"GET"},"response":{"status":200,"body":"{\n \"models\": [\n {\n \"id\": \"gpt-5\",\n \"name\": \"name\",\n \"provider\": \"open_ai\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"533b5d52-ab21-4763-aaae-87cf52f49aa5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"20e1029e-8bb9-4092-a809-b943e60822ef","name":"Token-based Authentication - default","request":{"urlPathTemplate":"/v1/auth/grant","method":"POST"},"response":{"status":200,"body":"{\n \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U\",\n \"expires_in\": 30\n}","headers":{"Content-Type":"application/json"}},"uuid":"20e1029e-8bb9-4092-a809-b943e60822ef","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"49d8d51a-7f01-4598-804f-b6f54cdc22da","name":"Transcribe and analyze pre-recorded audio and video - default","request":{"urlPathTemplate":"/v1/listen","method":"POST","queryParameters":{"callback":{"equalTo":"callback"},"callback_method":{"equalTo":"POST"},"extra":{"equalTo":"extra"},"sentiment":{"equalTo":"true"},"summarize":{"equalTo":"v2"},"tag":{"equalTo":"tag"},"topics":{"equalTo":"true"},"custom_topic":{"equalTo":"custom_topic"},"custom_topic_mode":{"equalTo":"extended"},"intents":{"equalTo":"true"},"custom_intent":{"equalTo":"custom_intent"},"custom_intent_mode":{"equalTo":"extended"},"detect_entities":{"equalTo":"true"},"detect_language":{"equalTo":"true"},"diarize":{"equalTo":"true"},"dictation":{"equalTo":"true"},"encoding":{"equalTo":"linear16"},"filler_words":{"equalTo":"true"},"keywords":{"equalTo":"keywords"},"language":{"equalTo":"language"},"measurements":{"equalTo":"true"},"model":{"equalTo":"nova-3"},"multichannel":{"equalTo":"true"},"numerals":{"equalTo":"true"},"paragraphs":{"equalTo":"true"},"profanity_filter":{"equalTo":"true"},"punctuate":{"equalTo":"true"},"redact":{"equalTo":"redact"},"replace":{"equalTo":"replace"},"search":{"equalTo":"search"},"smart_format":{"equalTo":"true"},"utterances":{"equalTo":"true"},"utt_split":{"equalTo":"1.1"},"version":{"equalTo":"latest"},"mip_opt_out":{"equalTo":"true"}}},"response":{"status":200,"body":"{\n \"metadata\": {\n \"request_id\": \"a847f427-4ad5-4d67-9b95-db801e58251c\",\n \"sha256\": \"154e291ecfa8be6ab8343560bcc109008fa7853eb5372533e8efdefc9b504c33\",\n \"created\": \"2024-05-12T18:57:13Z\",\n \"duration\": 25.933313,\n \"channels\": 1,\n \"models\": [\n \"30089e05-99d1-4376-b32e-c263170674af\"\n ],\n \"model_info\": {\n \"30089e05-99d1-4376-b32e-c263170674af\": {\n \"name\": \"2-general-nova\",\n \"version\": \"2024-01-09.29447\",\n \"arch\": \"nova-2\"\n }\n },\n \"summary_info\": {\n \"model_uuid\": \"67875a7f-c9c4-48a0-aa55-5bdb8a91c34a\",\n \"input_tokens\": 95,\n \"output_tokens\": 63\n },\n \"sentiment_info\": {\n \"model_uuid\": \"80ab3179-d113-4254-bd6b-4a2f96498695\",\n \"input_tokens\": 105,\n \"output_tokens\": 105\n },\n \"topics_info\": {\n \"model_uuid\": \"80ab3179-d113-4254-bd6b-4a2f96498695\",\n \"input_tokens\": 105,\n \"output_tokens\": 7\n },\n \"intents_info\": {\n \"model_uuid\": \"80ab3179-d113-4254-bd6b-4a2f96498695\",\n \"input_tokens\": 105,\n \"output_tokens\": 4\n },\n \"tags\": [\n \"test\"\n ]\n },\n \"results\": {\n \"channels\": [\n {}\n ],\n \"utterances\": [\n {}\n ],\n \"summary\": {\n \"result\": \"success\",\n \"short\": \"Speaker 0 discusses the significance of the first all-female spacewalk with an all-female team, stating that it is a tribute to the skilled and qualified women who were denied opportunities in the past.\"\n },\n \"topics\": {\n \"results\": {\n \"topics\": {\n \"segments\": [\n {\n \"text\": \"And, um, I think if it signifies anything, it is, uh, to honor the the women who came before us who, um, were skilled and qualified, um, and didn't get the the same opportunities that we have today.\",\n \"start_word\": 32,\n \"end_word\": 69,\n \"topics\": [\n {\n \"topic\": \"Spacewalk\",\n \"confidence_score\": 0.91581345\n }\n ]\n }\n ]\n }\n }\n },\n \"intents\": {\n \"results\": {\n \"intents\": {\n \"segments\": [\n {\n \"text\": \"If you found this valuable, you can subscribe to the show on spotify or your favorite podcast app.\",\n \"start_word\": 354,\n \"end_word\": 414,\n \"intents\": [\n {\n \"intent\": \"Encourage podcasting\",\n \"confidence_score\": 0.0038975573\n }\n ]\n }\n ]\n }\n }\n },\n \"sentiments\": {\n \"segments\": [\n {\n \"text\": \"Yeah. As as much as, um, it's worth celebrating, uh, the first, uh, spacewalk, um, with an all-female team, I think many of us are looking forward to it just being normal. And, um, I think if it signifies anything, it is, uh, to honor the the women who came before us who, um, were skilled and qualified, um, and didn't get the the same opportunities that we have today.\",\n \"start_word\": 0,\n \"end_word\": 69,\n \"sentiment\": \"positive\",\n \"sentiment_score\": 0.5810546875\n }\n ],\n \"average\": {\n \"sentiment\": \"positive\",\n \"sentiment_score\": 0.5810185185185185\n }\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"49d8d51a-7f01-4598-804f-b6f54cdc22da","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0a0be61b-f024-4120-9c54-23bca3e07c93","name":"List Models - default","request":{"urlPathTemplate":"/v1/models","method":"GET","queryParameters":{"include_outdated":{"equalTo":"true"}}},"response":{"status":200,"body":"{\n \"stt\": [\n {\n \"name\": \"nova-3\",\n \"canonical_name\": \"nova-3\",\n \"architecture\": \"base\",\n \"languages\": [\n \"en\",\n \"en-us\"\n ],\n \"version\": \"2021-11-10.1\",\n \"uuid\": \"6b28e919-8427-4f32-9847-492e2efd7daf\",\n \"batch\": true,\n \"streaming\": true,\n \"formatted_output\": true\n }\n ],\n \"tts\": [\n {\n \"name\": \"zeus\",\n \"canonical_name\": \"aura-2-zeus-en\",\n \"architecture\": \"aura-2\",\n \"languages\": [\n \"en\",\n \"en-US\"\n ],\n \"version\": \"2025-04-07.0\",\n \"uuid\": \"2baf189d-91ac-481d-b6d1-750888667b31\",\n \"metadata\": {\n \"accent\": \"American\",\n \"age\": \"Adult\",\n \"color\": \"#C58DFF\",\n \"image\": \"https://static.deepgram.com/examples/avatars/zeus.jpg\",\n \"sample\": \"https://static.deepgram.com/examples/Aura-2-zeus.wav\",\n \"tags\": [\n \"masculine\",\n \"deep\",\n \"trustworthy\",\n \"smooth\"\n ],\n \"use_cases\": [\n \"IVR\"\n ]\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"0a0be61b-f024-4120-9c54-23bca3e07c93","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"0f94d3ea-43b6-4a1a-bce4-ab05b85440ae","name":"Get a specific Model - default","request":{"urlPathTemplate":"/v1/models/{model_id}","method":"GET","pathParameters":{"model_id":{"equalTo":"af6e9977-99f6-4d8f-b6f5-dfdf6fb6e291"}}},"response":{"status":200,"body":"{\n \"name\": \"general\",\n \"canonical_name\": \"enhanced-general\",\n \"architecture\": \"polaris\",\n \"languages\": [\n \"en\",\n \"en-us\"\n ],\n \"version\": \"2022-05-18.1\",\n \"uuid\": \"c7226e9e-ae1c-4057-ae2a-a71a6b0dc588\",\n \"batch\": true,\n \"streaming\": true,\n \"formatted_output\": false\n}","headers":{"Content-Type":"application/json"}},"uuid":"0f94d3ea-43b6-4a1a-bce4-ab05b85440ae","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d08cac56-bc4d-4756-8fd1-d508914334d5","name":"List Projects - default","request":{"urlPathTemplate":"/v1/projects","method":"GET"},"response":{"status":200,"body":"{\n \"projects\": [\n {\n \"project_id\": \"project_id\",\n \"name\": \"name\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"d08cac56-bc4d-4756-8fd1-d508914334d5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"6f0163a8-530c-4e25-bbe0-9ca86b9525dc","name":"Get a Project - default","request":{"urlPathTemplate":"/v1/projects/{project_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}},"queryParameters":{"limit":{"equalTo":"1.1"},"page":{"equalTo":"1.1"}}},"response":{"status":200,"body":"{\n \"project_id\": \"project_id\",\n \"mip_opt_out\": true,\n \"name\": \"name\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"6f0163a8-530c-4e25-bbe0-9ca86b9525dc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"68918577-6401-4439-8533-356257ff7bcf","name":"Delete a Project - default","request":{"urlPathTemplate":"/v1/projects/{project_id}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"68918577-6401-4439-8533-356257ff7bcf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"659fc38b-3934-4e43-93bf-d331f547449e","name":"Update a Project - default","request":{"urlPathTemplate":"/v1/projects/{project_id}","method":"PATCH","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"message\": \"Successfully updated project info.\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"659fc38b-3934-4e43-93bf-d331f547449e","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"43ae6622-ad2f-4c81-9bc9-a8bbe17ef9d8","name":"Leave a Project - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/leave","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"43ae6622-ad2f-4c81-9bc9-a8bbe17ef9d8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"032d9b1c-3b87-40fb-bfab-8c5be92a5d71","name":"List Project Keys - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/keys","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}},"queryParameters":{"status":{"equalTo":"active"}}},"response":{"status":200,"body":"{\n \"api_keys\": [\n {\n \"member\": {\n \"member_id\": \"1000-2000-3000-4000\",\n \"email\": \"john@test.com\"\n },\n \"api_key\": {\n \"api_key_id\": \"1234567890abcdef1234567890abcdef\",\n \"comment\": \"A comment\",\n \"scopes\": [\n \"admin\"\n ],\n \"created\": \"2021-01-01T00:00:00Z\"\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"032d9b1c-3b87-40fb-bfab-8c5be92a5d71","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0167c735-0b6f-4715-8df8-32300d4dae72","name":"Create a Project Key - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/keys","method":"POST","pathParameters":{"project_id":{"equalTo":"project_id"}}},"response":{"status":200,"body":"{\n \"api_key_id\": \"api_key_id\",\n \"key\": \"key\",\n \"comment\": \"comment\",\n \"scopes\": [\n \"scopes\",\n \"scopes\"\n ],\n \"tags\": [\n \"tags\",\n \"tags\"\n ],\n \"expiration_date\": \"2024-01-15T09:30:00Z\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"0167c735-0b6f-4715-8df8-32300d4dae72","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c9812dd3-f87e-4798-aec3-af0933330dd5","name":"Get a Project Key - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/keys/{key_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"key_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"item\": {\n \"member\": {\n \"member_id\": \"1000-2000-3000-4000\",\n \"email\": \"john@test.com\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\",\n \"api_key\": {\n \"api_key_id\": \"1000-2000-3000-4000\",\n \"comment\": \"A comment\",\n \"scopes\": [\n \"admin\"\n ],\n \"tags\": [\n \"prod\",\n \"west-region\"\n ],\n \"expiration_date\": \"2021-01-01T00:00:00Z\",\n \"created\": \"2021-01-01T00:00:00Z\"\n }\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"c9812dd3-f87e-4798-aec3-af0933330dd5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"3d2fbc7c-7bac-436f-a6ac-abe1b2c2caac","name":"Delete a Project Key - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/keys/{key_id}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"key_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"3d2fbc7c-7bac-436f-a6ac-abe1b2c2caac","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"91e103d5-72f7-463d-840d-310069e33de9","name":"List Project Members - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/members","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"members\": [\n {\n \"member_id\": \"member_id\",\n \"email\": \"email\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"91e103d5-72f7-463d-840d-310069e33de9","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"515c6f7e-09c3-43ea-ad6c-65bc11d20f46","name":"Delete a Project Member - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/members/{member_id}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"member_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"515c6f7e-09c3-43ea-ad6c-65bc11d20f46","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"a920ad0e-2796-4361-ac16-ac83fb75e32a","name":"List Project Models - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/models","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}},"queryParameters":{"include_outdated":{"equalTo":"true"}}},"response":{"status":200,"body":"{\n \"stt\": [\n {\n \"name\": \"nova-3\",\n \"canonical_name\": \"nova-3\",\n \"architecture\": \"base\",\n \"languages\": [\n \"en\",\n \"en-us\"\n ],\n \"version\": \"2021-11-10.1\",\n \"uuid\": \"6b28e919-8427-4f32-9847-492e2efd7daf\",\n \"batch\": true,\n \"streaming\": true,\n \"formatted_output\": true\n }\n ],\n \"tts\": [\n {\n \"name\": \"zeus\",\n \"canonical_name\": \"aura-2-zeus-en\",\n \"architecture\": \"aura-2\",\n \"languages\": [\n \"en\",\n \"en-US\"\n ],\n \"version\": \"2025-04-07.0\",\n \"uuid\": \"2baf189d-91ac-481d-b6d1-750888667b31\",\n \"metadata\": {\n \"accent\": \"American\",\n \"age\": \"Adult\",\n \"color\": \"#C58DFF\",\n \"image\": \"https://static.deepgram.com/examples/avatars/zeus.jpg\",\n \"sample\": \"https://static.deepgram.com/examples/Aura-2-zeus.wav\",\n \"tags\": [\n \"masculine\",\n \"deep\",\n \"trustworthy\",\n \"smooth\"\n ],\n \"use_cases\": [\n \"IVR\"\n ]\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"a920ad0e-2796-4361-ac16-ac83fb75e32a","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"9f8c6bf2-ebee-4956-b39f-0291b9d64b6e","name":"Get a Project Model - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/models/{model_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"model_id":{"equalTo":"af6e9977-99f6-4d8f-b6f5-dfdf6fb6e291"}}},"response":{"status":200,"body":"{\n \"name\": \"general\",\n \"canonical_name\": \"enhanced-general\",\n \"architecture\": \"polaris\",\n \"languages\": [\n \"en\",\n \"en-us\"\n ],\n \"version\": \"2022-05-18.1\",\n \"uuid\": \"c7226e9e-ae1c-4057-ae2a-a71a6b0dc588\",\n \"batch\": true,\n \"streaming\": true,\n \"formatted_output\": false\n}","headers":{"Content-Type":"application/json"}},"uuid":"9f8c6bf2-ebee-4956-b39f-0291b9d64b6e","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d6a14959-05fa-4aec-9f0c-ba2a817c66e5","name":"List Project Requests - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/requests","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}},"queryParameters":{"start":{"equalTo":"2024-01-15T09:30:00.000Z"},"end":{"equalTo":"2024-01-15T09:30:00.000Z"},"limit":{"equalTo":"1.1"},"page":{"equalTo":"1.1"},"accessor":{"equalTo":"12345678-1234-1234-1234-123456789012"},"request_id":{"equalTo":"12345678-1234-1234-1234-123456789012"},"deployment":{"equalTo":"hosted"},"endpoint":{"equalTo":"listen"},"method":{"equalTo":"sync"},"status":{"equalTo":"succeeded"}}},"response":{"status":200,"body":"{\n \"page\": 1.1,\n \"limit\": 1.1,\n \"requests\": [\n {\n \"request_id\": \"request_id\",\n \"project_uuid\": \"project_uuid\",\n \"created\": \"2024-01-15T09:30:00Z\",\n \"path\": \"path\",\n \"api_key_id\": \"api_key_id\",\n \"response\": {\n \"key\": \"value\"\n },\n \"code\": 1.1,\n \"deployment\": \"deployment\",\n \"callback\": \"callback\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"d6a14959-05fa-4aec-9f0c-ba2a817c66e5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"733e39aa-d3ef-4ea7-8062-af080c6288c4","name":"Get a Project Request - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/requests/{request_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"request_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"request\": {\n \"request_id\": \"request_id\",\n \"project_uuid\": \"project_uuid\",\n \"created\": \"2024-01-15T09:30:00Z\",\n \"path\": \"path\",\n \"api_key_id\": \"api_key_id\",\n \"response\": {\n \"key\": \"value\"\n },\n \"code\": 1.1,\n \"deployment\": \"deployment\",\n \"callback\": \"callback\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"733e39aa-d3ef-4ea7-8062-af080c6288c4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6309dd55-c993-4ce1-b0b2-01a41c9f08d6","name":"Get Project Usage - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/usage","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}},"queryParameters":{"start":{"equalTo":"start"},"end":{"equalTo":"end"},"accessor":{"equalTo":"12345678-1234-1234-1234-123456789012"},"alternatives":{"equalTo":"true"},"callback_method":{"equalTo":"true"},"callback":{"equalTo":"true"},"channels":{"equalTo":"true"},"custom_intent_mode":{"equalTo":"true"},"custom_intent":{"equalTo":"true"},"custom_topic_mode":{"equalTo":"true"},"custom_topic":{"equalTo":"true"},"deployment":{"equalTo":"hosted"},"detect_entities":{"equalTo":"true"},"detect_language":{"equalTo":"true"},"diarize":{"equalTo":"true"},"dictation":{"equalTo":"true"},"encoding":{"equalTo":"true"},"endpoint":{"equalTo":"listen"},"extra":{"equalTo":"true"},"filler_words":{"equalTo":"true"},"intents":{"equalTo":"true"},"keyterm":{"equalTo":"true"},"keywords":{"equalTo":"true"},"language":{"equalTo":"true"},"measurements":{"equalTo":"true"},"method":{"equalTo":"sync"},"model":{"equalTo":"6f548761-c9c0-429a-9315-11a1d28499c8"},"multichannel":{"equalTo":"true"},"numerals":{"equalTo":"true"},"paragraphs":{"equalTo":"true"},"profanity_filter":{"equalTo":"true"},"punctuate":{"equalTo":"true"},"redact":{"equalTo":"true"},"replace":{"equalTo":"true"},"sample_rate":{"equalTo":"true"},"search":{"equalTo":"true"},"sentiment":{"equalTo":"true"},"smart_format":{"equalTo":"true"},"summarize":{"equalTo":"true"},"tag":{"equalTo":"tag1"},"topics":{"equalTo":"true"},"utt_split":{"equalTo":"true"},"utterances":{"equalTo":"true"},"version":{"equalTo":"true"}}},"response":{"status":200,"body":"{\n \"start\": \"2024-10-16\",\n \"end\": \"2024-10-23\",\n \"resolution\": {\n \"units\": \"day\",\n \"amount\": 1\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"6309dd55-c993-4ce1-b0b2-01a41c9f08d6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b132121b-4efe-42ad-a268-8acac35c189b","name":"Get Project Balances - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/balances","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"balances\": [\n {\n \"balance_id\": \"balance_id\",\n \"amount\": 1.1,\n \"units\": \"units\",\n \"purchase_order_id\": \"purchase_order_id\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"b132121b-4efe-42ad-a268-8acac35c189b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4019c244-52d3-4d57-902c-af837631650a","name":"Get a Project Balance - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/balances/{balance_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"balance_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"balance_id\": \"balance_id\",\n \"amount\": 1.1,\n \"units\": \"units\",\n \"purchase_order_id\": \"purchase_order_id\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"4019c244-52d3-4d57-902c-af837631650a","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"555b6751-587f-400c-bf5e-400e108ad6b4","name":"Get Project Billing Breakdown - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/billing/breakdown","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}},"queryParameters":{"start":{"equalTo":"start"},"end":{"equalTo":"end"},"accessor":{"equalTo":"12345678-1234-1234-1234-123456789012"},"deployment":{"equalTo":"hosted"},"tag":{"equalTo":"tag1"},"line_item":{"equalTo":"streaming::nova-3"}}},"response":{"status":200,"body":"{\n \"start\": \"2025-01-16\",\n \"end\": \"2025-01-23\",\n \"resolution\": {\n \"units\": \"day\",\n \"amount\": 1\n },\n \"results\": [\n {\n \"dollars\": 0.25,\n \"grouping\": {\n \"start\": \"2025-01-16\",\n \"end\": \"2025-01-16\",\n \"accessor\": \"123456789012345678901234\",\n \"deployment\": \"hosted\",\n \"line_item\": \"streaming::nova-3\",\n \"tags\": [\n \"tag1\",\n \"tag2\"\n ]\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"555b6751-587f-400c-bf5e-400e108ad6b4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"a61ae38c-e41f-4726-a55c-88f2135897be","name":"List Project Billing Fields - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/billing/fields","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}},"queryParameters":{"start":{"equalTo":"start"},"end":{"equalTo":"end"}}},"response":{"status":200,"body":"{\n \"accessors\": [\n \"12345678-1234-1234-1234-123456789012\",\n \"87654321-4321-4321-4321-210987654321\"\n ],\n \"deployments\": [\n \"hosted\",\n \"self-hosted\"\n ],\n \"tags\": [\n \"dev\",\n \"production\"\n ],\n \"line_items\": {\n \"streaming::nova-3\": \"Nova - 3 (Stream)\",\n \"sync::aura-2\": \"Aura -2 (Sync)\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"a61ae38c-e41f-4726-a55c-88f2135897be","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"85b4373c-ba39-41b1-84e8-ae1ee6b180ca","name":"List Project Purchases - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/purchases","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}},"queryParameters":{"limit":{"equalTo":"1.1"}}},"response":{"status":200,"body":"{\n \"orders\": [\n {\n \"order_id\": \"025e19ba-b6d9-4a04-9f99-4fe715aca5f1\",\n \"expiration\": \"2026-03-04T00:00:00Z\",\n \"created\": \"2023-02-21T21:13:40Z\",\n \"amount\": 150,\n \"units\": \"usd\",\n \"order_type\": \"promotional\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"85b4373c-ba39-41b1-84e8-ae1ee6b180ca","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"2dd14c67-ed4e-4d97-9636-0a712899deb8","name":"List Project Invites - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/invites","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"invites\": [\n {\n \"email\": \"email\",\n \"scope\": \"scope\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"2dd14c67-ed4e-4d97-9636-0a712899deb8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"7c109496-adfe-4e85-b007-a6f799ee95cb","name":"Create a Project Invite - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/invites","method":"POST","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"7c109496-adfe-4e85-b007-a6f799ee95cb","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d6d268d0-d91e-4a65-80e0-339621173db9","name":"Delete a Project Invite - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/invites/{email}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"email":{"equalTo":"john.doe@example.com"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"d6d268d0-d91e-4a65-80e0-339621173db9","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"1b965d71-c930-4a0b-90f3-2289f80f3634","name":"List Project Member Scopes - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/members/{member_id}/scopes","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"member_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"scopes\": [\n \"scopes\"\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"1b965d71-c930-4a0b-90f3-2289f80f3634","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"eb2f5de2-b887-47be-abd5-7cb702aca55d","name":"Update Project Member Scopes - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/members/{member_id}/scopes","method":"PUT","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"member_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"eb2f5de2-b887-47be-abd5-7cb702aca55d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"9bdf51a4-1e10-41b8-8de2-2df650562db3","name":"Get Project Usage Breakdown - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/usage/breakdown","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}},"queryParameters":{"start":{"equalTo":"start"},"end":{"equalTo":"end"},"grouping":{"equalTo":"accessor"},"accessor":{"equalTo":"12345678-1234-1234-1234-123456789012"},"alternatives":{"equalTo":"true"},"callback_method":{"equalTo":"true"},"callback":{"equalTo":"true"},"channels":{"equalTo":"true"},"custom_intent_mode":{"equalTo":"true"},"custom_intent":{"equalTo":"true"},"custom_topic_mode":{"equalTo":"true"},"custom_topic":{"equalTo":"true"},"deployment":{"equalTo":"hosted"},"detect_entities":{"equalTo":"true"},"detect_language":{"equalTo":"true"},"diarize":{"equalTo":"true"},"dictation":{"equalTo":"true"},"encoding":{"equalTo":"true"},"endpoint":{"equalTo":"listen"},"extra":{"equalTo":"true"},"filler_words":{"equalTo":"true"},"intents":{"equalTo":"true"},"keyterm":{"equalTo":"true"},"keywords":{"equalTo":"true"},"language":{"equalTo":"true"},"measurements":{"equalTo":"true"},"method":{"equalTo":"sync"},"model":{"equalTo":"6f548761-c9c0-429a-9315-11a1d28499c8"},"multichannel":{"equalTo":"true"},"numerals":{"equalTo":"true"},"paragraphs":{"equalTo":"true"},"profanity_filter":{"equalTo":"true"},"punctuate":{"equalTo":"true"},"redact":{"equalTo":"true"},"replace":{"equalTo":"true"},"sample_rate":{"equalTo":"true"},"search":{"equalTo":"true"},"sentiment":{"equalTo":"true"},"smart_format":{"equalTo":"true"},"summarize":{"equalTo":"true"},"tag":{"equalTo":"tag1"},"topics":{"equalTo":"true"},"utt_split":{"equalTo":"true"},"utterances":{"equalTo":"true"},"version":{"equalTo":"true"}}},"response":{"status":200,"body":"{\n \"start\": \"2025-01-16\",\n \"end\": \"2025-01-23\",\n \"resolution\": {\n \"units\": \"day\",\n \"amount\": 1\n },\n \"results\": [\n {\n \"hours\": 1619.7242069444444,\n \"total_hours\": 1621.7395791666668,\n \"agent_hours\": 41.33564388888889,\n \"tokens_in\": 0,\n \"tokens_out\": 0,\n \"tts_characters\": 9158866,\n \"requests\": 373381,\n \"grouping\": {\n \"start\": \"2025-01-16\",\n \"end\": \"2025-01-16\",\n \"accessor\": \"123456789012345678901234\",\n \"endpoint\": \"listen\",\n \"feature_set\": \"punctuate\",\n \"models\": \"Nova-2\",\n \"method\": \"async\",\n \"tags\": \"tag1\",\n \"deployment\": \"self-hosted\"\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"9bdf51a4-1e10-41b8-8de2-2df650562db3","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"bc9fcd54-076e-48dc-a2dd-d71a8bf8bd4e","name":"List Project Usage Fields - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/usage/fields","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}},"queryParameters":{"start":{"equalTo":"start"},"end":{"equalTo":"end"}}},"response":{"status":200,"body":"{\n \"tags\": [\n \"tag=dev\",\n \"tag=production\"\n ],\n \"models\": [\n {\n \"name\": \"2-medical-nova\",\n \"language\": \"en-MY\",\n \"version\": \"2024-05-31.13574\",\n \"model_id\": \"1234567890-12345-67890\"\n }\n ],\n \"processing_methods\": [\n \"sync\",\n \"streaming\"\n ],\n \"features\": [\n \"alternatives\",\n \"detect_entities\",\n \"detect_language\"\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"bc9fcd54-076e-48dc-a2dd-d71a8bf8bd4e","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b5ac3651-d3b7-4cd7-b0b8-e1a917a16f3b","name":"Analyze text content - default","request":{"urlPathTemplate":"/v1/read","method":"POST","queryParameters":{"callback":{"equalTo":"callback"},"callback_method":{"equalTo":"POST"},"sentiment":{"equalTo":"true"},"summarize":{"equalTo":"v2"},"tag":{"equalTo":"tag"},"topics":{"equalTo":"true"},"custom_topic":{"equalTo":"custom_topic"},"custom_topic_mode":{"equalTo":"extended"},"intents":{"equalTo":"true"},"custom_intent":{"equalTo":"custom_intent"},"custom_intent_mode":{"equalTo":"extended"},"language":{"equalTo":"language"}}},"response":{"status":200,"body":"{\n \"metadata\": {\n \"metadata\": {\n \"request_id\": \"d04af392-db11-4c1d-83e1-20e34f0b8999\",\n \"created\": \"2024-11-18T23:47:44Z\",\n \"language\": \"en\"\n }\n },\n \"results\": {\n \"summary\": {\n \"results\": {\n \"summary\": {\n \"text\": \"The summary of the text submitted.\"\n }\n }\n },\n \"topics\": {\n \"results\": {\n \"topics\": {\n \"segments\": [\n {\n \"text\": \"And, um, I think if it signifies anything, it is, uh, to honor the the women who came before us who, um, were skilled and qualified, um, and didn't get the the same opportunities that we have today.\",\n \"start_word\": 32,\n \"end_word\": 69,\n \"topics\": [\n {\n \"topic\": \"Spacewalk\",\n \"confidence_score\": 0.91581345\n }\n ]\n }\n ]\n }\n }\n },\n \"intents\": {\n \"results\": {\n \"intents\": {\n \"segments\": [\n {\n \"text\": \"If you found this valuable, you can subscribe to the show on spotify or your favorite podcast app.\",\n \"start_word\": 354,\n \"end_word\": 414,\n \"intents\": [\n {\n \"intent\": \"Encourage podcasting\",\n \"confidence_score\": 0.0038975573\n }\n ]\n }\n ]\n }\n }\n },\n \"sentiments\": {\n \"segments\": [\n {\n \"text\": \"Yeah. As as much as, um, it's worth celebrating, uh, the first, uh, spacewalk, um, with an all-female team, I think many of us are looking forward to it just being normal. And, um, I think if it signifies anything, it is, uh, to honor the the women who came before us who, um, were skilled and qualified, um, and didn't get the the same opportunities that we have today.\",\n \"start_word\": 0,\n \"end_word\": 69,\n \"sentiment\": \"positive\",\n \"sentiment_score\": 0.5810546875\n }\n ],\n \"average\": {\n \"sentiment\": \"positive\",\n \"sentiment_score\": 0.5810185185185185\n }\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"b5ac3651-d3b7-4cd7-b0b8-e1a917a16f3b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4110cb96-50e2-4fe6-b8ae-5d69120cee89","name":"List Project Self-Hosted Distribution Credentials - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/self-hosted/distribution/credentials","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"distribution_credentials\": [\n {\n \"member\": {\n \"member_id\": \"3376abcd-8e5e-49d3-92d4-876d3a4f0363\",\n \"email\": \"email@example.com\"\n },\n \"distribution_credentials\": {\n \"distribution_credentials_id\": \"8b36cfd0-472f-4a21-833f-2d6343c3a2f3\",\n \"provider\": \"quay\",\n \"comment\": \"My Self-Hosted Distribution Credentials\",\n \"scopes\": [\n \"self-hosted:product:api\",\n \"self-hosted:product:engine\"\n ],\n \"created\": \"2023-06-28T15:36:59Z\"\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"4110cb96-50e2-4fe6-b8ae-5d69120cee89","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"9c12eea9-6ba6-4d70-bb14-a2742cebc114","name":"Create a Project Self-Hosted Distribution Credential - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/self-hosted/distribution/credentials","method":"POST","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}},"queryParameters":{"provider":{"equalTo":"quay"}}},"response":{"status":200,"body":"{\n \"member\": {\n \"member_id\": \"c7b9b131-73f3-11d9-8665-0b00d2e44b83\",\n \"email\": \"email@example.com\"\n },\n \"distribution_credentials\": {\n \"distribution_credentials_id\": \"82c32c10-53b2-4d23-993f-864b3d44502a\",\n \"provider\": \"quay\",\n \"comment\": \"My Self-Hosted Distribution Credentials\",\n \"scopes\": [\n \"self-hosted:product:api\",\n \"self-hosted:product:engine\"\n ],\n \"created\": \"2023-06-28T15:36:59Z\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"9c12eea9-6ba6-4d70-bb14-a2742cebc114","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"a47cd13f-2314-4190-b2c7-20436ccffbd2","name":"Get a Project Self-Hosted Distribution Credential - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/self-hosted/distribution/credentials/{distribution_credentials_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"distribution_credentials_id":{"equalTo":"8b36cfd0-472f-4a21-833f-2d6343c3a2f3"}}},"response":{"status":200,"body":"{\n \"member\": {\n \"member_id\": \"c7b9b131-73f3-11d9-8665-0b00d2e44b83\",\n \"email\": \"email@example.com\"\n },\n \"distribution_credentials\": {\n \"distribution_credentials_id\": \"82c32c10-53b2-4d23-993f-864b3d44502a\",\n \"provider\": \"quay\",\n \"comment\": \"My Self-Hosted Distribution Credentials\",\n \"scopes\": [\n \"self-hosted:product:api\",\n \"self-hosted:product:engine\"\n ],\n \"created\": \"2023-06-28T15:36:59Z\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"a47cd13f-2314-4190-b2c7-20436ccffbd2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8bd46091-0e57-4b3d-9485-a86e6f1eaf17","name":"Delete a Project Self-Hosted Distribution Credential - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/self-hosted/distribution/credentials/{distribution_credentials_id}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"distribution_credentials_id":{"equalTo":"8b36cfd0-472f-4a21-833f-2d6343c3a2f3"}}},"response":{"status":200,"body":"{\n \"member\": {\n \"member_id\": \"c7b9b131-73f3-11d9-8665-0b00d2e44b83\",\n \"email\": \"email@example.com\"\n },\n \"distribution_credentials\": {\n \"distribution_credentials_id\": \"82c32c10-53b2-4d23-993f-864b3d44502a\",\n \"provider\": \"quay\",\n \"comment\": \"My Self-Hosted Distribution Credentials\",\n \"scopes\": [\n \"self-hosted:product:api\",\n \"self-hosted:product:engine\"\n ],\n \"created\": \"2023-06-28T15:36:59Z\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"8bd46091-0e57-4b3d-9485-a86e6f1eaf17","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b06ec977-02ed-41e2-8fff-2bc45cd2166b","name":"Text to Speech transformation - default","request":{"urlPathTemplate":"/v1/speak","method":"POST"},"response":{"status":200,"body":"{\n \"key\": \"value\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"b06ec977-02ed-41e2-8fff-2bc45cd2166b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}}],"meta":{"total":39}} \ No newline at end of file diff --git a/wiremock/wiremock-mappings.json.bak b/wiremock/wiremock-mappings.json.bak new file mode 100644 index 00000000..384ee619 --- /dev/null +++ b/wiremock/wiremock-mappings.json.bak @@ -0,0 +1 @@ +{"mappings":[{"id":"533b5d52-ab21-4763-aaae-87cf52f49aa5","name":"List Agent Think Models - default","request":{"urlPathTemplate":"/v1/agent/settings/think/models","method":"GET"},"response":{"status":200,"body":"{\n \"models\": [\n {\n \"id\": \"gpt-5\",\n \"name\": \"name\",\n \"provider\": \"open_ai\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"533b5d52-ab21-4763-aaae-87cf52f49aa5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"20e1029e-8bb9-4092-a809-b943e60822ef","name":"Token-based Authentication - default","request":{"urlPathTemplate":"/v1/auth/grant","method":"POST"},"response":{"status":200,"body":"{\n \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U\",\n \"expires_in\": 30\n}","headers":{"Content-Type":"application/json"}},"uuid":"20e1029e-8bb9-4092-a809-b943e60822ef","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"49d8d51a-7f01-4598-804f-b6f54cdc22da","name":"Transcribe and analyze pre-recorded audio and video - default","request":{"urlPathTemplate":"/v1/listen","method":"POST"},"response":{"status":200,"body":"{\n \"metadata\": {\n \"request_id\": \"a847f427-4ad5-4d67-9b95-db801e58251c\",\n \"sha256\": \"154e291ecfa8be6ab8343560bcc109008fa7853eb5372533e8efdefc9b504c33\",\n \"created\": \"2024-05-12T18:57:13Z\",\n \"duration\": 25.933313,\n \"channels\": 1,\n \"models\": [\n \"30089e05-99d1-4376-b32e-c263170674af\"\n ],\n \"model_info\": {\n \"30089e05-99d1-4376-b32e-c263170674af\": {\n \"name\": \"2-general-nova\",\n \"version\": \"2024-01-09.29447\",\n \"arch\": \"nova-2\"\n }\n },\n \"summary_info\": {\n \"model_uuid\": \"67875a7f-c9c4-48a0-aa55-5bdb8a91c34a\",\n \"input_tokens\": 95,\n \"output_tokens\": 63\n },\n \"sentiment_info\": {\n \"model_uuid\": \"80ab3179-d113-4254-bd6b-4a2f96498695\",\n \"input_tokens\": 105,\n \"output_tokens\": 105\n },\n \"topics_info\": {\n \"model_uuid\": \"80ab3179-d113-4254-bd6b-4a2f96498695\",\n \"input_tokens\": 105,\n \"output_tokens\": 7\n },\n \"intents_info\": {\n \"model_uuid\": \"80ab3179-d113-4254-bd6b-4a2f96498695\",\n \"input_tokens\": 105,\n \"output_tokens\": 4\n },\n \"tags\": [\n \"test\"\n ]\n },\n \"results\": {\n \"channels\": [\n {}\n ],\n \"utterances\": [\n {}\n ],\n \"summary\": {\n \"result\": \"success\",\n \"short\": \"Speaker 0 discusses the significance of the first all-female spacewalk with an all-female team, stating that it is a tribute to the skilled and qualified women who were denied opportunities in the past.\"\n },\n \"sentiments\": {\n \"segments\": [\n {\n \"text\": \"Yeah. As as much as, um, it's worth celebrating, uh, the first, uh, spacewalk, um, with an all-female team, I think many of us are looking forward to it just being normal. And, um, I think if it signifies anything, it is, uh, to honor the the women who came before us who, um, were skilled and qualified, um, and didn't get the the same opportunities that we have today.\",\n \"start_word\": 0,\n \"end_word\": 69,\n \"sentiment\": \"positive\",\n \"sentiment_score\": 0.5810546875\n }\n ],\n \"average\": {\n \"sentiment\": \"positive\",\n \"sentiment_score\": 0.5810185185185185\n }\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"49d8d51a-7f01-4598-804f-b6f54cdc22da","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0a0be61b-f024-4120-9c54-23bca3e07c93","name":"List Models - default","request":{"urlPathTemplate":"/v1/models","method":"GET"},"response":{"status":200,"body":"{\n \"stt\": [\n {\n \"name\": \"nova-3\",\n \"canonical_name\": \"nova-3\",\n \"architecture\": \"base\",\n \"languages\": [\n \"en\",\n \"en-us\"\n ],\n \"version\": \"2021-11-10.1\",\n \"uuid\": \"6b28e919-8427-4f32-9847-492e2efd7daf\",\n \"batch\": true,\n \"streaming\": true,\n \"formatted_output\": true\n }\n ],\n \"tts\": [\n {\n \"name\": \"zeus\",\n \"canonical_name\": \"aura-2-zeus-en\",\n \"architecture\": \"aura-2\",\n \"languages\": [\n \"en\",\n \"en-US\"\n ],\n \"version\": \"2025-04-07.0\",\n \"uuid\": \"2baf189d-91ac-481d-b6d1-750888667b31\",\n \"metadata\": {\n \"accent\": \"American\",\n \"age\": \"Adult\",\n \"color\": \"#C58DFF\",\n \"image\": \"https://static.deepgram.com/examples/avatars/zeus.jpg\",\n \"sample\": \"https://static.deepgram.com/examples/Aura-2-zeus.wav\",\n \"tags\": [\n \"masculine\",\n \"deep\",\n \"trustworthy\",\n \"smooth\"\n ],\n \"use_cases\": [\n \"IVR\"\n ]\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"0a0be61b-f024-4120-9c54-23bca3e07c93","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"0f94d3ea-43b6-4a1a-bce4-ab05b85440ae","name":"Get a specific Model - default","request":{"urlPathTemplate":"/v1/models/{model_id}","method":"GET","pathParameters":{"model_id":{"equalTo":"af6e9977-99f6-4d8f-b6f5-dfdf6fb6e291"}}},"response":{"status":200,"body":"{\n \"name\": \"general\",\n \"canonical_name\": \"enhanced-general\",\n \"architecture\": \"polaris\",\n \"languages\": [\n \"en\",\n \"en-us\"\n ],\n \"version\": \"2022-05-18.1\",\n \"uuid\": \"c7226e9e-ae1c-4057-ae2a-a71a6b0dc588\",\n \"batch\": true,\n \"streaming\": true,\n \"formatted_output\": false\n}","headers":{"Content-Type":"application/json"}},"uuid":"0f94d3ea-43b6-4a1a-bce4-ab05b85440ae","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d08cac56-bc4d-4756-8fd1-d508914334d5","name":"List Projects - default","request":{"urlPathTemplate":"/v1/projects","method":"GET"},"response":{"status":200,"body":"{\n \"projects\": [\n {\n \"project_id\": \"project_id\",\n \"name\": \"name\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"d08cac56-bc4d-4756-8fd1-d508914334d5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"6f0163a8-530c-4e25-bbe0-9ca86b9525dc","name":"Get a Project - default","request":{"urlPathTemplate":"/v1/projects/{project_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"project_id\": \"project_id\",\n \"mip_opt_out\": true,\n \"name\": \"name\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"6f0163a8-530c-4e25-bbe0-9ca86b9525dc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"68918577-6401-4439-8533-356257ff7bcf","name":"Delete a Project - default","request":{"urlPathTemplate":"/v1/projects/{project_id}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"68918577-6401-4439-8533-356257ff7bcf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"659fc38b-3934-4e43-93bf-d331f547449e","name":"Update a Project - default","request":{"urlPathTemplate":"/v1/projects/{project_id}","method":"PATCH","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"message\": \"Successfully updated project info.\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"659fc38b-3934-4e43-93bf-d331f547449e","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"43ae6622-ad2f-4c81-9bc9-a8bbe17ef9d8","name":"Leave a Project - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/leave","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"43ae6622-ad2f-4c81-9bc9-a8bbe17ef9d8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"032d9b1c-3b87-40fb-bfab-8c5be92a5d71","name":"List Project Keys - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/keys","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"api_keys\": [\n {\n \"member\": {\n \"member_id\": \"1000-2000-3000-4000\",\n \"email\": \"john@test.com\"\n },\n \"api_key\": {\n \"api_key_id\": \"1234567890abcdef1234567890abcdef\",\n \"comment\": \"A comment\",\n \"scopes\": [\n \"admin\"\n ],\n \"created\": \"2021-01-01T00:00:00Z\"\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"032d9b1c-3b87-40fb-bfab-8c5be92a5d71","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0167c735-0b6f-4715-8df8-32300d4dae72","name":"Create a Project Key - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/keys","method":"POST","pathParameters":{"project_id":{"equalTo":"project_id"}}},"response":{"status":200,"body":"{\n \"api_key_id\": \"api_key_id\",\n \"key\": \"key\",\n \"comment\": \"comment\",\n \"scopes\": [\n \"scopes\",\n \"scopes\"\n ],\n \"tags\": [\n \"tags\",\n \"tags\"\n ],\n \"expiration_date\": \"2024-01-15T09:30:00Z\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"0167c735-0b6f-4715-8df8-32300d4dae72","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c9812dd3-f87e-4798-aec3-af0933330dd5","name":"Get a Project Key - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/keys/{key_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"key_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"item\": {\n \"member\": {\n \"member_id\": \"1000-2000-3000-4000\",\n \"email\": \"john@test.com\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\",\n \"api_key\": {\n \"api_key_id\": \"1000-2000-3000-4000\",\n \"comment\": \"A comment\",\n \"scopes\": [\n \"admin\"\n ],\n \"tags\": [\n \"prod\",\n \"west-region\"\n ],\n \"expiration_date\": \"2021-01-01T00:00:00Z\",\n \"created\": \"2021-01-01T00:00:00Z\"\n }\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"c9812dd3-f87e-4798-aec3-af0933330dd5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"3d2fbc7c-7bac-436f-a6ac-abe1b2c2caac","name":"Delete a Project Key - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/keys/{key_id}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"key_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"3d2fbc7c-7bac-436f-a6ac-abe1b2c2caac","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"91e103d5-72f7-463d-840d-310069e33de9","name":"List Project Members - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/members","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"members\": [\n {\n \"member_id\": \"member_id\",\n \"email\": \"email\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"91e103d5-72f7-463d-840d-310069e33de9","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"515c6f7e-09c3-43ea-ad6c-65bc11d20f46","name":"Delete a Project Member - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/members/{member_id}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"member_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"515c6f7e-09c3-43ea-ad6c-65bc11d20f46","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"a920ad0e-2796-4361-ac16-ac83fb75e32a","name":"List Project Models - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/models","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"stt\": [\n {\n \"name\": \"nova-3\",\n \"canonical_name\": \"nova-3\",\n \"architecture\": \"base\",\n \"languages\": [\n \"en\",\n \"en-us\"\n ],\n \"version\": \"2021-11-10.1\",\n \"uuid\": \"6b28e919-8427-4f32-9847-492e2efd7daf\",\n \"batch\": true,\n \"streaming\": true,\n \"formatted_output\": true\n }\n ],\n \"tts\": [\n {\n \"name\": \"zeus\",\n \"canonical_name\": \"aura-2-zeus-en\",\n \"architecture\": \"aura-2\",\n \"languages\": [\n \"en\",\n \"en-US\"\n ],\n \"version\": \"2025-04-07.0\",\n \"uuid\": \"2baf189d-91ac-481d-b6d1-750888667b31\",\n \"metadata\": {\n \"accent\": \"American\",\n \"age\": \"Adult\",\n \"color\": \"#C58DFF\",\n \"image\": \"https://static.deepgram.com/examples/avatars/zeus.jpg\",\n \"sample\": \"https://static.deepgram.com/examples/Aura-2-zeus.wav\",\n \"tags\": [\n \"masculine\",\n \"deep\",\n \"trustworthy\",\n \"smooth\"\n ],\n \"use_cases\": [\n \"IVR\"\n ]\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"a920ad0e-2796-4361-ac16-ac83fb75e32a","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"9f8c6bf2-ebee-4956-b39f-0291b9d64b6e","name":"Get a Project Model - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/models/{model_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"model_id":{"equalTo":"af6e9977-99f6-4d8f-b6f5-dfdf6fb6e291"}}},"response":{"status":200,"body":"{\n \"name\": \"general\",\n \"canonical_name\": \"enhanced-general\",\n \"architecture\": \"polaris\",\n \"languages\": [\n \"en\",\n \"en-us\"\n ],\n \"version\": \"2022-05-18.1\",\n \"uuid\": \"c7226e9e-ae1c-4057-ae2a-a71a6b0dc588\",\n \"batch\": true,\n \"streaming\": true,\n \"formatted_output\": false\n}","headers":{"Content-Type":"application/json"}},"uuid":"9f8c6bf2-ebee-4956-b39f-0291b9d64b6e","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d6a14959-05fa-4aec-9f0c-ba2a817c66e5","name":"List Project Requests - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/requests","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"page\": 1.1,\n \"limit\": 1.1,\n \"requests\": [\n {\n \"request_id\": \"request_id\",\n \"project_uuid\": \"project_uuid\",\n \"created\": \"2024-01-15T09:30:00Z\",\n \"path\": \"path\",\n \"api_key_id\": \"api_key_id\",\n \"response\": {\n \"key\": \"value\"\n },\n \"code\": 1.1,\n \"deployment\": \"deployment\",\n \"callback\": \"callback\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"d6a14959-05fa-4aec-9f0c-ba2a817c66e5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"733e39aa-d3ef-4ea7-8062-af080c6288c4","name":"Get a Project Request - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/requests/{request_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"request_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"request\": {\n \"request_id\": \"request_id\",\n \"project_uuid\": \"project_uuid\",\n \"created\": \"2024-01-15T09:30:00Z\",\n \"path\": \"path\",\n \"api_key_id\": \"api_key_id\",\n \"response\": {\n \"key\": \"value\"\n },\n \"code\": 1.1,\n \"deployment\": \"deployment\",\n \"callback\": \"callback\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"733e39aa-d3ef-4ea7-8062-af080c6288c4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6309dd55-c993-4ce1-b0b2-01a41c9f08d6","name":"Get Project Usage - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/usage","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"start\": \"2024-10-16\",\n \"end\": \"2024-10-23\",\n \"resolution\": {\n \"units\": \"day\",\n \"amount\": 1\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"6309dd55-c993-4ce1-b0b2-01a41c9f08d6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b132121b-4efe-42ad-a268-8acac35c189b","name":"Get Project Balances - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/balances","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"balances\": [\n {\n \"balance_id\": \"balance_id\",\n \"amount\": 1.1,\n \"units\": \"units\",\n \"purchase_order_id\": \"purchase_order_id\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"b132121b-4efe-42ad-a268-8acac35c189b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4019c244-52d3-4d57-902c-af837631650a","name":"Get a Project Balance - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/balances/{balance_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"balance_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"balance_id\": \"balance_id\",\n \"amount\": 1.1,\n \"units\": \"units\",\n \"purchase_order_id\": \"purchase_order_id\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"4019c244-52d3-4d57-902c-af837631650a","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"555b6751-587f-400c-bf5e-400e108ad6b4","name":"Get Project Billing Breakdown - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/billing/breakdown","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"start\": \"2025-01-16\",\n \"end\": \"2025-01-23\",\n \"resolution\": {\n \"units\": \"day\",\n \"amount\": 1\n },\n \"results\": [\n {\n \"dollars\": 0.25,\n \"grouping\": {\n \"start\": \"2025-01-16\",\n \"end\": \"2025-01-16\",\n \"accessor\": \"123456789012345678901234\",\n \"deployment\": \"hosted\",\n \"line_item\": \"streaming::nova-3\",\n \"tags\": [\n \"tag1\",\n \"tag2\"\n ]\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"555b6751-587f-400c-bf5e-400e108ad6b4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"a61ae38c-e41f-4726-a55c-88f2135897be","name":"List Project Billing Fields - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/billing/fields","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"accessors\": [\n \"12345678-1234-1234-1234-123456789012\",\n \"87654321-4321-4321-4321-210987654321\"\n ],\n \"deployments\": [\n \"hosted\",\n \"self-hosted\"\n ],\n \"tags\": [\n \"dev\",\n \"production\"\n ],\n \"line_items\": {\n \"streaming::nova-3\": \"Nova - 3 (Stream)\",\n \"sync::aura-2\": \"Aura -2 (Sync)\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"a61ae38c-e41f-4726-a55c-88f2135897be","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"85b4373c-ba39-41b1-84e8-ae1ee6b180ca","name":"List Project Purchases - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/purchases","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"orders\": [\n {\n \"order_id\": \"025e19ba-b6d9-4a04-9f99-4fe715aca5f1\",\n \"expiration\": \"2026-03-04T00:00:00Z\",\n \"created\": \"2023-02-21T21:13:40Z\",\n \"amount\": 150,\n \"units\": \"usd\",\n \"order_type\": \"promotional\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"85b4373c-ba39-41b1-84e8-ae1ee6b180ca","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"2dd14c67-ed4e-4d97-9636-0a712899deb8","name":"List Project Invites - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/invites","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"invites\": [\n {\n \"email\": \"email\",\n \"scope\": \"scope\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"2dd14c67-ed4e-4d97-9636-0a712899deb8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"7c109496-adfe-4e85-b007-a6f799ee95cb","name":"Create a Project Invite - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/invites","method":"POST","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"7c109496-adfe-4e85-b007-a6f799ee95cb","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d6d268d0-d91e-4a65-80e0-339621173db9","name":"Delete a Project Invite - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/invites/{email}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"email":{"equalTo":"john.doe@example.com"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"d6d268d0-d91e-4a65-80e0-339621173db9","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"1b965d71-c930-4a0b-90f3-2289f80f3634","name":"List Project Member Scopes - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/members/{member_id}/scopes","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"member_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"scopes\": [\n \"scopes\"\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"1b965d71-c930-4a0b-90f3-2289f80f3634","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"eb2f5de2-b887-47be-abd5-7cb702aca55d","name":"Update Project Member Scopes - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/members/{member_id}/scopes","method":"PUT","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"member_id":{"equalTo":"123456789012345678901234"}}},"response":{"status":200,"body":"{\n \"message\": \"message\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"eb2f5de2-b887-47be-abd5-7cb702aca55d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"9bdf51a4-1e10-41b8-8de2-2df650562db3","name":"Get Project Usage Breakdown - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/usage/breakdown","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"start\": \"2025-01-16\",\n \"end\": \"2025-01-23\",\n \"resolution\": {\n \"units\": \"day\",\n \"amount\": 1\n },\n \"results\": [\n {\n \"hours\": 1619.7242069444444,\n \"total_hours\": 1621.7395791666668,\n \"agent_hours\": 41.33564388888889,\n \"tokens_in\": 0,\n \"tokens_out\": 0,\n \"tts_characters\": 9158866,\n \"requests\": 373381,\n \"grouping\": {\n \"start\": \"2025-01-16\",\n \"end\": \"2025-01-16\",\n \"accessor\": \"123456789012345678901234\",\n \"endpoint\": \"listen\",\n \"feature_set\": \"punctuate\",\n \"models\": \"Nova-2\",\n \"method\": \"async\",\n \"tags\": \"tag1\",\n \"deployment\": \"self-hosted\"\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"9bdf51a4-1e10-41b8-8de2-2df650562db3","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"bc9fcd54-076e-48dc-a2dd-d71a8bf8bd4e","name":"List Project Usage Fields - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/usage/fields","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"tags\": [\n \"tag=dev\",\n \"tag=production\"\n ],\n \"models\": [\n {\n \"name\": \"2-medical-nova\",\n \"language\": \"en-MY\",\n \"version\": \"2024-05-31.13574\",\n \"model_id\": \"1234567890-12345-67890\"\n }\n ],\n \"processing_methods\": [\n \"sync\",\n \"streaming\"\n ],\n \"features\": [\n \"alternatives\",\n \"detect_entities\",\n \"detect_language\"\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"bc9fcd54-076e-48dc-a2dd-d71a8bf8bd4e","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b5ac3651-d3b7-4cd7-b0b8-e1a917a16f3b","name":"Analyze text content - default","request":{"urlPathTemplate":"/v1/read","method":"POST"},"response":{"status":200,"body":"{\n \"metadata\": {\n \"metadata\": {\n \"request_id\": \"d04af392-db11-4c1d-83e1-20e34f0b8999\",\n \"created\": \"2024-11-18T23:47:44Z\",\n \"language\": \"en\"\n }\n },\n \"results\": {\n \"sentiments\": {\n \"segments\": [\n {\n \"text\": \"Yeah. As as much as, um, it's worth celebrating, uh, the first, uh, spacewalk, um, with an all-female team, I think many of us are looking forward to it just being normal. And, um, I think if it signifies anything, it is, uh, to honor the the women who came before us who, um, were skilled and qualified, um, and didn't get the the same opportunities that we have today.\",\n \"start_word\": 0,\n \"end_word\": 69,\n \"sentiment\": \"positive\",\n \"sentiment_score\": 0.5810546875\n }\n ],\n \"average\": {\n \"sentiment\": \"positive\",\n \"sentiment_score\": 0.5810185185185185\n }\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"b5ac3651-d3b7-4cd7-b0b8-e1a917a16f3b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4110cb96-50e2-4fe6-b8ae-5d69120cee89","name":"List Project Self-Hosted Distribution Credentials - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/self-hosted/distribution/credentials","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"distribution_credentials\": [\n {\n \"member\": {\n \"member_id\": \"3376abcd-8e5e-49d3-92d4-876d3a4f0363\",\n \"email\": \"email@example.com\"\n },\n \"distribution_credentials\": {\n \"distribution_credentials_id\": \"8b36cfd0-472f-4a21-833f-2d6343c3a2f3\",\n \"provider\": \"quay\",\n \"comment\": \"My Self-Hosted Distribution Credentials\",\n \"scopes\": [\n \"self-hosted:product:api\",\n \"self-hosted:product:engine\"\n ],\n \"created\": \"2023-06-28T15:36:59Z\"\n }\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"4110cb96-50e2-4fe6-b8ae-5d69120cee89","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"9c12eea9-6ba6-4d70-bb14-a2742cebc114","name":"Create a Project Self-Hosted Distribution Credential - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/self-hosted/distribution/credentials","method":"POST","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"}}},"response":{"status":200,"body":"{\n \"member\": {\n \"member_id\": \"c7b9b131-73f3-11d9-8665-0b00d2e44b83\",\n \"email\": \"email@example.com\"\n },\n \"distribution_credentials\": {\n \"distribution_credentials_id\": \"82c32c10-53b2-4d23-993f-864b3d44502a\",\n \"provider\": \"quay\",\n \"comment\": \"My Self-Hosted Distribution Credentials\",\n \"scopes\": [\n \"self-hosted:product:api\",\n \"self-hosted:product:engine\"\n ],\n \"created\": \"2023-06-28T15:36:59Z\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"9c12eea9-6ba6-4d70-bb14-a2742cebc114","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"a47cd13f-2314-4190-b2c7-20436ccffbd2","name":"Get a Project Self-Hosted Distribution Credential - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/self-hosted/distribution/credentials/{distribution_credentials_id}","method":"GET","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"distribution_credentials_id":{"equalTo":"8b36cfd0-472f-4a21-833f-2d6343c3a2f3"}}},"response":{"status":200,"body":"{\n \"member\": {\n \"member_id\": \"c7b9b131-73f3-11d9-8665-0b00d2e44b83\",\n \"email\": \"email@example.com\"\n },\n \"distribution_credentials\": {\n \"distribution_credentials_id\": \"82c32c10-53b2-4d23-993f-864b3d44502a\",\n \"provider\": \"quay\",\n \"comment\": \"My Self-Hosted Distribution Credentials\",\n \"scopes\": [\n \"self-hosted:product:api\",\n \"self-hosted:product:engine\"\n ],\n \"created\": \"2023-06-28T15:36:59Z\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"a47cd13f-2314-4190-b2c7-20436ccffbd2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8bd46091-0e57-4b3d-9485-a86e6f1eaf17","name":"Delete a Project Self-Hosted Distribution Credential - default","request":{"urlPathTemplate":"/v1/projects/{project_id}/self-hosted/distribution/credentials/{distribution_credentials_id}","method":"DELETE","pathParameters":{"project_id":{"equalTo":"123456-7890-1234-5678-901234"},"distribution_credentials_id":{"equalTo":"8b36cfd0-472f-4a21-833f-2d6343c3a2f3"}}},"response":{"status":200,"body":"{\n \"member\": {\n \"member_id\": \"c7b9b131-73f3-11d9-8665-0b00d2e44b83\",\n \"email\": \"email@example.com\"\n },\n \"distribution_credentials\": {\n \"distribution_credentials_id\": \"82c32c10-53b2-4d23-993f-864b3d44502a\",\n \"provider\": \"quay\",\n \"comment\": \"My Self-Hosted Distribution Credentials\",\n \"scopes\": [\n \"self-hosted:product:api\",\n \"self-hosted:product:engine\"\n ],\n \"created\": \"2023-06-28T15:36:59Z\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"8bd46091-0e57-4b3d-9485-a86e6f1eaf17","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b06ec977-02ed-41e2-8fff-2bc45cd2166b","name":"Text to Speech transformation - default","request":{"urlPathTemplate":"/v1/speak","method":"POST"},"response":{"status":200,"body":"{\n \"key\": \"value\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"b06ec977-02ed-41e2-8fff-2bc45cd2166b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}}],"meta":{"total":40}} \ No newline at end of file