mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-06 08:16:43 +00:00
* test: drop the cwd-relative sys.path.insert calls from the test suite
TQ003 stands at 1,077 across 1,058 files, and 1,015 of them are the same shape:
sys.path.insert(0, os.path.abspath("../..")) and its deeper siblings. The
argument resolves against the working directory rather than the file, so from
the repo root, where every job runs pytest, it inserts the directory two levels
above the checkout. It has never pointed at litellm. The package is installed
into the environment anyway, which is what actually makes the import work, and
what the rule's message has said all along.
Removing them leaves 1,634 imports of sys and os with no remaining reference,
and those go too, except where another test module imports the name back out of
the file. The rest of TQ003 is 62 call sites that resolve against __file__ or a
variable, which are a different question and are left alone.
Collection is identical either way: 45,871 tests and the same 51 pre-existing
collection errors before and after, and ruff reports no new undefined name.
* test: drop the duplicate imports the sys.path sweep exposed to F811
* test(pre-call-utils): restore the os import the new bedrock tests need
343 lines
11 KiB
Python
343 lines
11 KiB
Python
"""
|
|
Integration tests for Google Interactions API.
|
|
|
|
Tests the litellm.interactions.create() and related methods against the Google AI Studio API.
|
|
|
|
Per OpenAPI spec: https://ai.google.dev/static/api/interactions.openapi.json
|
|
|
|
Run with: pytest tests/test_litellm/interactions/test_google_interactions_integration.py -v
|
|
"""
|
|
|
|
import asyncio
|
|
import os
|
|
|
|
import pytest
|
|
|
|
|
|
import litellm
|
|
import litellm.interactions as interactions
|
|
import openai
|
|
|
|
# Test API key - should be set in environment
|
|
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
|
|
|
|
|
|
@pytest.fixture
|
|
def api_key():
|
|
"""Fixture to provide the API key."""
|
|
if not GEMINI_API_KEY:
|
|
pytest.skip("GEMINI_API_KEY not set")
|
|
return GEMINI_API_KEY
|
|
|
|
|
|
class TestGoogleInteractionsCreate:
|
|
"""Tests for creating interactions via litellm.interactions.create()."""
|
|
|
|
def test_create_simple_string_input(self, api_key):
|
|
"""Test creating an interaction with a simple string input."""
|
|
response = interactions.create(
|
|
model="gemini/gemini-2.5-flash",
|
|
input="Hello, what is 2 + 2?",
|
|
api_key=api_key,
|
|
)
|
|
print("SIMPLE RESPONSE: ", response)
|
|
assert response is not None
|
|
assert response.id is not None or response.status is not None
|
|
|
|
# Check outputs per OpenAPI spec
|
|
if response.outputs:
|
|
assert len(response.outputs) > 0
|
|
print(f"Response outputs: {response.outputs}")
|
|
|
|
# Check usage per OpenAPI spec
|
|
if response.usage:
|
|
print(f"Usage: {response.usage}")
|
|
|
|
def test_create_with_content_list(self, api_key):
|
|
"""Test creating an interaction with a structured content list (Content[] input)."""
|
|
response = interactions.create(
|
|
model="gemini/gemini-2.5-flash",
|
|
input=[{"type": "text", "text": "What is the capital of France?"}],
|
|
api_key=api_key,
|
|
)
|
|
|
|
assert response is not None
|
|
print(f"Response: {response}")
|
|
|
|
def test_create_with_system_instruction(self, api_key):
|
|
"""Test creating an interaction with system_instruction (per OpenAPI spec)."""
|
|
response = interactions.create(
|
|
model="gemini/gemini-2.5-flash",
|
|
input="What are you?",
|
|
system_instruction="You are a helpful pirate assistant. Always respond like a pirate.",
|
|
api_key=api_key,
|
|
)
|
|
|
|
assert response is not None
|
|
print(f"Response with system_instruction: {response}")
|
|
|
|
def test_create_with_tools(self, api_key):
|
|
"""Test creating an interaction with tools (per OpenAPI spec)."""
|
|
response = interactions.create(
|
|
model="gemini/gemini-2.5-flash",
|
|
input="What's the weather in Boston?",
|
|
tools=[
|
|
{
|
|
"type": "function",
|
|
"name": "get_weather",
|
|
"description": "Get the weather for a location",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"location": {
|
|
"type": "string",
|
|
"description": "The city name",
|
|
}
|
|
},
|
|
"required": ["location"],
|
|
},
|
|
}
|
|
],
|
|
api_key=api_key,
|
|
)
|
|
|
|
assert response is not None
|
|
# Check if status is requires_action (function call)
|
|
print(f"Response status: {response.status}")
|
|
print(f"Response outputs: {response.outputs}")
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_acreate_simple(self, api_key):
|
|
"""Test async interaction creation."""
|
|
response = await interactions.acreate(
|
|
model="gemini/gemini-2.5-flash",
|
|
input="What is the speed of light?",
|
|
api_key=api_key,
|
|
)
|
|
|
|
assert response is not None
|
|
print(f"Async response: {response}")
|
|
|
|
|
|
class TestGoogleInteractionsStreaming:
|
|
"""Tests for streaming interactions."""
|
|
|
|
def test_create_streaming(self, api_key):
|
|
"""Test creating a streaming interaction."""
|
|
response_stream = interactions.create(
|
|
model="gemini/gemini-2.5-flash",
|
|
input="Count from 1 to 5 slowly.",
|
|
stream=True,
|
|
api_key=api_key,
|
|
)
|
|
|
|
# Collect all chunks
|
|
chunks = []
|
|
for chunk in response_stream:
|
|
chunks.append(chunk)
|
|
print(f"Streaming chunk: {chunk}")
|
|
|
|
assert len(chunks) > 0
|
|
print(f"Total chunks received: {len(chunks)}")
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_acreate_streaming(self, api_key):
|
|
"""Test async streaming interaction."""
|
|
response_stream = await interactions.acreate(
|
|
model="gemini/gemini-2.5-flash",
|
|
input="Count from 1 to 3.",
|
|
stream=True,
|
|
api_key=api_key,
|
|
)
|
|
|
|
# Collect all chunks
|
|
chunks = []
|
|
async for chunk in response_stream:
|
|
chunks.append(chunk)
|
|
print(f"Async streaming chunk: {chunk}")
|
|
|
|
assert len(chunks) > 0
|
|
print(f"Total async chunks received: {len(chunks)}")
|
|
|
|
|
|
class TestGoogleInteractionsMultiTurn:
|
|
"""Tests for multi-turn conversations using Step[] input."""
|
|
|
|
def test_multi_turn_conversation(self, api_key):
|
|
"""Test a multi-turn conversation per OpenAPI spec (Step[] format)."""
|
|
response = interactions.create(
|
|
model="gemini/gemini-2.5-flash",
|
|
input=[
|
|
{
|
|
"type": "user_input",
|
|
"content": [{"type": "text", "text": "My name is Alice."}],
|
|
},
|
|
{
|
|
"type": "model_output",
|
|
"content": [
|
|
{"type": "text", "text": "Hello Alice! Nice to meet you."}
|
|
],
|
|
},
|
|
{
|
|
"type": "user_input",
|
|
"content": [{"type": "text", "text": "What is my name?"}],
|
|
},
|
|
],
|
|
api_key=api_key,
|
|
)
|
|
|
|
assert response is not None
|
|
print(f"Multi-turn response: {response}")
|
|
|
|
|
|
class TestGoogleInteractionsAgent:
|
|
"""Tests for agent interactions (per OpenAPI spec)."""
|
|
|
|
@pytest.mark.skip(reason="Deep research agent may not be available in all accounts")
|
|
def test_create_agent_interaction(self, api_key):
|
|
"""Test creating an agent interaction per OpenAPI spec."""
|
|
response = interactions.create(
|
|
agent="deep-research-pro-preview-12-2025",
|
|
input="Research the current state of quantum computing",
|
|
api_key=api_key,
|
|
)
|
|
|
|
assert response is not None
|
|
print(f"Agent response: {response}")
|
|
|
|
|
|
class TestGoogleInteractionsGetDelete:
|
|
"""Tests for get and delete operations."""
|
|
|
|
@pytest.mark.skip(
|
|
reason="Get/Delete require valid interaction IDs from previous calls"
|
|
)
|
|
def test_get_interaction(self, api_key):
|
|
"""Test getting an interaction by ID."""
|
|
# First create an interaction
|
|
create_response = interactions.create(
|
|
model="gemini/gemini-2.5-flash",
|
|
input="Hello",
|
|
api_key=api_key,
|
|
)
|
|
|
|
if create_response.id:
|
|
# Then get it
|
|
get_response = interactions.get(
|
|
interaction_id=create_response.id,
|
|
api_key=api_key,
|
|
)
|
|
assert get_response is not None
|
|
print(f"Get response: {get_response}")
|
|
|
|
@pytest.mark.skip(
|
|
reason="Get/Delete require valid interaction IDs from previous calls"
|
|
)
|
|
def test_delete_interaction(self, api_key):
|
|
"""Test deleting an interaction by ID."""
|
|
# First create an interaction
|
|
create_response = interactions.create(
|
|
model="gemini/gemini-2.5-flash",
|
|
input="Hello",
|
|
api_key=api_key,
|
|
)
|
|
|
|
if create_response.id:
|
|
# Then delete it
|
|
delete_result = interactions.delete(
|
|
interaction_id=create_response.id,
|
|
api_key=api_key,
|
|
)
|
|
assert delete_result.success is True
|
|
print(f"Delete result: {delete_result}")
|
|
|
|
|
|
class TestGoogleInteractionsErrorHandling:
|
|
"""Tests for error handling."""
|
|
|
|
def test_invalid_model(self, api_key):
|
|
"""Test error handling for invalid model."""
|
|
with pytest.raises(openai.APIError):
|
|
interactions.create(
|
|
model="gemini/invalid-model-name-xyz",
|
|
input="Hello",
|
|
api_key=api_key,
|
|
)
|
|
|
|
def test_missing_model_and_agent(self, api_key):
|
|
"""Test error when neither model nor agent is provided."""
|
|
with pytest.raises((ValueError, litellm.APIConnectionError)):
|
|
interactions.create(
|
|
input="Hello",
|
|
api_key=api_key,
|
|
)
|
|
|
|
|
|
class TestGoogleInteractionsResponseStructure:
|
|
"""Tests to verify the response structure matches OpenAPI spec."""
|
|
|
|
def test_response_has_expected_fields(self, api_key):
|
|
"""Test that the response has fields per OpenAPI spec."""
|
|
response = interactions.create(
|
|
model="gemini/gemini-2.5-flash",
|
|
input="Hello",
|
|
api_key=api_key,
|
|
)
|
|
|
|
# Check fields per OpenAPI spec
|
|
assert hasattr(response, "id")
|
|
assert hasattr(response, "object")
|
|
assert hasattr(response, "status")
|
|
assert hasattr(response, "outputs")
|
|
assert hasattr(response, "usage")
|
|
assert hasattr(response, "model") or hasattr(response, "agent")
|
|
assert hasattr(response, "created")
|
|
assert hasattr(response, "updated")
|
|
|
|
print(
|
|
f"Response structure: id={response.id}, status={response.status}, object={response.object}"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Run a quick smoke test
|
|
print("Running Google Interactions API smoke test...")
|
|
|
|
api_key = GEMINI_API_KEY
|
|
if not api_key:
|
|
print("GEMINI_API_KEY not set, skipping smoke test")
|
|
exit(1)
|
|
|
|
print("\n1. Testing basic interaction...")
|
|
response = interactions.create(
|
|
model="gemini/gemini-2.5-flash",
|
|
input="What is 2 + 2?",
|
|
api_key=api_key,
|
|
)
|
|
print(f"Response: {response}")
|
|
|
|
print("\n2. Testing streaming interaction...")
|
|
stream = interactions.create(
|
|
model="gemini/gemini-2.5-flash",
|
|
input="Count to 3.",
|
|
stream=True,
|
|
api_key=api_key,
|
|
)
|
|
print("Streaming response chunks:")
|
|
for chunk in stream:
|
|
print(f" {chunk}")
|
|
|
|
print("\n3. Testing async interaction...")
|
|
|
|
async def test_async():
|
|
response = await interactions.acreate(
|
|
model="gemini/gemini-2.5-flash",
|
|
input="Say hello!",
|
|
api_key=api_key,
|
|
)
|
|
return response
|
|
|
|
async_response = asyncio.run(test_async())
|
|
print(f"Async response: {async_response}")
|
|
|
|
print("\nSmoke test complete!")
|