mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-09 16:18:30 +00:00
feat: async llm support
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Check Documentation Broken Links / Check broken links (push) Has been cancelled
Notify Downstream / notify-downstream (push) Has been cancelled
Build uv cache / build-cache (3.10) (push) Has been cancelled
Build uv cache / build-cache (3.11) (push) Has been cancelled
Build uv cache / build-cache (3.12) (push) Has been cancelled
Build uv cache / build-cache (3.13) (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Check Documentation Broken Links / Check broken links (push) Has been cancelled
Notify Downstream / notify-downstream (push) Has been cancelled
Build uv cache / build-cache (3.10) (push) Has been cancelled
Build uv cache / build-cache (3.11) (push) Has been cancelled
Build uv cache / build-cache (3.12) (push) Has been cancelled
Build uv cache / build-cache (3.13) (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
feat: introduce async contract to BaseLLM feat: add async call support for: Azure provider Anthropic provider OpenAI provider Gemini provider Bedrock provider LiteLLM provider chore: expand scrubbed header fields (conftest, anthropic, bedrock) chore: update docs to cover async functionality chore: update and harden tests to support acall; re-add uri for cassette compatibility chore: generate missing cassette fix: ensure acall is non-abstract and set supports_tools = true for supported Anthropic models chore: improve Bedrock async docstring and general test robustness
This commit is contained in:
199
lib/crewai/tests/llms/anthropic/test_anthropic_async.py
Normal file
199
lib/crewai/tests/llms/anthropic/test_anthropic_async.py
Normal file
@@ -0,0 +1,199 @@
|
||||
"""Tests for Anthropic async completion functionality."""
|
||||
import json
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
import tiktoken
|
||||
from pydantic import BaseModel
|
||||
|
||||
from crewai.llm import LLM
|
||||
from crewai.llms.providers.anthropic.completion import AnthropicCompletion
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_anthropic_async_basic_call():
|
||||
"""Test basic async call with Anthropic."""
|
||||
llm = LLM(model="anthropic/claude-sonnet-4-0")
|
||||
|
||||
result = await llm.acall("Say hello")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
assert len(result) > 0
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_anthropic_async_with_temperature():
|
||||
"""Test async call with temperature parameter."""
|
||||
llm = LLM(model="anthropic/claude-sonnet-4-0", temperature=0.1)
|
||||
|
||||
result = await llm.acall("Say the word 'test' once")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_anthropic_async_with_max_tokens():
|
||||
"""Test async call with max_tokens parameter."""
|
||||
llm = LLM(model="anthropic/claude-sonnet-4-0", max_tokens=10)
|
||||
|
||||
result = await llm.acall("Write a very long story about a dragon.")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
encoder = tiktoken.get_encoding("cl100k_base")
|
||||
token_count = len(encoder.encode(result))
|
||||
assert token_count <= 10
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_anthropic_async_with_system_message():
|
||||
"""Test async call with system message."""
|
||||
llm = LLM(model="anthropic/claude-sonnet-4-0")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "What is 2+2?"}
|
||||
]
|
||||
|
||||
result = await llm.acall(messages)
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_anthropic_async_conversation():
|
||||
"""Test async call with conversation history."""
|
||||
llm = LLM(model="anthropic/claude-sonnet-4-0")
|
||||
|
||||
messages = [
|
||||
{"role": "user", "content": "My name is Alice."},
|
||||
{"role": "assistant", "content": "Hello Alice! Nice to meet you."},
|
||||
{"role": "user", "content": "What is my name?"}
|
||||
]
|
||||
|
||||
result = await llm.acall(messages)
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_anthropic_async_stop_sequences():
|
||||
"""Test async call with stop sequences."""
|
||||
llm = LLM(
|
||||
model="anthropic/claude-sonnet-4-0",
|
||||
stop_sequences=["END", "STOP"]
|
||||
)
|
||||
|
||||
result = await llm.acall("Count from 1 to 10")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_anthropic_async_multiple_calls():
|
||||
"""Test making multiple async calls in sequence."""
|
||||
llm = LLM(model="anthropic/claude-sonnet-4-0")
|
||||
|
||||
result1 = await llm.acall("What is 1+1?")
|
||||
result2 = await llm.acall("What is 2+2?")
|
||||
|
||||
assert result1 is not None
|
||||
assert result2 is not None
|
||||
assert isinstance(result1, str)
|
||||
assert isinstance(result2, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_anthropic_async_with_response_format_none():
|
||||
"""Test async call with response_format set to None."""
|
||||
llm = LLM(model="anthropic/claude-sonnet-4-0", response_format=None)
|
||||
|
||||
result = await llm.acall("Tell me a short fact")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_anthropic_async_with_response_format_json():
|
||||
"""Test async call with JSON response format."""
|
||||
llm = LLM(model="anthropic/claude-sonnet-4-0", response_format={"type": "json_object"})
|
||||
|
||||
result = await llm.acall("Return a JSON object devoid of ```json{x}```, where x is the json object, with a 'greeting' field")
|
||||
assert isinstance(result, str)
|
||||
deserialized_result = json.loads(result)
|
||||
assert isinstance(deserialized_result, dict)
|
||||
assert isinstance(deserialized_result["greeting"], str)
|
||||
|
||||
|
||||
class GreetingResponse(BaseModel):
|
||||
"""Response model for greeting test."""
|
||||
|
||||
greeting: str
|
||||
language: str
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_anthropic_async_with_response_model():
|
||||
"""Test async call with Pydantic response_model for structured output."""
|
||||
llm = LLM(model="anthropic/claude-sonnet-4-0")
|
||||
|
||||
result = await llm.acall(
|
||||
"Say hello in French",
|
||||
response_model=GreetingResponse
|
||||
)
|
||||
model = GreetingResponse.model_validate_json(result)
|
||||
assert isinstance(model, GreetingResponse)
|
||||
assert isinstance(model.greeting, str)
|
||||
assert isinstance(model.language, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_anthropic_async_with_tools():
|
||||
"""Test async call with tools."""
|
||||
llm = AnthropicCompletion(model="claude-sonnet-4-0")
|
||||
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get the current weather for a location",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA"
|
||||
}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
result = await llm.acall(
|
||||
"What's the weather in San Francisco?",
|
||||
tools=tools
|
||||
)
|
||||
logging.debug("result: %s", result)
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
116
lib/crewai/tests/llms/azure/test_azure_async.py
Normal file
116
lib/crewai/tests/llms/azure/test_azure_async.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""Tests for Azure async completion functionality."""
|
||||
|
||||
import pytest
|
||||
import tiktoken
|
||||
|
||||
from crewai.llm import LLM
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_azure_async_non_streaming():
|
||||
"""Test basic async non-streaming call."""
|
||||
llm = LLM(model="azure/gpt-4o-mini", stream=False)
|
||||
|
||||
result = await llm.acall("Say hello")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
assert len(result) > 0
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_azure_async_multiple_calls():
|
||||
"""Test making multiple async calls in sequence."""
|
||||
llm = LLM(model="azure/gpt-4o-mini", stream=False)
|
||||
|
||||
result1 = await llm.acall("What is 1+1?")
|
||||
result2 = await llm.acall("What is 2+2?")
|
||||
|
||||
assert result1 is not None
|
||||
assert result2 is not None
|
||||
assert isinstance(result1, str)
|
||||
assert isinstance(result2, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_azure_async_with_temperature():
|
||||
"""Test async call with temperature parameter."""
|
||||
llm = LLM(model="azure/gpt-4o-mini", temperature=0.1, stream=False)
|
||||
|
||||
result = await llm.acall("Say the word 'test' once")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_azure_async_with_max_tokens():
|
||||
"""Test async call with max_tokens parameter."""
|
||||
llm = LLM(model="azure/gpt-4o-mini", max_tokens=10, stream=False)
|
||||
|
||||
result = await llm.acall("Write a very long story about a dragon.")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
encoder = tiktoken.get_encoding("cl100k_base")
|
||||
token_count = len(encoder.encode(result))
|
||||
assert token_count <= 10
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_azure_async_with_system_message():
|
||||
"""Test async call with system message."""
|
||||
llm = LLM(model="azure/gpt-4o-mini", stream=False)
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "What is 2+2?"}
|
||||
]
|
||||
|
||||
result = await llm.acall(messages)
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_azure_async_with_parameters():
|
||||
"""Test async call with multiple parameters."""
|
||||
llm = LLM(
|
||||
model="azure/gpt-4o-mini",
|
||||
temperature=0.7,
|
||||
max_tokens=100,
|
||||
top_p=0.9,
|
||||
frequency_penalty=0.5,
|
||||
presence_penalty=0.3,
|
||||
stream=False
|
||||
)
|
||||
|
||||
result = await llm.acall("Tell me a short fact")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_azure_async_conversation():
|
||||
"""Test async call with conversation history."""
|
||||
llm = LLM(model="azure/gpt-4o-mini", stream=False)
|
||||
|
||||
messages = [
|
||||
{"role": "user", "content": "My name is Alice."},
|
||||
{"role": "assistant", "content": "Hello Alice! Nice to meet you."},
|
||||
{"role": "user", "content": "What is my name?"}
|
||||
]
|
||||
|
||||
result = await llm.acall(messages)
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
127
lib/crewai/tests/llms/bedrock/test_bedrock_async.py
Normal file
127
lib/crewai/tests/llms/bedrock/test_bedrock_async.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""Tests for Bedrock async completion functionality.
|
||||
|
||||
Note: These tests are skipped in CI because VCR.py does not support
|
||||
aiobotocore's HTTP session. The cassettes were recorded locally but
|
||||
cannot be played back properly in CI.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import tiktoken
|
||||
|
||||
from crewai.llm import LLM
|
||||
|
||||
SKIP_REASON = "VCR does not support aiobotocore async HTTP client"
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skip(reason=SKIP_REASON)
|
||||
async def test_bedrock_async_basic_call():
|
||||
"""Test basic async call with Bedrock."""
|
||||
llm = LLM(model="bedrock/us.anthropic.claude-3-5-sonnet-20241022-v2:0")
|
||||
|
||||
result = await llm.acall("Say hello")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
assert len(result) > 0
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skip(reason=SKIP_REASON)
|
||||
async def test_bedrock_async_with_temperature():
|
||||
"""Test async call with temperature parameter."""
|
||||
llm = LLM(model="bedrock/us.anthropic.claude-3-5-sonnet-20241022-v2:0", temperature=0.1)
|
||||
|
||||
result = await llm.acall("Say the word 'test' once")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skip(reason=SKIP_REASON)
|
||||
async def test_bedrock_async_with_max_tokens():
|
||||
"""Test async call with max_tokens parameter."""
|
||||
llm = LLM(model="bedrock/us.anthropic.claude-3-5-sonnet-20241022-v2:0", max_tokens=10)
|
||||
|
||||
result = await llm.acall("Write a very long story about a dragon.")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
encoder = tiktoken.get_encoding("cl100k_base")
|
||||
token_count = len(encoder.encode(result))
|
||||
assert token_count <= 10
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skip(reason=SKIP_REASON)
|
||||
async def test_bedrock_async_with_system_message():
|
||||
"""Test async call with system message."""
|
||||
llm = LLM(model="bedrock/us.anthropic.claude-3-5-sonnet-20241022-v2:0")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "What is 2+2?"}
|
||||
]
|
||||
|
||||
result = await llm.acall(messages)
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skip(reason=SKIP_REASON)
|
||||
async def test_bedrock_async_conversation():
|
||||
"""Test async call with conversation history."""
|
||||
llm = LLM(model="bedrock/us.anthropic.claude-3-5-sonnet-20241022-v2:0")
|
||||
|
||||
messages = [
|
||||
{"role": "user", "content": "My name is Alice."},
|
||||
{"role": "assistant", "content": "Hello Alice! Nice to meet you."},
|
||||
{"role": "user", "content": "What is my name?"}
|
||||
]
|
||||
|
||||
result = await llm.acall(messages)
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skip(reason=SKIP_REASON)
|
||||
async def test_bedrock_async_multiple_calls():
|
||||
"""Test making multiple async calls in sequence."""
|
||||
llm = LLM(model="bedrock/us.anthropic.claude-3-5-sonnet-20241022-v2:0")
|
||||
|
||||
result1 = await llm.acall("What is 1+1?")
|
||||
result2 = await llm.acall("What is 2+2?")
|
||||
|
||||
assert result1 is not None
|
||||
assert result2 is not None
|
||||
assert isinstance(result1, str)
|
||||
assert isinstance(result2, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skip(reason=SKIP_REASON)
|
||||
async def test_bedrock_async_with_parameters():
|
||||
"""Test async call with multiple parameters."""
|
||||
llm = LLM(
|
||||
model="bedrock/us.anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
temperature=0.7,
|
||||
max_tokens=100,
|
||||
top_p=0.9
|
||||
)
|
||||
|
||||
result = await llm.acall("Tell me a short fact")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
114
lib/crewai/tests/llms/google/test_google_async.py
Normal file
114
lib/crewai/tests/llms/google/test_google_async.py
Normal file
@@ -0,0 +1,114 @@
|
||||
"""Tests for Google (Gemini) async completion functionality."""
|
||||
|
||||
import pytest
|
||||
import tiktoken
|
||||
|
||||
from crewai.llm import LLM
|
||||
from crewai.llms.providers.gemini.completion import GeminiCompletion
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_gemini_async_basic_call():
|
||||
"""Test basic async call with Gemini."""
|
||||
llm = LLM(model="gemini/gemini-3-pro-preview")
|
||||
|
||||
result = await llm.acall("Say hello")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
assert len(result) > 0
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_gemini_async_with_temperature():
|
||||
"""Test async call with temperature parameter."""
|
||||
llm = LLM(model="gemini/gemini-3-pro-preview", temperature=0.1)
|
||||
|
||||
result = await llm.acall("Say the word 'test' once")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_gemini_async_with_max_tokens():
|
||||
"""Test async call with max_tokens parameter."""
|
||||
llm = GeminiCompletion(model="gemini-3-pro-preview", max_output_tokens=1000)
|
||||
|
||||
result = await llm.acall("Write a very short story about a dragon.")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
encoder = tiktoken.get_encoding("cl100k_base")
|
||||
token_count = len(encoder.encode(result))
|
||||
assert token_count <= 1000
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_gemini_async_with_system_message():
|
||||
"""Test async call with system message."""
|
||||
llm = LLM(model="gemini/gemini-3-pro-preview")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "What is 2+2?"}
|
||||
]
|
||||
|
||||
result = await llm.acall(messages)
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_gemini_async_conversation():
|
||||
"""Test async call with conversation history."""
|
||||
llm = LLM(model="gemini/gemini-3-pro-preview")
|
||||
|
||||
messages = [
|
||||
{"role": "user", "content": "My name is Alice."},
|
||||
{"role": "assistant", "content": "Hello Alice! Nice to meet you."},
|
||||
{"role": "user", "content": "What is my name?"}
|
||||
]
|
||||
|
||||
result = await llm.acall(messages)
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_gemini_async_multiple_calls():
|
||||
"""Test making multiple async calls in sequence."""
|
||||
llm = LLM(model="gemini/gemini-3-pro-preview")
|
||||
|
||||
result1 = await llm.acall("What is 1+1?")
|
||||
result2 = await llm.acall("What is 2+2?")
|
||||
|
||||
assert result1 is not None
|
||||
assert result2 is not None
|
||||
assert isinstance(result1, str)
|
||||
assert isinstance(result2, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_gemini_async_with_parameters():
|
||||
"""Test async call with multiple parameters."""
|
||||
llm = LLM(
|
||||
model="gemini/gemini-3-pro-preview",
|
||||
temperature=0.7,
|
||||
max_output_tokens=1000,
|
||||
top_p=0.9
|
||||
)
|
||||
|
||||
result = await llm.acall("Tell me a short fact")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
1
lib/crewai/tests/llms/litellm/__init__.py
Normal file
1
lib/crewai/tests/llms/litellm/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""LiteLLM fallback tests."""
|
||||
156
lib/crewai/tests/llms/litellm/test_litellm_async.py
Normal file
156
lib/crewai/tests/llms/litellm/test_litellm_async.py
Normal file
@@ -0,0 +1,156 @@
|
||||
"""Tests for LiteLLM fallback async completion functionality."""
|
||||
|
||||
import pytest
|
||||
import tiktoken
|
||||
|
||||
from crewai.llm import LLM
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
@pytest.mark.skip(reason="cassettes do not read properly but were generated correctly.")
|
||||
async def test_litellm_async_basic_call():
|
||||
"""Test basic async call with LiteLLM fallback."""
|
||||
llm = LLM(model="gpt-4o-mini", is_litellm=True)
|
||||
|
||||
result = await llm.acall("Say hello")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
assert len(result) > 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
@pytest.mark.skip(reason="cassettes do not read properly but were generated correctly.")
|
||||
async def test_litellm_async_with_temperature():
|
||||
"""Test async call with temperature parameter."""
|
||||
llm = LLM(model="gpt-4o-mini", is_litellm=True, temperature=0.1)
|
||||
|
||||
result = await llm.acall("Say the word 'test' once")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
@pytest.mark.skip(reason="cassettes do not read properly but were generated correctly.")
|
||||
async def test_litellm_async_with_max_tokens():
|
||||
"""Test async call with max_tokens parameter."""
|
||||
llm = LLM(model="gpt-4o-mini", is_litellm=True, max_tokens=10)
|
||||
|
||||
result = await llm.acall("Write a very long story about a dragon.")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
encoder = tiktoken.get_encoding("cl100k_base")
|
||||
token_count = len(encoder.encode(result))
|
||||
assert token_count <= 10
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
@pytest.mark.skip(reason="cassettes do not read properly but were generated correctly.")
|
||||
async def test_litellm_async_with_system_message():
|
||||
"""Test async call with system message."""
|
||||
llm = LLM(model="gpt-4o-mini", is_litellm=True)
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "What is 2+2?"},
|
||||
]
|
||||
|
||||
result = await llm.acall(messages)
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
@pytest.mark.skip(reason="cassettes do not read properly but were generated correctly.")
|
||||
async def test_litellm_async_conversation():
|
||||
"""Test async call with conversation history."""
|
||||
llm = LLM(model="gpt-4o-mini", is_litellm=True)
|
||||
|
||||
messages = [
|
||||
{"role": "user", "content": "My name is Alice."},
|
||||
{"role": "assistant", "content": "Hello Alice! Nice to meet you."},
|
||||
{"role": "user", "content": "What is my name?"},
|
||||
]
|
||||
|
||||
result = await llm.acall(messages)
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
@pytest.mark.skip(reason="cassettes do not read properly but were generated correctly.")
|
||||
async def test_litellm_async_multiple_calls():
|
||||
"""Test making multiple async calls in sequence."""
|
||||
llm = LLM(model="gpt-4o-mini", is_litellm=True)
|
||||
|
||||
result1 = await llm.acall("What is 1+1?")
|
||||
result2 = await llm.acall("What is 2+2?")
|
||||
|
||||
assert result1 is not None
|
||||
assert result2 is not None
|
||||
assert isinstance(result1, str)
|
||||
assert isinstance(result2, str)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
@pytest.mark.skip(reason="cassettes do not read properly but were generated correctly.")
|
||||
async def test_litellm_async_with_parameters():
|
||||
"""Test async call with multiple parameters."""
|
||||
llm = LLM(
|
||||
model="gpt-4o-mini",
|
||||
is_litellm=True,
|
||||
temperature=0.7,
|
||||
max_tokens=100,
|
||||
top_p=0.9,
|
||||
frequency_penalty=0.5,
|
||||
presence_penalty=0.3,
|
||||
)
|
||||
|
||||
result = await llm.acall("Tell me a short fact")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
@pytest.mark.skip(reason="cassettes do not read properly but were generated correctly.")
|
||||
async def test_litellm_async_streaming():
|
||||
"""Test async streaming call with LiteLLM fallback."""
|
||||
llm = LLM(model="gpt-4o-mini", is_litellm=True, stream=True)
|
||||
|
||||
result = await llm.acall("Say hello world")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
assert len(result) > 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
@pytest.mark.skip(reason="cassettes do not read properly but were generated correctly.")
|
||||
async def test_litellm_async_streaming_with_parameters():
|
||||
"""Test async streaming call with multiple parameters."""
|
||||
llm = LLM(
|
||||
model="gpt-4o-mini",
|
||||
is_litellm=True,
|
||||
stream=True,
|
||||
temperature=0.5,
|
||||
max_tokens=50,
|
||||
)
|
||||
|
||||
result = await llm.acall("Count from 1 to 5")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
@@ -475,10 +475,14 @@ def test_openai_get_client_params_priority_order():
|
||||
params3 = llm3._get_client_params()
|
||||
assert params3["base_url"] == "https://env.openai.com/v1"
|
||||
|
||||
def test_openai_get_client_params_no_base_url():
|
||||
def test_openai_get_client_params_no_base_url(monkeypatch):
|
||||
"""
|
||||
Test that _get_client_params works correctly when no base_url is specified
|
||||
"""
|
||||
# Clear env vars that could set base_url
|
||||
monkeypatch.delenv("OPENAI_BASE_URL", raising=False)
|
||||
monkeypatch.delenv("OPENAI_API_BASE", raising=False)
|
||||
|
||||
llm = OpenAICompletion(model="gpt-4o")
|
||||
client_params = llm._get_client_params()
|
||||
# When no base_url is provided, it should not be in the params (filtered out as None)
|
||||
|
||||
139
lib/crewai/tests/llms/openai/test_openai_async.py
Normal file
139
lib/crewai/tests/llms/openai/test_openai_async.py
Normal file
@@ -0,0 +1,139 @@
|
||||
"""Tests for OpenAI async completion functionality."""
|
||||
|
||||
import pytest
|
||||
import tiktoken
|
||||
|
||||
from crewai.llm import LLM
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_async_basic_call():
|
||||
"""Test basic async call with OpenAI."""
|
||||
llm = LLM(model="gpt-4o-mini")
|
||||
|
||||
result = await llm.acall("Say hello")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
assert len(result) > 0
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_async_with_temperature():
|
||||
"""Test async call with temperature parameter."""
|
||||
llm = LLM(model="gpt-4o-mini", temperature=0.1)
|
||||
|
||||
result = await llm.acall("Say the word 'test' once")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_async_with_max_tokens():
|
||||
"""Test async call with max_tokens parameter."""
|
||||
llm = LLM(model="gpt-4o-mini", max_tokens=10)
|
||||
|
||||
result = await llm.acall("Write a very long story about a dragon.")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
encoder = tiktoken.get_encoding("cl100k_base")
|
||||
token_count = len(encoder.encode(result))
|
||||
assert token_count <= 10
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_async_with_system_message():
|
||||
"""Test async call with system message."""
|
||||
llm = LLM(model="gpt-4o-mini")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "What is 2+2?"}
|
||||
]
|
||||
|
||||
result = await llm.acall(messages)
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_async_conversation():
|
||||
"""Test async call with conversation history."""
|
||||
llm = LLM(model="gpt-4o-mini")
|
||||
|
||||
messages = [
|
||||
{"role": "user", "content": "My name is Alice."},
|
||||
{"role": "assistant", "content": "Hello Alice! Nice to meet you."},
|
||||
{"role": "user", "content": "What is my name?"}
|
||||
]
|
||||
|
||||
result = await llm.acall(messages)
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_async_multiple_calls():
|
||||
"""Test making multiple async calls in sequence."""
|
||||
llm = LLM(model="gpt-4o-mini")
|
||||
|
||||
result1 = await llm.acall("What is 1+1?")
|
||||
result2 = await llm.acall("What is 2+2?")
|
||||
|
||||
assert result1 is not None
|
||||
assert result2 is not None
|
||||
assert isinstance(result1, str)
|
||||
assert isinstance(result2, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_async_with_response_format_none():
|
||||
"""Test async call with response_format set to None."""
|
||||
llm = LLM(model="gpt-4o-mini", response_format=None)
|
||||
|
||||
result = await llm.acall("Tell me a short fact")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_async_with_response_format_json():
|
||||
"""Test async call with JSON response format."""
|
||||
llm = LLM(model="gpt-4o-mini", response_format={"type": "json_object"})
|
||||
|
||||
result = await llm.acall("Return a JSON object with a 'greeting' field")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_async_with_parameters():
|
||||
"""Test async call with multiple parameters."""
|
||||
llm = LLM(
|
||||
model="gpt-4o-mini",
|
||||
temperature=0.7,
|
||||
max_tokens=100,
|
||||
top_p=0.9,
|
||||
frequency_penalty=0.5,
|
||||
presence_penalty=0.3
|
||||
)
|
||||
|
||||
result = await llm.acall("Tell me a short fact")
|
||||
|
||||
assert result is not None
|
||||
assert isinstance(result, str)
|
||||
Reference in New Issue
Block a user