136 lines
4.4 KiB
Python
136 lines
4.4 KiB
Python
"""Tests for the async client path (Completions.acreate)."""
|
|
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock, Mock, patch
|
|
|
|
import pytest
|
|
|
|
from aisuite import Client
|
|
from aisuite.framework.message import ChatCompletionMessageToolCall, Function, Message
|
|
|
|
|
|
def _chat_response(content=None, tool_calls=None):
|
|
message = Message(role="assistant", content=content, tool_calls=tool_calls)
|
|
return SimpleNamespace(choices=[SimpleNamespace(message=message)])
|
|
|
|
|
|
def _tool_call(name, arguments, call_id="call_1"):
|
|
return ChatCompletionMessageToolCall(
|
|
id=call_id, type="function", function=Function(name=name, arguments=arguments)
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@patch("aisuite.provider.ProviderFactory.create_provider")
|
|
async def test_acreate_runs_full_tool_loop(mock_create_provider):
|
|
provider = Mock()
|
|
provider.achat_completions_create = AsyncMock(
|
|
side_effect=[
|
|
_chat_response(
|
|
tool_calls=[_tool_call("get_weather", '{"location": "San Francisco"}')]
|
|
),
|
|
_chat_response(content="It is sunny in San Francisco."),
|
|
]
|
|
)
|
|
mock_create_provider.return_value = provider
|
|
|
|
def get_weather(location: str):
|
|
"""Get the weather for a location."""
|
|
return {"location": location, "condition": "sunny"}
|
|
|
|
client = Client()
|
|
response = await client.chat.completions.acreate(
|
|
model="openai:gpt-4o",
|
|
messages=[{"role": "user", "content": "What is the weather?"}],
|
|
tools=[get_weather],
|
|
max_turns=2,
|
|
)
|
|
|
|
assert response.choices[0].message.content == "It is sunny in San Francisco."
|
|
assert provider.achat_completions_create.await_count == 2
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@patch("aisuite.provider.ProviderFactory.create_provider")
|
|
async def test_acreate_without_tools(mock_create_provider):
|
|
provider = Mock()
|
|
provider.achat_completions_create = AsyncMock(
|
|
return_value=_chat_response(content="hello")
|
|
)
|
|
mock_create_provider.return_value = provider
|
|
|
|
client = Client()
|
|
response = await client.chat.completions.acreate(
|
|
model="openai:gpt-4o",
|
|
messages=[{"role": "user", "content": "hi"}],
|
|
)
|
|
|
|
assert response.choices[0].message.content == "hello"
|
|
provider.achat_completions_create.assert_awaited_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@patch("aisuite.provider.ProviderFactory.create_provider")
|
|
async def test_acreate_manual_tool_calling_passes_tools(mock_create_provider):
|
|
"""Manual mode (no max_turns): the provider must receive the tool schemas.
|
|
|
|
Async twin of the #266 regression: tools are popped from kwargs, so the
|
|
manual path must re-add provider-ready specs before delegating.
|
|
"""
|
|
provider = Mock()
|
|
provider.achat_completions_create = AsyncMock(
|
|
return_value=_chat_response(content="ok")
|
|
)
|
|
mock_create_provider.return_value = provider
|
|
|
|
def get_weather(location: str):
|
|
"""Get the weather for a location."""
|
|
return {"location": location}
|
|
|
|
schema = {
|
|
"type": "function",
|
|
"function": {
|
|
"name": "manual_tool",
|
|
"parameters": {"type": "object", "properties": {}},
|
|
},
|
|
}
|
|
|
|
client = Client()
|
|
await client.chat.completions.acreate(
|
|
model="openai:gpt-4o",
|
|
messages=[{"role": "user", "content": "hi"}],
|
|
tools=[schema, get_weather], # no max_turns → manual mode
|
|
)
|
|
|
|
sent = provider.achat_completions_create.await_args.kwargs["tools"]
|
|
assert sent[0] == schema # dicts pass through untouched
|
|
assert sent[1]["function"]["name"] == "get_weather" # callables become specs
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@patch("aisuite.provider.ProviderFactory.create_provider")
|
|
async def test_acreate_awaits_async_tool(mock_create_provider):
|
|
"""An async def tool is awaited inside the async tool loop."""
|
|
provider = Mock()
|
|
provider.achat_completions_create = AsyncMock(
|
|
side_effect=[
|
|
_chat_response(tool_calls=[_tool_call("lookup", '{"q": "x"}')]),
|
|
_chat_response(content="done"),
|
|
]
|
|
)
|
|
mock_create_provider.return_value = provider
|
|
|
|
async def lookup(q: str):
|
|
"""An async tool."""
|
|
return {"q": q, "value": 42}
|
|
|
|
client = Client()
|
|
response = await client.chat.completions.acreate(
|
|
model="openai:gpt-4o",
|
|
messages=[{"role": "user", "content": "look it up"}],
|
|
tools=[lookup],
|
|
max_turns=3,
|
|
)
|
|
|
|
assert response.choices[0].message.content == "done"
|