Skip to content

Commit 48354d2

Browse files
fix: 🌿 skip_validation:true to allow unknown messages back from the API (#669)
This PR regenerates code to match the latest API Definition. --------- Co-authored-by: fern-api[bot] <115122769+fern-api[bot]@users.noreply.github.com> Co-authored-by: Luke Oliff <luke@lukeoliff.com>
1 parent b474a6d commit 48354d2

235 files changed

Lines changed: 1295 additions & 660 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.fern/metadata.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
},
1212
"use_typeddict_requests": true,
1313
"should_generate_websocket_clients": true,
14-
"enable_wire_tests": true
14+
"enable_wire_tests": true,
15+
"pydantic_config": {
16+
"skip_validation": true
17+
}
1518
},
16-
"sdkVersion": "6.0.0-beta.4"
19+
"sdkVersion": "6.0.1"
1720
}

.fernignore

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,30 @@ tests/wire/test_listen_v1_media.py
1414
# WebSocket socket clients:
1515
# - Optional message parameter defaults for send_flush, send_close, send_clear,
1616
# send_finalize, send_close_stream, send_keep_alive
17-
# - Removed unused imports (JSONDecodeError, websockets) flagged by ruff F401
17+
# - construct_type instead of parse_obj_as (skip_validation for unknown WS messages)
18+
# - except Exception (broad catch for custom transports)
19+
# - _sanitize_numeric_types in agent socket client (float→int for API)
1820
src/deepgram/speak/v1/socket_client.py
1921
src/deepgram/listen/v1/socket_client.py
2022
src/deepgram/listen/v2/socket_client.py
2123
src/deepgram/agent/v1/socket_client.py
2224

25+
# Type files with manual int type corrections (Fern generates float for speaker/channel/num_words)
26+
src/deepgram/types/listen_v1response_results_utterances_item.py
27+
src/deepgram/types/listen_v1response_results_utterances_item_words_item.py
28+
src/deepgram/types/listen_v1response_results_channels_item_alternatives_item_paragraphs_paragraphs_item.py
29+
30+
# Redact type with Union[str, Sequence[str]] support (Fern narrows to Union[Literal, Any])
31+
src/deepgram/types/listen_v1redact.py
32+
33+
# Listen client files with Union[str, Sequence[str]] array param support
34+
src/deepgram/listen/v1/client.py
35+
src/deepgram/listen/v2/client.py
36+
37+
# Hand-written custom tests
38+
tests/custom/test_text_builder.py
39+
tests/custom/test_transport.py
40+
2341
# Manual standalone tests
2442
tests/manual
2543

