diff --git a/assemblyai/__version__.py b/assemblyai/__version__.py index 51bbb3f..31e744e 100644 --- a/assemblyai/__version__.py +++ b/assemblyai/__version__.py @@ -1 +1 @@ -__version__ = "1.6.2" +__version__ = "1.6.3" diff --git a/assemblyai/streaming/v3/models.py b/assemblyai/streaming/v3/models.py index b278dee..3acc663 100644 --- a/assemblyai/streaming/v3/models.py +++ b/assemblyai/streaming/v3/models.py @@ -402,17 +402,17 @@ class RealTimeTranscriberOptions(BaseModel): api_key: Optional[str] = None token: Optional[str] = None # Seconds to wait for the WebSocket handshake to complete before treating - # the attempt as failed. - connect_timeout: float = 1.0 + # the attempt as failed. Must be positive. + connect_timeout: float = Field(default=1.0, gt=0) # Additional handshake attempts after the first one fails on a transient # error (timeout, network drop). 0 disables retries. HTTP-level rejections - # (auth, quota, bad request) are never retried. - max_connection_retries: int = 2 - # Seconds to wait between handshake attempts. - connection_retry_delay: float = 0.5 + # (auth, quota, bad request) are never retried. Must be non-negative. + max_connection_retries: int = Field(default=2, ge=0) + # Seconds to wait between handshake attempts. Must be non-negative. + connection_retry_delay: float = Field(default=0.5, ge=0) # Seconds disconnect(terminate=True) waits for the server's - # TerminationEvent (and any final Turn) before tearing down. - terminate_timeout: float = 5.0 + # TerminationEvent (and any final Turn) before tearing down. Must be non-negative. + terminate_timeout: float = Field(default=5.0, ge=0) # Alias: the former name for `RealTimeTranscriberOptions`, bound to the same object. diff --git a/tests/unit/test_streaming.py b/tests/unit/test_streaming.py index 70049a4..fe5de94 100644 --- a/tests/unit/test_streaming.py +++ b/tests/unit/test_streaming.py @@ -2487,3 +2487,49 @@ def test_silence_event_dispatched_to_handler(mocker: MockFixture): assert isinstance(received[0], SilenceEvent) assert received[0].start_ms == 12000 assert received[0].end_ms == 13000 + + +# --------------------------------------------------------------------------- +# Boundary validation tests for RealTimeTranscriberOptions +# --------------------------------------------------------------------------- + + +def test_connect_timeout_rejects_non_positive(): + """Negative or zero connect_timeout must raise a validation error.""" + with pytest.raises(Exception): # pydantic ValidationError (v1 or v2) + StreamingClientOptions(api_key="test", connect_timeout=-1.0) + with pytest.raises(Exception): + StreamingClientOptions(api_key="test", connect_timeout=0) + + +def test_max_connection_retries_rejects_negative(): + """Negative max_connection_retries must raise a validation error.""" + with pytest.raises(Exception): + StreamingClientOptions(api_key="test", max_connection_retries=-1) + + +def test_connection_retry_delay_rejects_negative(): + """Negative connection_retry_delay must raise a validation error.""" + with pytest.raises(Exception): + StreamingClientOptions(api_key="test", connection_retry_delay=-0.5) + + +def test_terminate_timeout_rejects_negative(): + """Negative terminate_timeout must raise a validation error.""" + with pytest.raises(Exception): + StreamingClientOptions(api_key="test", terminate_timeout=-5.0) + + +def test_options_zero_values_are_accepted(): + """Zero values must be accepted for non-negative option fields (retries, delays, timeouts).""" + opts = StreamingClientOptions( + api_key="test", + connect_timeout=0.1, + max_connection_retries=0, + connection_retry_delay=0, + terminate_timeout=0, + ) + assert opts.connect_timeout == 0.1 + assert opts.max_connection_retries == 0 + assert opts.connection_retry_delay == 0 + assert opts.terminate_timeout == 0