Repository: BerriAI/litellm

Issue:
[Bug]: SSE chunks that come from OpenRouter in a stream lose their OpenRouter ID
### What happened?

When using OpenRouter with stream=true the ids are getting generated inside litellm and not getting picked up from OpenRouter generated gen IDs.

I added in the logs the output of a small test script I have that prints the chunks as they come, I also created a fork of the main repo to add a log in the transformation.py of the openrouter right before we return the sse chunk to see what the id is equal in there (formatted as "CHUNK_ID = " + chunk["id"]). As you can see its generating a new one instead of using the one it gets.

### Relevant log output

```shell
Chunk 40 details:
  ID: chatcmpl-27788257-1cd5-489a-81f7-ad3acdc77f47
  Object: chat.completion.chunk
  Created: 1747832344
  Model: openai/gpt-3.5-turbo
  Finish reason: None
  Has usage: False

CHUNK_ID = gen-1747832343-r1R4jd69FBv5lkWPp9e6

Chunk 41 details:
  ID: chatcmpl-27788257-1cd5-489a-81f7-ad3acdc77f47
  Object: chat.completion.chunk
  Created: 1747832344
  Model: openai/gpt-3.5-turbo
  Finish reason: stop
  Has usage: False
```

### Are you a ML Ops Team?

No

### What LiteLLM version are you on ?

v1.70.3

### Twitter / LinkedIn details

https://www.linkedin.com/in/dagokogos/

Hints from issue comments:
@daarko10: Added a PR that fixes this, please have a look @krrishdholakia 😃 

Solution patch:
diff --git a/litellm/litellm_core_utils/streaming_handler.py b/litellm/litellm_core_utils/streaming_handler.py
index 785186b8a..a0767c09e 100644
--- a/litellm/litellm_core_utils/streaming_handler.py
+++ b/litellm/litellm_core_utils/streaming_handler.py
@@ -695,10 +695,13 @@ class CustomStreamWrapper:
 
         Ensure model id is always the same across all chunks.
 
-        If first chunk sent + id set, use that id for all chunks.
+        If a valid ID is received in any chunk, use it for the response.
         """
-        if self.response_id is None:
+        if id and isinstance(id, str) and id.strip():
             self.response_id = id
+        elif self.response_id is None:
+            self.response_id = id
+
         if self.response_id is not None and isinstance(self.response_id, str):
             model_response.id = self.response_id
         return model_response


Test patch:
diff --git a/tests/litellm/litellm_core_utils/test_streaming_handler.py b/tests/litellm/litellm_core_utils/test_streaming_handler.py
index 81bde88f3..c38709735 100644
--- a/tests/litellm/litellm_core_utils/test_streaming_handler.py
+++ b/tests/litellm/litellm_core_utils/test_streaming_handler.py
@@ -612,6 +612,33 @@ def test_streaming_handler_with_stop_chunk(
     assert returned_chunk is None
 
 
+def test_set_response_id_propagation_empty_to_valid(initialized_custom_stream_wrapper: CustomStreamWrapper):
+    """Test that response_id is properly set when first chunk has empty ID and second chunk has valid ID"""
+
+    model_response1 = ModelResponseStream(id="", created=1742056047, model=None)
+    model_response1 = initialized_custom_stream_wrapper.set_model_id(model_response1.id, model_response1)
+    assert model_response1.id == ""
+
+    model_response2 = ModelResponseStream(id="valid-id-123", created=1742056048, model=None)
+    model_response2 = initialized_custom_stream_wrapper.set_model_id("valid-id-123", model_response2)
+    assert model_response2.id == "valid-id-123"
+    assert initialized_custom_stream_wrapper.response_id == "valid-id-123"
+
+
+def test_set_response_id_propagation_valid_to_invalid(initialized_custom_stream_wrapper: CustomStreamWrapper):
+    """Test that response_id is maintained when first chunk has valid ID and second chunk has invalid ID"""
+
+    model_response1 = ModelResponseStream(id="first-valid-id", created=1742056049, model=None)
+    model_response1 = initialized_custom_stream_wrapper.set_model_id("first-valid-id", model_response1)
+    assert model_response1.id == "first-valid-id"
+    assert initialized_custom_stream_wrapper.response_id == "first-valid-id"
+
+    model_response2 = ModelResponseStream(id="", created=1742056050, model=None)
+    model_response2 = initialized_custom_stream_wrapper.set_model_id("", model_response2)
+    assert model_response2.id == "first-valid-id"
+    assert initialized_custom_stream_wrapper.response_id == "first-valid-id"
+
+
 @pytest.mark.asyncio
 async def test_streaming_completion_start_time(logging_obj: Logging):
     """Test that the start time is set correctly"""