mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
fix(ovhcloud): fix tool calling
This commit is contained in:
parent
850fe595ac
commit
844e050768
2 changed files with 76 additions and 67 deletions
|
|
@ -7,9 +7,8 @@ More information on our website: https://endpoints.ai.cloud.ovh.net
|
|||
from typing import Optional, Union, List
|
||||
|
||||
import httpx
|
||||
from litellm.utils import ModelResponseStream, _get_model_info_helper
|
||||
from litellm.utils import ModelResponseStream
|
||||
from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig
|
||||
from litellm._logging import verbose_logger
|
||||
from litellm.llms.ovhcloud.utils import OVHCloudException
|
||||
from litellm.llms.base_llm.base_model_iterator import BaseModelResponseIterator
|
||||
from litellm.llms.base_llm.chat.transformation import BaseLLMException
|
||||
|
|
@ -21,34 +20,6 @@ class OVHCloudChatConfig(OpenAIGPTConfig):
|
|||
def custom_llm_provider(self) -> Optional[str]:
|
||||
return "ovhcloud"
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> list:
|
||||
"""
|
||||
Details about function calling support can be found here:
|
||||
https://help.ovhcloud.com/csm/en-gb-public-cloud-ai-endpoints-function-calling?id=kb_article_view&sysparm_article=KB0071907
|
||||
"""
|
||||
supports_function_calling: Optional[bool] = None
|
||||
try:
|
||||
model_info = _get_model_info_helper(model, custom_llm_provider="ovhcloud")
|
||||
supports_function_calling = model_info.get(
|
||||
"supports_function_calling", None
|
||||
)
|
||||
if supports_function_calling is None:
|
||||
supports_function_calling = False
|
||||
except Exception as e:
|
||||
verbose_logger.debug(f"Error getting supported OpenAI params: {e}")
|
||||
supports_function_calling = False
|
||||
|
||||
optional_params = super().get_supported_openai_params(model)
|
||||
if supports_function_calling is not True:
|
||||
verbose_logger.debug(
|
||||
"You can see our models supporting function_calling in our catalog: https://endpoints.ai.cloud.ovh.net/catalog "
|
||||
)
|
||||
optional_params.remove("tools")
|
||||
optional_params.remove("tool_choice")
|
||||
optional_params.remove("function_call")
|
||||
optional_params.remove("response_format")
|
||||
return optional_params
|
||||
|
||||
def get_complete_url(
|
||||
self,
|
||||
api_base: Optional[str],
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ from litellm.llms.ovhcloud.chat.transformation import (
|
|||
config = OVHCloudChatConfig()
|
||||
model = "ovhcloud/Mistral-7B-Instruct-v0.3"
|
||||
|
||||
|
||||
class TestOvhCloudChatCompletionStreamingHandler:
|
||||
def test_chunk_parser_successful(self):
|
||||
handler = OVHCloudChatCompletionStreamingHandler(
|
||||
|
|
@ -58,7 +59,7 @@ class TestOvhCloudChatCompletionStreamingHandler:
|
|||
"error": {
|
||||
"message": "test error",
|
||||
"code": 400,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
with pytest.raises(OVHCloudException) as exc_info:
|
||||
|
|
@ -83,12 +84,10 @@ class TestOvhCloudChatCompletionStreamingHandler:
|
|||
|
||||
class TestOVHCloudConfig:
|
||||
def test_transform_request_basic(self):
|
||||
"""Test basic request transformation"""
|
||||
"""Test basic request transformation"""
|
||||
transformed_request = config.transform_request(
|
||||
model,
|
||||
messages=[
|
||||
{"role": "user", "content": "Hello, world!"}
|
||||
],
|
||||
messages=[{"role": "user", "content": "Hello, world!"}],
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
headers={},
|
||||
|
|
@ -100,7 +99,7 @@ class TestOVHCloudConfig:
|
|||
]
|
||||
|
||||
def test_transform_request_with_extra_body(self):
|
||||
"""Test request transformation with extra_body parameters"""
|
||||
"""Test request transformation with extra_body parameters"""
|
||||
transformed_request = config.transform_request(
|
||||
model,
|
||||
messages=[{"role": "user", "content": "Hello, world!"}],
|
||||
|
|
@ -115,60 +114,94 @@ class TestOVHCloudConfig:
|
|||
]
|
||||
|
||||
def test_map_openai_params(self):
|
||||
"""Test OpenAI parameter mapping"""
|
||||
"""Test OpenAI parameter mapping"""
|
||||
non_default_params = {
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 100,
|
||||
"top_p": 0.9,
|
||||
}
|
||||
|
||||
|
||||
mapped_params = config.map_openai_params(
|
||||
non_default_params=non_default_params,
|
||||
optional_params={},
|
||||
model=model,
|
||||
drop_params=False,
|
||||
)
|
||||
|
||||
|
||||
assert mapped_params["temperature"] == 0.7
|
||||
assert mapped_params["max_tokens"] == 100
|
||||
assert mapped_params["top_p"] == 0.9
|
||||
|
||||
def test_get_error_class(self):
|
||||
"""Test error class creation"""
|
||||
"""Test error class creation"""
|
||||
error = config.get_error_class(
|
||||
error_message="Test error",
|
||||
status_code=400,
|
||||
headers={"Content-Type": "application/json"}
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
|
||||
assert isinstance(error, OVHCloudException)
|
||||
assert error.message == "Test error"
|
||||
assert error.status_code == 400
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"model",
|
||||
[
|
||||
"Meta-Llama-3_3-70B-Instruct",
|
||||
"Meta-Llama-3_1-70B-Instruct",
|
||||
"Mixtral-8x7B-Instruct-v0.1",
|
||||
"gpt-oss-120b",
|
||||
"some-model-not-in-the-cost-map",
|
||||
],
|
||||
)
|
||||
def test_tools_not_filtered_by_static_model_map(self, model):
|
||||
"""
|
||||
OVHCloud AI Endpoints are OpenAI-compatible; tools/tool_choice must pass
|
||||
through for any model. The server is responsible for rejecting unsupported
|
||||
tool calls — LiteLLM must not strip them based on a stale static catalog.
|
||||
"""
|
||||
from litellm.utils import get_optional_params
|
||||
|
||||
params = get_optional_params(
|
||||
model=model,
|
||||
custom_llm_provider="ovhcloud",
|
||||
tools=[
|
||||
{
|
||||
"type": "function",
|
||||
"function": {"name": "x", "parameters": {}},
|
||||
}
|
||||
],
|
||||
tool_choice="auto",
|
||||
)
|
||||
|
||||
assert "tools" in params
|
||||
assert "tool_choice" in params
|
||||
|
||||
|
||||
def test_ovhcloud_integration():
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
api_key = os.getenv("OVHCLOUD_API_KEY")
|
||||
|
||||
|
||||
api_key = os.getenv("OVHCLOUD_API_KEY")
|
||||
|
||||
if not api_key:
|
||||
pytest.skip("OVHCLOUD_API_KEY not set, skipping test")
|
||||
|
||||
|
||||
response = completion(
|
||||
model,
|
||||
messages=[{"role": "user", "content": "Say hello in one word"}],
|
||||
api_key=api_key,
|
||||
max_tokens=10,
|
||||
temperature=0.7
|
||||
temperature=0.7,
|
||||
)
|
||||
|
||||
|
||||
assert response.choices[0].message.content
|
||||
assert len(response.choices[0].message.content.strip()) > 0
|
||||
assert response.model
|
||||
assert response.usage
|
||||
assert response.usage.total_tokens > 0
|
||||
|
||||
|
||||
def test_OVHCloud_streaming_integration():
|
||||
"""
|
||||
Integration test for streaming - requires real API key
|
||||
|
|
@ -176,22 +209,24 @@ def test_OVHCloud_streaming_integration():
|
|||
"""
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
api_key = os.getenv("OVHCLOUD_API_KEY")
|
||||
|
||||
|
||||
api_key = os.getenv("OVHCLOUD_API_KEY")
|
||||
|
||||
if not api_key:
|
||||
pytest.skip("OVHCLOUD_API_KEY not set, skipping test")
|
||||
|
||||
|
||||
try:
|
||||
print(f"🔍 Testing streaming with API key: {api_key[:6]}...{api_key[-4:]} (length: {len(api_key)})")
|
||||
print(
|
||||
f"🔍 Testing streaming with API key: {api_key[:6]}...{api_key[-4:]} (length: {len(api_key)})"
|
||||
)
|
||||
print(f"🔍 API base URL: {os.getenv('OVHCLOUD_API_BASE')}")
|
||||
|
||||
|
||||
response = completion(
|
||||
model,
|
||||
messages=[{"role": "user", "content": "Count from 1 to 5"}],
|
||||
api_key=api_key,
|
||||
max_tokens=50,
|
||||
stream=True
|
||||
stream=True,
|
||||
)
|
||||
|
||||
chunks = []
|
||||
|
|
@ -215,42 +250,45 @@ def test_OVHCloud_streaming_integration():
|
|||
print(f"❌ Streaming integration test error details:")
|
||||
print(f" Error type: {type(e).__name__}")
|
||||
print(f" Error message: {str(e)}")
|
||||
if hasattr(e, 'status_code'):
|
||||
if hasattr(e, "status_code"):
|
||||
print(f" Status code: {e.status_code}")
|
||||
if hasattr(e, 'response'):
|
||||
if hasattr(e, "response"):
|
||||
print(f" Response: {e.response}")
|
||||
|
||||
|
||||
pytest.fail(f"Streaming integration test failed: {type(e).__name__}: {str(e)}")
|
||||
|
||||
|
||||
def test_ovhcloud_with_custom_base_url():
|
||||
"""
|
||||
Test OVHCloud with custom base URL
|
||||
"""
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
api_key = os.getenv("OVHCLOUD_API_KEY")
|
||||
|
||||
|
||||
api_key = os.getenv("OVHCLOUD_API_KEY")
|
||||
|
||||
if not api_key:
|
||||
pytest.skip("OVHCLOUD_API_KEY not set, skipping test")
|
||||
|
||||
custom_base_url = os.getenv("OVHCLOUD_API_BASE", "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1")
|
||||
|
||||
custom_base_url = os.getenv(
|
||||
"OVHCLOUD_API_BASE", "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1"
|
||||
)
|
||||
|
||||
try:
|
||||
response = completion(
|
||||
model,
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
api_key=api_key,
|
||||
api_base=custom_base_url,
|
||||
max_tokens=5
|
||||
max_tokens=5,
|
||||
)
|
||||
|
||||
|
||||
assert response.choices[0].message.content
|
||||
print(f"✅ Custom base URL test passed: {response.choices[0].message.content}")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
pytest.fail(f"Custom base URL test failed: {str(e)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
pytest.main([__file__, "-v"])
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue