From c42efe959eedfc5c0733542a5f5194c23c477dc6 Mon Sep 17 00:00:00 2001 From: Abhishek Mishra Date: Wed, 5 Aug 2026 18:50:33 +0530 Subject: [PATCH] fix(cli): pass required payment url to SmallestAIEnvironment; type-guard ws - cli/lib/client.py: SmallestAIEnvironment now requires a payment url. The custom SMALLEST_BASE_URL branch omitted it, so make_client raised TypeError at runtime whenever SMALLEST_BASE_URL was set (dev-rig / self-host). Pass payment=base. - waves/stream_tts.py: self.ws is Optional[WebSocketApp]; capture a non-None local after connect (and assert after is_connected checks) so send/close are type-safe. Clears the mypy union-attr errors in these hand-maintained files. --- src/smallestai/cli/lib/client.py | 4 +++- src/smallestai/waves/stream_tts.py | 16 +++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/smallestai/cli/lib/client.py b/src/smallestai/cli/lib/client.py index 87b4c687..bf15e3fd 100644 --- a/src/smallestai/cli/lib/client.py +++ b/src/smallestai/cli/lib/client.py @@ -36,6 +36,8 @@ def make_client(auth_client: AuthClient): base = base.rstrip("/") ws = base.replace("https://", "wss://").replace("http://", "ws://") - env = SmallestAIEnvironment(atoms=f"{base}/atoms/v1", waves=base, waves_ws=ws) + env = SmallestAIEnvironment( + atoms=f"{base}/atoms/v1", waves=base, waves_ws=ws, payment=base + ) return SmallestAI(api_key=key, environment=env) return SmallestAI(api_key=key) diff --git a/src/smallestai/waves/stream_tts.py b/src/smallestai/waves/stream_tts.py index 7c7d6722..c8262834 100644 --- a/src/smallestai/waves/stream_tts.py +++ b/src/smallestai/waves/stream_tts.py @@ -169,9 +169,11 @@ def synthesize(self, text: str) -> Generator[bytes, None, None]: """Synthesize a single text string and stream back PCM audio chunks.""" self._reset_state() self._connect() + ws = self.ws + assert ws is not None # _connect() raises unless the socket opened payload = self._create_payload(text) - self.ws.send(json.dumps(payload)) + ws.send(json.dumps(payload)) while True: if not self.error_queue.empty(): @@ -187,7 +189,7 @@ def synthesize(self, text: str) -> Generator[bytes, None, None]: break continue - self.ws.close() + ws.close() def synthesize_streaming( self, @@ -198,17 +200,19 @@ def synthesize_streaming( """Synthesize a stream of text chunks. Useful when piping LLM output.""" self._reset_state() self._connect() + ws = self.ws + assert ws is not None # _connect() raises unless the socket opened def send_text(): try: for text_chunk in text_stream: if text_chunk.strip(): payload = self._create_payload(text_chunk, continue_stream=continue_stream) - self.ws.send(json.dumps(payload)) + ws.send(json.dumps(payload)) if auto_flush: flush_payload = self._create_payload("", flush=True) - self.ws.send(json.dumps(flush_payload)) + ws.send(json.dumps(flush_payload)) except Exception as e: self.error_queue.put(e) @@ -230,17 +234,19 @@ def send_text(): break continue - self.ws.close() + ws.close() def send_text_chunk(self, text: str, continue_stream: bool = True, flush: bool = False): if not self.is_connected: raise Exception("WebSocket not connected") + assert self.ws is not None # is_connected implies the socket is set payload = self._create_payload(text, continue_stream=continue_stream, flush=flush) self.ws.send(json.dumps(payload)) def flush_buffer(self): if not self.is_connected: raise Exception("WebSocket not connected") + assert self.ws is not None # is_connected implies the socket is set payload = self._create_payload("", flush=True) self.ws.send(json.dumps(payload))