diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json
index 6d49d6e7e2..38cf967168 100644
--- a/litellm/model_prices_and_context_window_backup.json
+++ b/litellm/model_prices_and_context_window_backup.json
@@ -19904,6 +19904,39 @@
"supports_parallel_function_calling": true,
"supports_tool_choice": true
},
+ "together_ai/moonshotai/Kimi-K2-Instruct-0905": {
+ "input_cost_per_token": 1e-06,
+ "litellm_provider": "together_ai",
+ "max_input_tokens": 262144,
+ "mode": "chat",
+ "output_cost_per_token": 3e-06,
+ "source": "https://www.together.ai/models/kimi-k2-0905",
+ "supports_function_calling": true,
+ "supports_parallel_function_calling": true,
+ "supports_tool_choice": true
+ },
+ "together_ai/Qwen/Qwen3-Next-80B-A3B-Instruct": {
+ "input_cost_per_token": 1.5e-07,
+ "litellm_provider": "together_ai",
+ "max_input_tokens": 262144,
+ "mode": "chat",
+ "output_cost_per_token": 1.5e-06,
+ "source": "https://www.together.ai/models/qwen3-next-80b-a3b-instruct",
+ "supports_function_calling": true,
+ "supports_parallel_function_calling": true,
+ "supports_tool_choice": true
+ },
+ "together_ai/Qwen/Qwen3-Next-80B-A3B-Thinking": {
+ "input_cost_per_token": 1.5e-07,
+ "litellm_provider": "together_ai",
+ "max_input_tokens": 262144,
+ "mode": "chat",
+ "output_cost_per_token": 1.5e-06,
+ "source": "https://www.together.ai/models/qwen3-next-80b-a3b-thinking",
+ "supports_function_calling": true,
+ "supports_parallel_function_calling": true,
+ "supports_tool_choice": true
+ },
"tts-1": {
"input_cost_per_character": 1.5e-05,
"litellm_provider": "openai",
diff --git a/litellm/proxy/_experimental/out/api-reference.html b/litellm/proxy/_experimental/out/api-reference/index.html
similarity index 100%
rename from litellm/proxy/_experimental/out/api-reference.html
rename to litellm/proxy/_experimental/out/api-reference/index.html
diff --git a/litellm/proxy/_experimental/out/guardrails.html b/litellm/proxy/_experimental/out/guardrails/index.html
similarity index 100%
rename from litellm/proxy/_experimental/out/guardrails.html
rename to litellm/proxy/_experimental/out/guardrails/index.html
diff --git a/litellm/proxy/_experimental/out/logs.html b/litellm/proxy/_experimental/out/logs/index.html
similarity index 100%
rename from litellm/proxy/_experimental/out/logs.html
rename to litellm/proxy/_experimental/out/logs/index.html
diff --git a/litellm/proxy/_experimental/out/model-hub.html b/litellm/proxy/_experimental/out/model-hub/index.html
similarity index 100%
rename from litellm/proxy/_experimental/out/model-hub.html
rename to litellm/proxy/_experimental/out/model-hub/index.html
diff --git a/litellm/proxy/_experimental/out/model_hub_table.html b/litellm/proxy/_experimental/out/model_hub_table/index.html
similarity index 100%
rename from litellm/proxy/_experimental/out/model_hub_table.html
rename to litellm/proxy/_experimental/out/model_hub_table/index.html
diff --git a/litellm/proxy/_experimental/out/models-and-endpoints.html b/litellm/proxy/_experimental/out/models-and-endpoints/index.html
similarity index 100%
rename from litellm/proxy/_experimental/out/models-and-endpoints.html
rename to litellm/proxy/_experimental/out/models-and-endpoints/index.html
diff --git a/litellm/proxy/_experimental/out/onboarding.html b/litellm/proxy/_experimental/out/onboarding.html
deleted file mode 100644
index 5df786e1f1..0000000000
--- a/litellm/proxy/_experimental/out/onboarding.html
+++ /dev/null
@@ -1 +0,0 @@
-
LiteLLM Dashboard
\ No newline at end of file
diff --git a/litellm/proxy/_experimental/out/organizations.html b/litellm/proxy/_experimental/out/organizations/index.html
similarity index 100%
rename from litellm/proxy/_experimental/out/organizations.html
rename to litellm/proxy/_experimental/out/organizations/index.html
diff --git a/litellm/proxy/_experimental/out/teams.html b/litellm/proxy/_experimental/out/teams/index.html
similarity index 100%
rename from litellm/proxy/_experimental/out/teams.html
rename to litellm/proxy/_experimental/out/teams/index.html
diff --git a/litellm/proxy/_experimental/out/test-key.html b/litellm/proxy/_experimental/out/test-key/index.html
similarity index 100%
rename from litellm/proxy/_experimental/out/test-key.html
rename to litellm/proxy/_experimental/out/test-key/index.html
diff --git a/litellm/proxy/_experimental/out/usage.html b/litellm/proxy/_experimental/out/usage/index.html
similarity index 100%
rename from litellm/proxy/_experimental/out/usage.html
rename to litellm/proxy/_experimental/out/usage/index.html
diff --git a/litellm/proxy/_experimental/out/users.html b/litellm/proxy/_experimental/out/users/index.html
similarity index 100%
rename from litellm/proxy/_experimental/out/users.html
rename to litellm/proxy/_experimental/out/users/index.html
diff --git a/litellm/proxy/_experimental/out/virtual-keys.html b/litellm/proxy/_experimental/out/virtual-keys/index.html
similarity index 100%
rename from litellm/proxy/_experimental/out/virtual-keys.html
rename to litellm/proxy/_experimental/out/virtual-keys/index.html
diff --git a/litellm/proxy/_new_secret_config.yaml b/litellm/proxy/_new_secret_config.yaml
index 95c6869b95..5feb779797 100644
--- a/litellm/proxy/_new_secret_config.yaml
+++ b/litellm/proxy/_new_secret_config.yaml
@@ -1,29 +1,22 @@
model_list:
- - model_name: gpt-5-mini
+ - model_name: openai/gpt-4o-mini
litellm_params:
model: openai/gpt-4o-mini
- api_base: "https://webhook.site/2f385e05-00aa-402b-86d1-efc9261471a5"
- api_key: dummy
- - model_name: "byok-wildcard/*"
+ api_key: os.environ/OPENAI_API_KEY
+ - model_name: vertex/gemini-2.5-flash
litellm_params:
- model: openai/*
- - model_name: xai-grok-3
+ model: gemini/gemini-2.5-flash
+ api_key: os.environ/GEMINI_API_KEY
+ - model_name: anthropic/claude-sonnet-4-5
litellm_params:
- model: xai/grok-3
- - model_name: hosted_vllm/whisper-v3
+ model: anthropic/claude-sonnet-4-5
+ api_key: os.environ/ANTHROPIC_API_KEY
+
+guardrails:
+ - guardrail_name: "bedrock-guardrail"
litellm_params:
- model: hosted_vllm/whisper-v3
- api_base: "https://webhook.site/2f385e05-00aa-402b-86d1-efc9261471a5"
- api_key: dummy
-
-mcp_servers:
- my_api_mcp:
- url: "http://0.0.0.0:8090"
- spec_path: "/Users/krrishdholakia/Documents/temp_py_folder/example_openapi.json"
- auth_type: none
- allowed_tools: ["getpetbyid", "my_api_mcp-findpetsbystatus"]
-
-
-litellm_settings:
- callbacks: ["prometheus"]
- custom_prometheus_metadata_labels: ["metadata.initiative", "metadata.business-unit"]
+ guardrail: bedrock
+ mode: "post_call"
+ guardrailIdentifier: gf3sc1mzinjw
+ guardrailVersion: "DRAFT"
+ disable_exception_on_block: true # Prevents exceptions when content is blocked
\ No newline at end of file
diff --git a/litellm/responses/litellm_completion_transformation/handler.py b/litellm/responses/litellm_completion_transformation/handler.py
index 9317bf2617..7e0b4cfa24 100644
--- a/litellm/responses/litellm_completion_transformation/handler.py
+++ b/litellm/responses/litellm_completion_transformation/handler.py
@@ -58,7 +58,7 @@ class LiteLLMCompletionTransformationHandler:
responses_api_request=responses_api_request,
**kwargs,
)
-
+
completion_args = {}
completion_args.update(kwargs)
completion_args.update(litellm_completion_request)
@@ -83,6 +83,7 @@ class LiteLLMCompletionTransformationHandler:
elif isinstance(litellm_completion_response, litellm.CustomStreamWrapper):
return LiteLLMCompletionStreamingIterator(
+ model=model,
litellm_custom_stream_wrapper=litellm_completion_response,
request_input=input,
responses_api_request=responses_api_request,
@@ -106,7 +107,7 @@ class LiteLLMCompletionTransformationHandler:
previous_response_id=previous_response_id,
litellm_completion_request=litellm_completion_request,
)
-
+
acompletion_args = {}
acompletion_args.update(kwargs)
acompletion_args.update(litellm_completion_request)
@@ -130,9 +131,12 @@ class LiteLLMCompletionTransformationHandler:
elif isinstance(litellm_completion_response, litellm.CustomStreamWrapper):
return LiteLLMCompletionStreamingIterator(
+ model=litellm_completion_request.get("model") or "",
litellm_custom_stream_wrapper=litellm_completion_response,
request_input=request_input,
responses_api_request=responses_api_request,
- custom_llm_provider=litellm_completion_request.get("custom_llm_provider"),
+ custom_llm_provider=litellm_completion_request.get(
+ "custom_llm_provider"
+ ),
litellm_metadata=kwargs.get("litellm_metadata", {}),
)
diff --git a/litellm/responses/litellm_completion_transformation/streaming_iterator.py b/litellm/responses/litellm_completion_transformation/streaming_iterator.py
index 20822b3628..af2d388304 100644
--- a/litellm/responses/litellm_completion_transformation/streaming_iterator.py
+++ b/litellm/responses/litellm_completion_transformation/streaming_iterator.py
@@ -1,3 +1,5 @@
+import time
+import uuid
from typing import List, Optional, Union
import litellm
@@ -8,11 +10,17 @@ from litellm.responses.litellm_completion_transformation.transformation import (
from litellm.responses.streaming_iterator import ResponsesAPIStreamingIterator
from litellm.responses.utils import ResponsesAPIRequestUtils
from litellm.types.llms.openai import (
+ BaseLiteLLMOpenAIResponseObject,
+ ContentPartAddedEvent,
+ OutputItemAddedEvent,
OutputTextDeltaEvent,
ReasoningSummaryTextDeltaEvent,
ResponseCompletedEvent,
+ ResponseCreatedEvent,
+ ResponseInProgressEvent,
ResponseInputParam,
ResponsesAPIOptionalRequestParams,
+ ResponsesAPIResponse,
ResponsesAPIStreamEvents,
ResponsesAPIStreamingResponse,
)
@@ -32,12 +40,14 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator):
def __init__(
self,
+ model: str,
litellm_custom_stream_wrapper: litellm.CustomStreamWrapper,
request_input: Union[str, ResponseInputParam],
responses_api_request: ResponsesAPIOptionalRequestParams,
custom_llm_provider: Optional[str] = None,
litellm_metadata: Optional[dict] = None,
):
+ self.model: str = model
self.litellm_custom_stream_wrapper: litellm.CustomStreamWrapper = (
litellm_custom_stream_wrapper
)
@@ -50,14 +60,139 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator):
self.collected_chat_completion_chunks: List[ModelResponseStream] = []
self.finished: bool = False
self.litellm_logging_obj = litellm_custom_stream_wrapper.logging_obj
+ self.sent_response_created_event: bool = False
+ self.sent_response_in_progress_event: bool = False
+ self.sent_output_item_added_event: bool = False
+ self.sent_content_part_added_event: bool = False
+
+ def _default_response_created_event_data(self) -> dict:
+ response_created_event_data = {
+ "id": f"resp_{str(uuid.uuid4())}",
+ "object": "response",
+ "created_at": int(time.time()),
+ "status": "in_progress",
+ "error": None,
+ "incomplete_details": None,
+ "instructions": self.request_input,
+ "max_output_tokens": None,
+ "model": self.model,
+ "output": [],
+ "parallel_tool_calls": True,
+ "previous_response_id": None,
+ "reasoning": {"effort": None, "summary": None},
+ "store": True,
+ }
+ if "temperature" in self.responses_api_request:
+ response_created_event_data["temperature"] = self.responses_api_request[
+ "temperature"
+ ]
+ if "text" in self.responses_api_request:
+ response_created_event_data["text"] = self.responses_api_request["text"]
+ if "tool_choice" in self.responses_api_request:
+ response_created_event_data["tool_choice"] = self.responses_api_request[
+ "tool_choice"
+ ]
+ else:
+ response_created_event_data["tool_choice"] = "auto"
+ if "tools" in self.responses_api_request:
+ response_created_event_data["tools"] = self.responses_api_request["tools"]
+ else:
+ response_created_event_data["tools"] = []
+ if "top_p" in self.responses_api_request:
+ response_created_event_data["top_p"] = self.responses_api_request["top_p"]
+ else:
+ response_created_event_data["top_p"] = 1.0
+ if "truncation" in self.responses_api_request:
+ response_created_event_data["truncation"] = self.responses_api_request[
+ "truncation"
+ ]
+ if "usage" in self.responses_api_request:
+ response_created_event_data["usage"] = self.responses_api_request["usage"]
+ if "user" in self.responses_api_request:
+ response_created_event_data["user"] = self.responses_api_request["user"]
+ if "metadata" in self.responses_api_request:
+ response_created_event_data["metadata"] = self.responses_api_request[
+ "metadata"
+ ]
+ return response_created_event_data
+
+ def create_response_created_event(self) -> ResponseCreatedEvent:
+ """
+ data: {"type":"response.created","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-4.1-2025-04-14","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
+
+ """
+ response_created_event_data = self._default_response_created_event_data()
+ return ResponseCreatedEvent(
+ type=ResponsesAPIStreamEvents.RESPONSE_CREATED,
+ response=ResponsesAPIResponse(**response_created_event_data),
+ )
+
+ def create_response_in_progress_event(self) -> ResponseInProgressEvent:
+ response_in_progress_event_data = self._default_response_created_event_data()
+ response_in_progress_event_data["status"] = "in_progress"
+ return ResponseInProgressEvent(
+ type=ResponsesAPIStreamEvents.RESPONSE_IN_PROGRESS,
+ response=ResponsesAPIResponse(**response_in_progress_event_data),
+ )
+
+ def create_output_item_added_event(self) -> OutputItemAddedEvent:
+ return OutputItemAddedEvent(
+ type=ResponsesAPIStreamEvents.OUTPUT_ITEM_ADDED,
+ output_index=0,
+ item=BaseLiteLLMOpenAIResponseObject(
+ **{
+ "id": f"msg_{str(uuid.uuid4())}",
+ "type": "message",
+ "status": "in_progress",
+ "role": "assistant",
+ "content": [],
+ }
+ ),
+ )
+
+ def create_content_part_added_event(self) -> ContentPartAddedEvent:
+ return ContentPartAddedEvent(
+ type=ResponsesAPIStreamEvents.CONTENT_PART_ADDED,
+ item_id=f"msg_{str(uuid.uuid4())}",
+ output_index=0,
+ content_index=0,
+ part=BaseLiteLLMOpenAIResponseObject(
+ **{"type": "output_text", "text": "", "annotations": []}
+ ),
+ )
+
+ def return_default_initial_events(
+ self,
+ ) -> Optional[BaseLiteLLMOpenAIResponseObject]:
+ if self.sent_response_created_event is False:
+ self.sent_response_created_event = True
+ return self.create_response_created_event()
+ elif self.sent_response_in_progress_event is False:
+ self.sent_response_in_progress_event = True
+ return self.create_response_in_progress_event()
+ elif self.sent_output_item_added_event is False:
+ self.sent_output_item_added_event = True
+ return self.create_output_item_added_event()
+ elif self.sent_content_part_added_event is False:
+ self.sent_content_part_added_event = True
+ return self.create_content_part_added_event()
+ return None
async def __anext__(
self,
- ) -> Union[ResponsesAPIStreamingResponse, ResponseCompletedEvent]:
+ ) -> Union[
+ ResponsesAPIStreamingResponse,
+ ResponseCompletedEvent,
+ BaseLiteLLMOpenAIResponseObject,
+ ]:
try:
while True:
if self.finished is True:
raise StopAsyncIteration
+
+ result = self.return_default_initial_events()
+ if result:
+ return result
# Get the next chunk from the stream
try:
chunk = await self.litellm_custom_stream_wrapper.__anext__()
@@ -87,12 +222,20 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator):
def __next__(
self,
- ) -> Union[ResponsesAPIStreamingResponse, ResponseCompletedEvent]:
+ ) -> Union[
+ ResponsesAPIStreamingResponse,
+ ResponseCompletedEvent,
+ BaseLiteLLMOpenAIResponseObject,
+ ]:
try:
while True:
if self.finished is True:
raise StopIteration
# Get the next chunk from the stream
+
+ result = self.return_default_initial_events()
+ if result:
+ return result
try:
chunk = self.litellm_custom_stream_wrapper.__next__()
self.collected_chat_completion_chunks.append(chunk)
@@ -168,23 +311,33 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator):
def _emit_response_completed_event(self) -> Optional[ResponseCompletedEvent]:
litellm_model_response: Optional[
Union[ModelResponse, TextCompletionResponse]
- ] = stream_chunk_builder(chunks=self.collected_chat_completion_chunks, logging_obj=self.litellm_logging_obj)
+ ] = stream_chunk_builder(
+ chunks=self.collected_chat_completion_chunks,
+ logging_obj=self.litellm_logging_obj,
+ )
if litellm_model_response and isinstance(litellm_model_response, ModelResponse):
# Add cost to usage object if include_cost_in_streaming_usage is True
- if litellm.include_cost_in_streaming_usage and self.litellm_logging_obj is not None:
+ if (
+ litellm.include_cost_in_streaming_usage
+ and self.litellm_logging_obj is not None
+ ):
usage = getattr(litellm_model_response, "usage", None)
if usage is not None:
setattr(
- usage, "cost", self.litellm_logging_obj._response_cost_calculator(result=litellm_model_response)
+ usage,
+ "cost",
+ self.litellm_logging_obj._response_cost_calculator(
+ result=litellm_model_response
+ ),
)
-
+
# Transform the response
responses_api_response = LiteLLMCompletionResponsesConfig.transform_chat_completion_response_to_responses_api_response(
request_input=self.request_input,
chat_completion_response=litellm_model_response,
responses_api_request=self.responses_api_request,
)
-
+
# Encode the response ID to match non-streaming behavior
encoded_response = ResponsesAPIRequestUtils._update_responses_api_response_id_with_model_id(
responses_api_response=responses_api_response,
diff --git a/litellm/types/llms/openai.py b/litellm/types/llms/openai.py
index 0369ed1e20..1dae91f94c 100644
--- a/litellm/types/llms/openai.py
+++ b/litellm/types/llms/openai.py
@@ -1,3 +1,4 @@
+import uuid
from enum import Enum
from os import PathLike
from typing import IO, Any, Iterable, List, Literal, Mapping, Optional, Tuple, Union
@@ -44,7 +45,7 @@ from openai.types.responses.response import (
# Handle OpenAI SDK version compatibility for Text type
try:
# fmt: off
- from openai.types.responses.response_create_params import ( # type: ignore[attr-defined]
+ from openai.types.responses.response_create_params import (
Text as ResponseText, # type: ignore[attr-defined]
)
@@ -992,7 +993,9 @@ class ResponsesAPIOptionalRequestParams(TypedDict, total=False):
prompt_cache_key: Optional[str]
stream_options: Optional[dict]
top_logprobs: Optional[int]
- partial_images: Optional[int] # Number of partial images to generate (1-3) for streaming image generation
+ partial_images: Optional[
+ int
+ ] # Number of partial images to generate (1-3) for streaming image generation
class ResponsesAPIRequestParams(ResponsesAPIOptionalRequestParams, total=False):
@@ -1180,13 +1183,13 @@ class ReasoningSummaryTextDeltaEvent(BaseLiteLLMOpenAIResponseObject):
class OutputItemAddedEvent(BaseLiteLLMOpenAIResponseObject):
type: Literal[ResponsesAPIStreamEvents.OUTPUT_ITEM_ADDED]
output_index: int
- item: Optional[dict]
+ item: Optional[BaseLiteLLMOpenAIResponseObject]
class OutputItemDoneEvent(BaseLiteLLMOpenAIResponseObject):
type: Literal[ResponsesAPIStreamEvents.OUTPUT_ITEM_DONE]
output_index: int
- item: dict
+ item: BaseLiteLLMOpenAIResponseObject
class ContentPartAddedEvent(BaseLiteLLMOpenAIResponseObject):
@@ -1194,7 +1197,7 @@ class ContentPartAddedEvent(BaseLiteLLMOpenAIResponseObject):
item_id: str
output_index: int
content_index: int
- part: dict
+ part: BaseLiteLLMOpenAIResponseObject
class ContentPartDoneEvent(BaseLiteLLMOpenAIResponseObject):
@@ -1202,7 +1205,7 @@ class ContentPartDoneEvent(BaseLiteLLMOpenAIResponseObject):
item_id: str
output_index: int
content_index: int
- part: dict
+ part: BaseLiteLLMOpenAIResponseObject
class OutputTextDeltaEvent(BaseLiteLLMOpenAIResponseObject):
@@ -1413,6 +1416,7 @@ ResponsesAPIStreamingResponse = Annotated[
ImageGenerationPartialImageEvent,
ErrorEvent,
GenericEvent,
+ BaseLiteLLMOpenAIResponseObject,
],
Discriminator("type"),
]
diff --git a/tests/llm_translation/test_anthropic_completion.py b/tests/llm_translation/test_anthropic_completion.py
index 4eeb80c419..7d9ef56f16 100644
--- a/tests/llm_translation/test_anthropic_completion.py
+++ b/tests/llm_translation/test_anthropic_completion.py
@@ -361,6 +361,7 @@ def test_process_anthropic_headers_with_no_matching_headers():
def test_anthropic_tool_use(tool_type, tool_config, message_content):
"""Test Anthropic tool use with computer use and web fetch tools."""
from litellm import completion
+
litellm._turn_on_debug()
tools = [tool_config]
@@ -1518,3 +1519,126 @@ def test_anthropic_streaming():
role_set_count += 1
assert role_set_count == 1
+
+
+def test_anthropic_via_responses_api():
+ from litellm.types.llms.openai import ResponsesAPIStreamEvents
+
+ response = litellm.responses(
+ model="anthropic/claude-sonnet-4-5",
+ input="Who won the World Cup in 2022?",
+ max_output_tokens=100,
+ stream=True,
+ )
+
+ assert response is not None
+
+ # Expected event sequence
+ expected_events = [
+ ResponsesAPIStreamEvents.RESPONSE_CREATED,
+ ResponsesAPIStreamEvents.RESPONSE_IN_PROGRESS,
+ ResponsesAPIStreamEvents.OUTPUT_ITEM_ADDED,
+ ResponsesAPIStreamEvents.CONTENT_PART_ADDED,
+ ResponsesAPIStreamEvents.OUTPUT_TEXT_DELTA, # Can occur multiple times
+ ResponsesAPIStreamEvents.OUTPUT_TEXT_DONE,
+ ResponsesAPIStreamEvents.CONTENT_PART_DONE,
+ ResponsesAPIStreamEvents.OUTPUT_ITEM_DONE,
+ ResponsesAPIStreamEvents.RESPONSE_COMPLETED,
+ ]
+
+ events_seen = []
+ text_delta_count = 0
+
+ for chunk in response:
+ print(f"chunk: {chunk}")
+
+ # Each chunk should have a type attribute
+ assert hasattr(chunk, "type"), f"Chunk missing 'type' attribute: {chunk}"
+
+ event_type = chunk.type
+
+ # Track events seen
+ if event_type == ResponsesAPIStreamEvents.OUTPUT_TEXT_DELTA:
+ text_delta_count += 1
+ if ResponsesAPIStreamEvents.OUTPUT_TEXT_DELTA not in events_seen:
+ events_seen.append(event_type)
+ else:
+ events_seen.append(event_type)
+
+ # Assert specific structures for each event type
+ if event_type == ResponsesAPIStreamEvents.RESPONSE_CREATED:
+ assert chunk.type == ResponsesAPIStreamEvents.RESPONSE_CREATED
+ assert hasattr(chunk, "response")
+ assert chunk.response.status == "in_progress"
+ assert hasattr(chunk.response, "id")
+ assert hasattr(chunk.response, "model")
+
+ elif event_type == ResponsesAPIStreamEvents.RESPONSE_IN_PROGRESS:
+ assert chunk.type == ResponsesAPIStreamEvents.RESPONSE_IN_PROGRESS
+ assert hasattr(chunk, "response")
+ assert chunk.response.status == "in_progress"
+
+ elif event_type == ResponsesAPIStreamEvents.OUTPUT_ITEM_ADDED:
+ assert chunk.type == ResponsesAPIStreamEvents.OUTPUT_ITEM_ADDED
+ assert hasattr(chunk, "output_index")
+ assert hasattr(chunk, "item")
+ assert chunk.item.type == "message"
+ assert chunk.item.role == "assistant"
+
+ elif event_type == ResponsesAPIStreamEvents.CONTENT_PART_ADDED:
+ assert chunk.type == ResponsesAPIStreamEvents.CONTENT_PART_ADDED
+ assert hasattr(chunk, "item_id")
+ assert hasattr(chunk, "output_index")
+ assert hasattr(chunk, "content_index")
+ assert hasattr(chunk, "part")
+ assert chunk.part.type == "output_text"
+
+ elif event_type == ResponsesAPIStreamEvents.OUTPUT_TEXT_DELTA:
+ assert chunk.type == ResponsesAPIStreamEvents.OUTPUT_TEXT_DELTA
+ assert hasattr(chunk, "item_id")
+ assert hasattr(chunk, "output_index")
+ assert hasattr(chunk, "content_index")
+ assert hasattr(chunk, "delta")
+ assert isinstance(chunk.delta, str)
+
+ elif event_type == ResponsesAPIStreamEvents.OUTPUT_TEXT_DONE:
+ assert chunk.type == ResponsesAPIStreamEvents.OUTPUT_TEXT_DONE
+ assert hasattr(chunk, "item_id")
+ assert hasattr(chunk, "output_index")
+ assert hasattr(chunk, "content_index")
+ assert hasattr(chunk, "text")
+
+ elif event_type == ResponsesAPIStreamEvents.CONTENT_PART_DONE:
+ assert chunk.type == ResponsesAPIStreamEvents.CONTENT_PART_DONE
+ assert hasattr(chunk, "item_id")
+ assert hasattr(chunk, "output_index")
+ assert hasattr(chunk, "content_index")
+ assert hasattr(chunk, "part")
+ assert chunk.part.type == "output_text"
+
+ elif event_type == ResponsesAPIStreamEvents.OUTPUT_ITEM_DONE:
+ assert chunk.type == ResponsesAPIStreamEvents.OUTPUT_ITEM_DONE
+ assert hasattr(chunk, "output_index")
+ assert hasattr(chunk, "item")
+ assert chunk.item.status == "completed"
+
+ elif event_type == ResponsesAPIStreamEvents.RESPONSE_COMPLETED:
+ assert chunk.type == ResponsesAPIStreamEvents.RESPONSE_COMPLETED
+ assert hasattr(chunk, "response")
+ assert chunk.response.status == "completed"
+ assert hasattr(chunk.response, "usage")
+ assert hasattr(chunk.response, "output")
+
+ # Assert we saw all expected events
+ print(f"Events seen: {events_seen}")
+ assert (
+ events_seen == expected_events
+ ), f"Event sequence mismatch. Expected: {expected_events}, Got: {events_seen}"
+
+ # Assert we saw at least one text delta
+ assert (
+ text_delta_count > 0
+ ), f"Expected at least one response.output_text.delta event, got {text_delta_count}"
+
+ print(f"✓ All {len(events_seen)} events matched expected structure")
+ print(f"✓ Received {text_delta_count} text delta chunks")
diff --git a/tests/test_litellm/responses/litellm_completion_transformation/test_reasoning_content_transformation.py b/tests/test_litellm/responses/litellm_completion_transformation/test_reasoning_content_transformation.py
index 5323589818..d1926bbc93 100644
--- a/tests/test_litellm/responses/litellm_completion_transformation/test_reasoning_content_transformation.py
+++ b/tests/test_litellm/responses/litellm_completion_transformation/test_reasoning_content_transformation.py
@@ -4,14 +4,20 @@ Test reasoning content preservation in Responses API transformation
from unittest.mock import AsyncMock
-from litellm.types.utils import ModelResponseStream, StreamingChoices, Delta
from litellm.responses.litellm_completion_transformation.streaming_iterator import (
LiteLLMCompletionStreamingIterator,
)
from litellm.responses.litellm_completion_transformation.transformation import (
LiteLLMCompletionResponsesConfig,
)
-from litellm.types.utils import ModelResponse, Choices, Message
+from litellm.types.utils import (
+ Choices,
+ Delta,
+ Message,
+ ModelResponse,
+ ModelResponseStream,
+ StreamingChoices,
+)
class TestReasoningContentStreaming:
@@ -41,6 +47,7 @@ class TestReasoningContentStreaming:
mock_stream = AsyncMock()
iterator = LiteLLMCompletionStreamingIterator(
+ model="test-model",
litellm_custom_stream_wrapper=mock_stream,
request_input="Test input",
responses_api_request={},
@@ -78,6 +85,7 @@ class TestReasoningContentStreaming:
mock_stream = AsyncMock()
iterator = LiteLLMCompletionStreamingIterator(
+ model="test-model",
litellm_custom_stream_wrapper=mock_stream,
request_input="Test input",
responses_api_request={},
@@ -114,6 +122,7 @@ class TestReasoningContentStreaming:
mock_stream = AsyncMock()
iterator = LiteLLMCompletionStreamingIterator(
+ model="test-model",
litellm_custom_stream_wrapper=mock_stream,
request_input="Test input",
responses_api_request={},