mirror of
https://github.com/tiennm99/litellm.git
synced 2026-06-17 14:48:44 +00:00
208 lines
6.3 KiB
Python
208 lines
6.3 KiB
Python
# What is this?
|
|
## Unit tests for the 'function_setup()' function
|
|
import sys, os
|
|
import traceback
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv()
|
|
import os, io
|
|
|
|
sys.path.insert(
|
|
0, os.path.abspath("../..")
|
|
) # Adds the parent directory to the system path
|
|
import pytest, uuid
|
|
from litellm.utils import function_setup, Rules
|
|
from litellm.litellm_core_utils.prompt_templates.factory import THOUGHT_SIGNATURE_SEPARATOR
|
|
from datetime import datetime
|
|
|
|
|
|
def test_empty_content():
|
|
"""
|
|
Make a chat completions request with empty content -> expect this to work
|
|
"""
|
|
rules_obj = Rules()
|
|
|
|
def completion():
|
|
pass
|
|
|
|
function_setup(
|
|
original_function="completion",
|
|
rules_obj=rules_obj,
|
|
start_time=datetime.now(),
|
|
messages=[],
|
|
litellm_call_id=str(uuid.uuid4()),
|
|
)
|
|
|
|
|
|
def test_thought_signature_removal_for_non_gemini():
|
|
"""
|
|
Test that thought signatures are removed from tool call IDs when sending to non-Gemini models
|
|
"""
|
|
rules_obj = Rules()
|
|
|
|
# Create messages with thought signatures (as would come from Gemini)
|
|
messages = [
|
|
{"role": "user", "content": "What's the weather?"},
|
|
{
|
|
"role": "assistant",
|
|
"tool_calls": [
|
|
{
|
|
"id": f"call_123{THOUGHT_SIGNATURE_SEPARATOR}sig1",
|
|
"type": "function",
|
|
"function": {
|
|
"name": "get_weather",
|
|
"arguments": '{"location": "SF"}'
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"role": "tool",
|
|
"tool_call_id": f"call_123{THOUGHT_SIGNATURE_SEPARATOR}sig1",
|
|
"content": "Sunny, 72°F"
|
|
}
|
|
]
|
|
|
|
# Call function_setup with OpenAI model (non-Gemini)
|
|
logging_obj, kwargs = function_setup(
|
|
original_function="acompletion",
|
|
rules_obj=rules_obj,
|
|
start_time=datetime.now(),
|
|
model="gpt-4",
|
|
messages=messages,
|
|
litellm_call_id=str(uuid.uuid4()),
|
|
custom_llm_provider="openai"
|
|
)
|
|
|
|
# Verify thought signatures were removed
|
|
processed_messages = kwargs["messages"]
|
|
assert processed_messages[1]["tool_calls"][0]["id"] == "call_123"
|
|
assert processed_messages[2]["tool_call_id"] == "call_123"
|
|
assert THOUGHT_SIGNATURE_SEPARATOR not in processed_messages[1]["tool_calls"][0]["id"]
|
|
assert THOUGHT_SIGNATURE_SEPARATOR not in processed_messages[2]["tool_call_id"]
|
|
|
|
|
|
def test_thought_signature_preserved_for_gemini():
|
|
"""
|
|
Test that thought signatures are preserved when sending to Gemini models
|
|
"""
|
|
rules_obj = Rules()
|
|
|
|
# Create messages with thought signatures
|
|
messages = [
|
|
{"role": "user", "content": "What's the weather?"},
|
|
{
|
|
"role": "assistant",
|
|
"tool_calls": [
|
|
{
|
|
"id": f"call_456{THOUGHT_SIGNATURE_SEPARATOR}sig2",
|
|
"type": "function",
|
|
"function": {
|
|
"name": "get_weather",
|
|
"arguments": '{"location": "NYC"}'
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"role": "tool",
|
|
"tool_call_id": f"call_456{THOUGHT_SIGNATURE_SEPARATOR}sig2",
|
|
"content": "Rainy, 65°F"
|
|
}
|
|
]
|
|
|
|
# Call function_setup with Gemini model
|
|
logging_obj, kwargs = function_setup(
|
|
original_function="acompletion",
|
|
rules_obj=rules_obj,
|
|
start_time=datetime.now(),
|
|
model="gemini-1.5-pro",
|
|
messages=messages,
|
|
litellm_call_id=str(uuid.uuid4()),
|
|
custom_llm_provider="vertex_ai"
|
|
)
|
|
|
|
# Verify thought signatures were preserved (messages should be unchanged)
|
|
processed_messages = kwargs["messages"]
|
|
assert THOUGHT_SIGNATURE_SEPARATOR in processed_messages[1]["tool_calls"][0]["id"]
|
|
assert THOUGHT_SIGNATURE_SEPARATOR in processed_messages[2]["tool_call_id"]
|
|
|
|
|
|
def test_thought_signature_removal_with_multiple_tool_calls():
|
|
"""
|
|
Test that thought signatures are removed from multiple tool calls
|
|
"""
|
|
rules_obj = Rules()
|
|
|
|
messages = [
|
|
{"role": "user", "content": "Get weather and time"},
|
|
{
|
|
"role": "assistant",
|
|
"tool_calls": [
|
|
{
|
|
"id": f"call_1{THOUGHT_SIGNATURE_SEPARATOR}sig1",
|
|
"type": "function",
|
|
"function": {"name": "get_weather", "arguments": "{}"}
|
|
},
|
|
{
|
|
"id": f"call_2{THOUGHT_SIGNATURE_SEPARATOR}sig2",
|
|
"type": "function",
|
|
"function": {"name": "get_time", "arguments": "{}"}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"role": "tool",
|
|
"tool_call_id": f"call_1{THOUGHT_SIGNATURE_SEPARATOR}sig1",
|
|
"content": "Sunny"
|
|
},
|
|
{
|
|
"role": "tool",
|
|
"tool_call_id": f"call_2{THOUGHT_SIGNATURE_SEPARATOR}sig2",
|
|
"content": "3:00 PM"
|
|
}
|
|
]
|
|
|
|
logging_obj, kwargs = function_setup(
|
|
original_function="acompletion",
|
|
rules_obj=rules_obj,
|
|
start_time=datetime.now(),
|
|
model="claude-3-opus",
|
|
messages=messages,
|
|
litellm_call_id=str(uuid.uuid4()),
|
|
custom_llm_provider="anthropic"
|
|
)
|
|
|
|
processed_messages = kwargs["messages"]
|
|
|
|
# Check all tool call IDs are cleaned
|
|
assert processed_messages[1]["tool_calls"][0]["id"] == "call_1"
|
|
assert processed_messages[1]["tool_calls"][1]["id"] == "call_2"
|
|
assert processed_messages[2]["tool_call_id"] == "call_1"
|
|
assert processed_messages[3]["tool_call_id"] == "call_2"
|
|
|
|
|
|
def test_messages_without_tool_calls_unchanged():
|
|
"""
|
|
Test that messages without tool calls pass through unchanged
|
|
"""
|
|
rules_obj = Rules()
|
|
|
|
messages = [
|
|
{"role": "user", "content": "Hello"},
|
|
{"role": "assistant", "content": "Hi there!"}
|
|
]
|
|
|
|
logging_obj, kwargs = function_setup(
|
|
original_function="acompletion",
|
|
rules_obj=rules_obj,
|
|
start_time=datetime.now(),
|
|
model="gpt-4",
|
|
messages=messages,
|
|
litellm_call_id=str(uuid.uuid4()),
|
|
custom_llm_provider="openai"
|
|
)
|
|
|
|
# Messages should be unchanged
|
|
assert kwargs["messages"] == messages
|