examples/14-transcription-live-websocket-v2.py

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
with contextual turn detection.
66
"""
77

8+
import os
9+
import threading
10+
import time
11+
from pathlib import Path
812
from typing import Union
913

1014
from dotenv import load_dotenv
@@ -14,42 +18,59 @@
1418
from deepgram import DeepgramClient
1519
from deepgram.core.events import EventType
1620
from deepgram.listen.v2.types import (
21+
ListenV2CloseStream,
1722
ListenV2Connected,
1823
ListenV2FatalError,
1924
ListenV2TurnInfo,
2025
)
2126

2227
ListenV2SocketClientResponse = Union[ListenV2Connected, ListenV2TurnInfo, ListenV2FatalError]
2328

24-
client = DeepgramClient()
29+
client = DeepgramClient(api_key=os.environ.get("DEEPGRAM_API_KEY"))
2530

2631
try:
27-
with client.listen.v2.connect(model="flux-general-en", encoding="linear16", sample_rate=16000) as connection:
32+
with client.listen.v2.connect(
33+
model="flux-general-en",
34+
encoding="linear16",
35+
sample_rate="16000",
36+
) as connection:
2837

2938
def on_message(message: ListenV2SocketClientResponse) -> None:
30-
msg_type = getattr(message, "type", "Unknown")
31-
print(f"Received {msg_type} event")
39+
msg_type = getattr(message, "type", type(message).__name__)
40+
print(f"Received {msg_type} event ({type(message).__name__})")
3241

3342
# Extract transcription from TurnInfo events
3443
if isinstance(message, ListenV2TurnInfo):
35-
print(f"Turn transcript: {message.transcript}")
36-
print(f"Turn event: {message.event}")
37-
print(f"Turn index: {message.turn_index}")
44+
print(f" transcript: {message.transcript}")
45+
print(f" event: {message.event}")
46+
print(f" turn_index: {message.turn_index}")
3847

3948
connection.on(EventType.OPEN, lambda _: print("Connection opened"))
4049
connection.on(EventType.MESSAGE, on_message)
4150
connection.on(EventType.CLOSE, lambda _: print("Connection closed"))
42-
connection.on(EventType.ERROR, lambda error: print(f"Error: {error}"))
51+
connection.on(EventType.ERROR, lambda error: print(f"Error: {type(error).__name__}: {error}"))
4352

44-
# Start listening - this blocks until the connection closes
45-
# In production, you would send audio data here using connection.send_media()
46-
connection.start_listening()
53+
# Send audio in a background thread so start_listening can process responses
54+
def send_audio():
55+
audio_path = Path(__file__).parent / "fixtures" / "audio.wav"
56+
with open(audio_path, "rb") as f:
57+
audio = f.read()
58+
59+
# Send in chunks
60+
chunk_size = 4096
61+
for i in range(0, len(audio), chunk_size):
62+
connection.send_media(audio[i : i + chunk_size])
63+
time.sleep(0.01) # pace the sending
64+
65+
# Signal end of audio
66+
time.sleep(2)
67+
connection.send_close_stream(ListenV2CloseStream(type="CloseStream"))
4768

48-
# For async version:
49-
# from deepgram import AsyncDeepgramClient
50-
# async with client.listen.v2.connect(...) as connection:
51-
# # ... same event handlers ...
52-
# await connection.start_listening()
69+
sender = threading.Thread(target=send_audio, daemon=True)
70+
sender.start()
71+
72+
# This blocks until the connection closes
73+
connection.start_listening()
5374

5475
except Exception as e:
55-
print(f"Error: {e}")
76+
print(f"Error: {type(e).__name__}: {e}")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ dynamic = ["version"]
44

55
[tool.poetry]
66
name = "deepgram-sdk"
7-
version = "6.0.0"
7+
version = "6.0.1"
88
description = ""
99
readme = "README.md"
1010
authors = []

src/deepgram/agent/v1/settings/think/models/raw_client.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
from ......core.api_error import ApiError
77
from ......core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
88
from ......core.http_response import AsyncHttpResponse, HttpResponse
9-
from ......core.pydantic_utilities import parse_obj_as
109
from ......core.request_options import RequestOptions
10+
from ......core.unchecked_base_model import construct_type
1111
from ......errors.bad_request_error import BadRequestError
1212
from ......types.agent_think_models_v1response import AgentThinkModelsV1Response
1313

@@ -42,7 +42,7 @@ def list(
4242
if 200 <= _response.status_code < 300:
4343
_data = typing.cast(
4444
AgentThinkModelsV1Response,
45-
parse_obj_as(
45+
construct_type(
4646
type_=AgentThinkModelsV1Response, # type: ignore
4747
object_=_response.json(),
4848
),
@@ -53,7 +53,7 @@ def list(
5353
headers=dict(_response.headers),
5454
body=typing.cast(
5555
typing.Any,
56-
parse_obj_as(
56+
construct_type(
5757
type_=typing.Any, # type: ignore
5858
object_=_response.json(),
5959
),
@@ -95,7 +95,7 @@ async def list(
9595
if 200 <= _response.status_code < 300:
9696
_data = typing.cast(
9797
AgentThinkModelsV1Response,
98-
parse_obj_as(
98+
construct_type(
9999
type_=AgentThinkModelsV1Response, # type: ignore
100100
object_=_response.json(),
101101
),
@@ -106,7 +106,7 @@ async def list(
106106
headers=dict(_response.headers),
107107
body=typing.cast(
108108
typing.Any,
109-
parse_obj_as(
109+
construct_type(
110110
type_=typing.Any, # type: ignore
111111
object_=_response.json(),
112112
),

src/deepgram/agent/v1/socket_client.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import json
44
import typing
5+
from json.decoder import JSONDecodeError
56

7+
import websockets
68
import websockets.sync.connection as websockets_sync_connection
79
from ...core.events import EventEmitterMixin, EventType
8-
from ...core.pydantic_utilities import parse_obj_as
10+
from ...core.unchecked_base_model import construct_type
911
from .types.agent_v1agent_audio_done import AgentV1AgentAudioDone
1012
from .types.agent_v1agent_started_speaking import AgentV1AgentStartedSpeaking
1113
from .types.agent_v1agent_thinking import AgentV1AgentThinking
@@ -82,7 +84,7 @@ async def __aiter__(self):
8284
if isinstance(message, bytes):
8385
yield message
8486
else:
85-
yield parse_obj_as(V1SocketClientResponse, json.loads(message)) # type: ignore
87+
yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore
8688

8789
async def start_listening(self):
8890
"""
@@ -101,7 +103,7 @@ async def start_listening(self):
101103
parsed = raw_message
102104
else:
103105
json_data = json.loads(raw_message)
104-
parsed = parse_obj_as(V1SocketClientResponse, json_data) # type: ignore
106+
parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore
105107
await self._emit_async(EventType.MESSAGE, parsed)
106108
except Exception as exc:
107109
await self._emit_async(EventType.ERROR, exc)
@@ -172,7 +174,7 @@ async def recv(self) -> V1SocketClientResponse:
172174
if isinstance(data, bytes):
173175
return data # type: ignore
174176
json_data = json.loads(data)
175-
return parse_obj_as(V1SocketClientResponse, json_data) # type: ignore
177+
return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore
176178

