Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion litellm/llms/cohere/chat/v2_transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ def get_supported_openai_params(self, model: str) -> List[str]:
"stream",
"temperature",
"max_tokens",
"max_completion_tokens",
"top_p",
"frequency_penalty",
"presence_penalty",
Expand All @@ -143,7 +144,12 @@ def map_openai_params(
optional_params["stream"] = value
if param == "temperature":
optional_params["temperature"] = value
if param == "max_tokens":
if (
param == "max_tokens"
and "max_completion_tokens" not in non_default_params
):
optional_params["max_tokens"] = value
if param == "max_completion_tokens":
optional_params["max_tokens"] = value
if param == "n":
optional_params["num_generations"] = value
Expand Down
68 changes: 68 additions & 0 deletions tests/test_litellm/llms/cohere/chat/test_cohere_transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
0, os.path.abspath("../../../../..")
) # Adds the parent directory to the system path

import litellm
from litellm.llms.cohere.chat.transformation import CohereChatConfig
from litellm.llms.cohere.chat.v2_transformation import CohereV2ChatConfig


class TestCohereTransform:
Expand Down Expand Up @@ -49,3 +51,69 @@ def test_cohere_max_tokens_backward_compat(self):

# The function should properly map max_tokens if max_completion_tokens is not provided
assert result == {"temperature": 0.7, "max_tokens": 200}


class TestCohereV2Transform:
def setup_method(self):
self.config = CohereV2ChatConfig()
self.model = "command-r"

def test_v2_supports_max_completion_tokens(self):
"""max_completion_tokens must be advertised so get_optional_params does not reject it"""
assert "max_completion_tokens" in self.config.get_supported_openai_params(
self.model
)

def test_v2_max_tokens_only_still_maps(self):
"""max_tokens alone maps to cohere max_tokens when max_completion_tokens is absent"""
result = self.config.map_openai_params(
non_default_params={"temperature": 0.7, "max_tokens": 200},
optional_params={},
model=self.model,
drop_params=False,
)

assert result == {"temperature": 0.7, "max_tokens": 200}

def test_v2_map_max_completion_tokens_overrides_max_tokens(self):
"""max_completion_tokens maps to cohere max_tokens and overrides max_tokens, matching v1"""
result = self.config.map_openai_params(
non_default_params={
"temperature": 0.7,
"max_tokens": 200,
"max_completion_tokens": 256,
},
optional_params={},
model=self.model,
drop_params=False,
)

assert result == {"temperature": 0.7, "max_tokens": 256}

def test_v2_max_completion_tokens_precedence_is_order_independent(self):
"""max_completion_tokens wins over max_tokens regardless of dict ordering"""
max_tokens_first = self.config.map_openai_params(
non_default_params={"max_tokens": 200, "max_completion_tokens": 256},
optional_params={},
model=self.model,
drop_params=False,
)
max_completion_first = self.config.map_openai_params(
non_default_params={"max_completion_tokens": 256, "max_tokens": 200},
optional_params={},
model=self.model,
drop_params=False,
)

assert max_tokens_first == {"max_tokens": 256}
assert max_completion_first == {"max_tokens": 256}

def test_v2_default_route_accepts_max_completion_tokens(self):
"""The default cohere_chat route resolves to v2; max_completion_tokens must not raise"""
optional_params = litellm.get_optional_params(
model=self.model,
custom_llm_provider="cohere_chat",
max_completion_tokens=256,
)

assert optional_params["max_tokens"] == 256
Loading