From 486ee37630c1c7ed1ae98373c0303fbde8c0c863 Mon Sep 17 00:00:00 2001 From: kobeng <38723692@qq.com> Date: Sun, 12 Apr 2026 18:05:52 +0800 Subject: [PATCH] feat(llm): add stream mode support for proxies that only return content via streaming Some OpenAI-compatible proxies (e.g. jarodfund.xyz) return empty content in non-streaming mode. When stream=True is passed via OPENSPACE_LLM_CONFIG, use litellm's stream_chunk_builder to collect streaming chunks into a standard response. --- openspace/llm/client.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/openspace/llm/client.py b/openspace/llm/client.py index 611cf87..867d28f 100644 --- a/openspace/llm/client.py +++ b/openspace/llm/client.py @@ -548,14 +548,27 @@ class LLMClient: - Total max time: timeout * max_retries + sum(retry_delays) """ last_exception = None + use_stream = completion_kwargs.pop("stream", False) + if use_stream: + completion_kwargs["stream_options"] = {"include_usage": True} for attempt in range(self.max_retries): try: - # Add timeout to the completion call - response = await asyncio.wait_for( - litellm.acompletion(**completion_kwargs), - timeout=self.timeout - ) + if use_stream: + chunks = [] + async for chunk in await litellm.acompletion(stream=True, **completion_kwargs): + chunks.append(chunk) + if not chunks: + raise ValueError("Stream returned no chunks") + response = await asyncio.wait_for( + asyncio.to_thread(litellm.stream_chunk_builder, chunks, completion_kwargs.get("messages", [])), + timeout=self.timeout, + ) + else: + response = await asyncio.wait_for( + litellm.acompletion(**completion_kwargs), + timeout=self.timeout + ) return response except asyncio.TimeoutError: self._logger.error(