177179
async def _send(self, data: typing.Any) -> None:
178180
"""
@@ -199,7 +201,7 @@ def __iter__(self):
199201
if isinstance(message, bytes):
200202
yield message
201203
else:
202-
yield parse_obj_as(V1SocketClientResponse, json.loads(message)) # type: ignore
204+
yield construct_type(type_=V1SocketClientResponse, object_=json.loads(message)) # type: ignore
203205

204206
def start_listening(self):
205207
"""
@@ -218,7 +220,7 @@ def start_listening(self):
218220
parsed = raw_message
219221
else:
220222
json_data = json.loads(raw_message)
221-
parsed = parse_obj_as(V1SocketClientResponse, json_data) # type: ignore
223+
parsed = construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore
222224
self._emit(EventType.MESSAGE, parsed)
223225
except Exception as exc:
224226
self._emit(EventType.ERROR, exc)
@@ -289,7 +291,7 @@ def recv(self) -> V1SocketClientResponse:
289291
if isinstance(data, bytes):
290292
return data # type: ignore
291293
json_data = json.loads(data)
292-
return parse_obj_as(V1SocketClientResponse, json_data) # type: ignore
294+
return construct_type(type_=V1SocketClientResponse, object_=json_data) # type: ignore
293295

294296
def _send(self, data: typing.Any) -> None:
295297
"""

src/deepgram/agent/v1/types/agent_v1agent_audio_done.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
import typing
44

55
import pydantic
6-
from ....core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
6+
from ....core.pydantic_utilities import IS_PYDANTIC_V2
7+
from ....core.unchecked_base_model import UncheckedBaseModel
78

89

9-
class AgentV1AgentAudioDone(UniversalBaseModel):
10+
class AgentV1AgentAudioDone(UncheckedBaseModel):
1011
type: typing.Literal["AgentAudioDone"] = pydantic.Field(default="AgentAudioDone")
1112
"""
1213
Message type identifier indicating the agent has finished sending audio

src/deepgram/agent/v1/types/agent_v1agent_started_speaking.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
import typing
44

55
import pydantic
6-
from ....core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
6+
from ....core.pydantic_utilities import IS_PYDANTIC_V2
7+
from ....core.unchecked_base_model import UncheckedBaseModel
78

89

9-
class AgentV1AgentStartedSpeaking(UniversalBaseModel):
10+
class AgentV1AgentStartedSpeaking(UncheckedBaseModel):
1011
type: typing.Literal["AgentStartedSpeaking"] = pydantic.Field(default="AgentStartedSpeaking")
1112
"""
1213
Message type identifier for agent started speaking

src/deepgram/agent/v1/types/agent_v1agent_thinking.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
import typing
44

55
import pydantic
6-
from ....core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
6+
from ....core.pydantic_utilities import IS_PYDANTIC_V2
7+
from ....core.unchecked_base_model import UncheckedBaseModel
78

89

9-
class AgentV1AgentThinking(UniversalBaseModel):
10+
class AgentV1AgentThinking(UncheckedBaseModel):
1011
type: typing.Literal["AgentThinking"] = pydantic.Field(default="AgentThinking")
1112
"""
1213
Message type identifier for agent thinking

src/deepgram/agent/v1/types/agent_v1conversation_text.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
import typing
44

55
import pydantic
6-
from ....core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
6+
from ....core.pydantic_utilities import IS_PYDANTIC_V2
7+
from ....core.unchecked_base_model import UncheckedBaseModel
78
from .agent_v1conversation_text_role import AgentV1ConversationTextRole
89

910

10-
class AgentV1ConversationText(UniversalBaseModel):
11+
class AgentV1ConversationText(UncheckedBaseModel):
1112
type: typing.Literal["ConversationText"] = pydantic.Field(default="ConversationText")
1213
"""
1314
Message type identifier for conversation text

0 commit comments

Comments
 (0)