mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-26 06:03:19 +00:00
* fix(openai): send chat completions through client.post so the SDK's per-message param transform stops holding the event loop create() walks every message, content part and tool of the request through openai._utils._transform before posting, 91 ms at 300k tokens and ~300 ms at 1M tokens on the event loop, and returns the same dict for the plain dicts litellm builds. client.post is what create() calls after that walk, same request, headers, retries, raw response and stream objects, so the chat helpers now call it directly through the helper embeddings already used * fix(openai): type the chat client.post result as LegacyAPIResponse so the type gate sees .headers and .parse * fix(openai): assert the chat client.post result is a LegacyAPIResponse instead of casting it * test(openai): stub client.post with a LegacyAPIResponse spec in the empty-response and no-leak tests * test(core-utils): stub client.post with a LegacyAPIResponse spec in the agentic loop leak test * test: every client.post stub on the OpenAI chat path returns a LegacyAPIResponse-spec'd mock --------- Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
148 lines
5.3 KiB
Python
148 lines
5.3 KiB
Python
import os
|
|
import sys
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from openai._legacy_response import LegacyAPIResponse
|
|
from pydantic import BaseModel
|
|
|
|
from litellm.llms.volcengine.chat.transformation import (
|
|
VolcEngineChatConfig as VolcEngineConfig,
|
|
)
|
|
from litellm.utils import get_optional_params
|
|
|
|
|
|
class TestVolcEngineConfig:
|
|
def test_get_optional_params(self):
|
|
config = VolcEngineConfig()
|
|
supported_params = config.get_supported_openai_params(model="doubao-seed-1.6")
|
|
assert "thinking" in supported_params
|
|
|
|
# Test thinking disabled - should appear in extra_body
|
|
mapped_params = config.map_openai_params(
|
|
non_default_params={
|
|
"thinking": {"type": "disabled"},
|
|
},
|
|
optional_params={},
|
|
model="doubao-seed-1.6",
|
|
drop_params=False,
|
|
)
|
|
|
|
# Fixed: thinking disabled should appear in extra_body
|
|
assert mapped_params == {"extra_body": {"thinking": {"type": "disabled"}}}
|
|
|
|
e2e_mapped_params = get_optional_params(
|
|
model="doubao-seed-1.6",
|
|
custom_llm_provider="volcengine",
|
|
thinking={"type": "enabled"},
|
|
drop_params=False,
|
|
)
|
|
|
|
assert "thinking" in e2e_mapped_params["extra_body"] and e2e_mapped_params[
|
|
"extra_body"
|
|
]["thinking"] == {
|
|
"type": "enabled",
|
|
}
|
|
|
|
def test_thinking_parameter_handling(self):
|
|
"""Test comprehensive thinking parameter handling scenarios"""
|
|
config = VolcEngineConfig()
|
|
|
|
# Test 1: thinking enabled - should appear in extra_body
|
|
result_enabled = config.map_openai_params(
|
|
non_default_params={"thinking": {"type": "enabled"}},
|
|
optional_params={},
|
|
model="doubao-seed-1.6",
|
|
drop_params=False,
|
|
)
|
|
assert result_enabled == {"extra_body": {"thinking": {"type": "enabled"}}}
|
|
|
|
# Test 2: thinking None - should NOT appear in extra_body
|
|
result_none = config.map_openai_params(
|
|
non_default_params={"thinking": None},
|
|
optional_params={},
|
|
model="doubao-seed-1.6",
|
|
drop_params=False,
|
|
)
|
|
assert result_none == {}
|
|
|
|
# Test 3: thinking with custom value - should NOT appear in extra_body (invalid value)
|
|
result_custom = config.map_openai_params(
|
|
non_default_params={"thinking": "custom_mode"},
|
|
optional_params={},
|
|
model="doubao-seed-1.6",
|
|
drop_params=False,
|
|
)
|
|
assert result_custom == {}
|
|
|
|
# Test 4: thinking disabled - should appear in extra_body with original structure
|
|
result_disabled = config.map_openai_params(
|
|
non_default_params={"thinking": {"type": "disabled"}},
|
|
optional_params={},
|
|
model="doubao-seed-1.6",
|
|
drop_params=False,
|
|
)
|
|
assert result_disabled == {"extra_body": {"thinking": {"type": "disabled"}}}
|
|
|
|
# Test 5: No thinking parameter - should return empty dict
|
|
result_no_thinking = config.map_openai_params(
|
|
non_default_params={},
|
|
optional_params={},
|
|
model="doubao-seed-1.6",
|
|
drop_params=False,
|
|
)
|
|
assert result_no_thinking == {}
|
|
|
|
# Test 6: invalid thinking type - should NOT appear in extra_body (invalid type)
|
|
result_no_thinking = config.map_openai_params(
|
|
non_default_params={"thinking": {"type": "invalid_type"}},
|
|
optional_params={},
|
|
model="doubao-seed-1.6",
|
|
drop_params=False,
|
|
)
|
|
assert result_no_thinking == {}
|
|
|
|
# Test 7: invalid thinking type - should NOT appear in extra_body (value is None)
|
|
result_no_thinking = config.map_openai_params(
|
|
non_default_params={"thinking": {"type": None}},
|
|
optional_params={},
|
|
model="doubao-seed-1.6",
|
|
drop_params=False,
|
|
)
|
|
assert result_no_thinking == {}
|
|
|
|
def test_e2e_completion(self):
|
|
from openai import OpenAI
|
|
|
|
from litellm import completion
|
|
from litellm.types.utils import ModelResponse
|
|
|
|
client = OpenAI(api_key="test_api_key")
|
|
|
|
mock_raw_response = MagicMock(spec=LegacyAPIResponse)
|
|
mock_raw_response.headers = {
|
|
"x-request-id": "123",
|
|
"openai-organization": "org-123",
|
|
"x-ratelimit-limit-requests": "100",
|
|
"x-ratelimit-remaining-requests": "99",
|
|
}
|
|
mock_raw_response.parse.return_value = ModelResponse()
|
|
|
|
with patch.object(client, "post", MagicMock(return_value=mock_raw_response)) as mock_create:
|
|
completion(
|
|
model="volcengine/doubao-seed-1.6",
|
|
messages=[
|
|
{
|
|
"role": "system",
|
|
"content": "**Tell me your model detail information.**",
|
|
}
|
|
],
|
|
user="guest",
|
|
stream=True,
|
|
thinking={"type": "disabled"},
|
|
client=client,
|
|
)
|
|
|
|
mock_create.assert_called_once()
|
|
print(mock_create.call_args.kwargs["body"])
|
|
# Fixed: thinking disabled should appear in extra_body with original structure
|
|
assert mock_create.call_args.kwargs["options"]["extra_json"]["thinking"] == {"type": "disabled"}
|