288 lines
10 KiB
Python
288 lines
10 KiB
Python
from unittest.mock import AsyncMock, Mock, patch
|
|
|
|
import pytest
|
|
|
|
from sdks.python.src.honcho.async_client.client import AsyncHoncho
|
|
from sdks.python.src.honcho.async_client.peer import AsyncPeer
|
|
from sdks.python.src.honcho.client import Honcho
|
|
from sdks.python.src.honcho.peer import Peer
|
|
from sdks.python.src.honcho.types import DialecticStreamResponse
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_peer_metadata(client_fixture: tuple[Honcho | AsyncHoncho, str]):
|
|
"""
|
|
Tests creation and metadata operations for peers.
|
|
"""
|
|
honcho_client, client_type = client_fixture
|
|
|
|
if client_type == "async":
|
|
assert isinstance(honcho_client, AsyncHoncho)
|
|
peer = await honcho_client.peer(id="test-peer-meta")
|
|
assert isinstance(peer, AsyncPeer)
|
|
|
|
metadata = await peer.get_metadata()
|
|
assert metadata == {}
|
|
|
|
await peer.set_metadata({"foo": "bar"})
|
|
metadata = await peer.get_metadata()
|
|
assert metadata == {"foo": "bar"}
|
|
else:
|
|
assert isinstance(honcho_client, Honcho)
|
|
peer = honcho_client.peer(id="test-peer-meta")
|
|
assert isinstance(peer, Peer)
|
|
|
|
metadata = peer.get_metadata()
|
|
assert metadata == {}
|
|
|
|
peer.set_metadata({"foo": "bar"})
|
|
metadata = peer.get_metadata()
|
|
assert metadata == {"foo": "bar"}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_peer_get_sessions(client_fixture: tuple[Honcho | AsyncHoncho, str]):
|
|
"""
|
|
Tests retrieving the sessions a peer is a member of.
|
|
"""
|
|
honcho_client, client_type = client_fixture
|
|
|
|
if client_type == "async":
|
|
assert isinstance(honcho_client, AsyncHoncho)
|
|
peer = await honcho_client.peer(id="test-peer-sessions")
|
|
session1 = await honcho_client.session(id="s1")
|
|
session2 = await honcho_client.session(id="s2")
|
|
|
|
await session1.add_peers(peer)
|
|
await session2.add_peers(peer)
|
|
|
|
sessions_page = await peer.get_sessions()
|
|
sessions = sessions_page.items
|
|
assert len(sessions) == 2
|
|
session_ids = {s.id for s in sessions}
|
|
assert "s1" in session_ids
|
|
assert "s2" in session_ids
|
|
else:
|
|
assert isinstance(honcho_client, Honcho)
|
|
peer = honcho_client.peer(id="test-peer-sessions")
|
|
session1 = honcho_client.session(id="s1")
|
|
session2 = honcho_client.session(id="s2")
|
|
|
|
session1.add_peers(peer)
|
|
session2.add_peers(peer)
|
|
|
|
sessions_page = peer.get_sessions()
|
|
sessions = list(sessions_page)
|
|
assert len(sessions) == 2
|
|
session_ids = {s.id for s in sessions}
|
|
assert "s1" in session_ids
|
|
assert "s2" in session_ids
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_peer_card_global(client_fixture: tuple[Honcho | AsyncHoncho, str]):
|
|
"""
|
|
Tests getting a global peer card (no target).
|
|
"""
|
|
honcho_client, client_type = client_fixture
|
|
|
|
if client_type == "async":
|
|
assert isinstance(honcho_client, AsyncHoncho)
|
|
peer = await honcho_client.peer(id="test-card-global-peer")
|
|
session = await honcho_client.session(id="test-card-global-session")
|
|
|
|
# Add some messages to create context
|
|
await session.add_messages([peer.message("I like pizza")])
|
|
|
|
# Get global peer card
|
|
card_response = await peer.card()
|
|
assert isinstance(card_response, str)
|
|
else:
|
|
assert isinstance(honcho_client, Honcho)
|
|
peer = honcho_client.peer(id="test-card-global-peer")
|
|
session = honcho_client.session(id="test-card-global-session")
|
|
|
|
# Add some messages to create context
|
|
session.add_messages([peer.message("I like pizza")])
|
|
|
|
# Get global peer card
|
|
card_response = peer.card()
|
|
assert isinstance(card_response, str)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_peer_card_local(client_fixture: tuple[Honcho | AsyncHoncho, str]):
|
|
"""
|
|
Tests getting a local peer card (with target).
|
|
"""
|
|
honcho_client, client_type = client_fixture
|
|
|
|
if client_type == "async":
|
|
assert isinstance(honcho_client, AsyncHoncho)
|
|
observer = await honcho_client.peer(id="test-card-local-observer")
|
|
target = await honcho_client.peer(id="test-card-local-target")
|
|
session = await honcho_client.session(id="test-card-local-session")
|
|
|
|
# Add messages from both peers
|
|
await session.add_messages(
|
|
[observer.message("Hello"), target.message("Hi there")]
|
|
)
|
|
|
|
# Get local peer card with target as Peer object
|
|
card_response = await observer.card(target=target)
|
|
assert isinstance(card_response, str)
|
|
|
|
# Get local peer card with target as string
|
|
card_response = await observer.card(target=target.id)
|
|
assert isinstance(card_response, str)
|
|
else:
|
|
assert isinstance(honcho_client, Honcho)
|
|
observer = honcho_client.peer(id="test-card-local-observer")
|
|
target = honcho_client.peer(id="test-card-local-target")
|
|
session = honcho_client.session(id="test-card-local-session")
|
|
|
|
# Add messages from both peers
|
|
session.add_messages([observer.message("Hello"), target.message("Hi there")])
|
|
|
|
# Get local peer card with target as Peer object
|
|
card_response = observer.card(target=target)
|
|
assert isinstance(card_response, str)
|
|
|
|
# Get local peer card with target as string
|
|
card_response = observer.card(target=target.id)
|
|
assert isinstance(card_response, str)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_peer_card_validation(client_fixture: tuple[Honcho | AsyncHoncho, str]):
|
|
"""
|
|
Tests peer card validation.
|
|
"""
|
|
honcho_client, client_type = client_fixture
|
|
|
|
if client_type == "async":
|
|
assert isinstance(honcho_client, AsyncHoncho)
|
|
peer = await honcho_client.peer(id="test-card-validation-peer")
|
|
|
|
# Test with empty string
|
|
with pytest.raises(ValueError, match="target string cannot be empty"):
|
|
await peer.card(target="")
|
|
else:
|
|
assert isinstance(honcho_client, Honcho)
|
|
peer = honcho_client.peer(id="test-card-validation-peer")
|
|
|
|
# Test with empty string
|
|
with pytest.raises(ValueError, match="target string cannot be empty"):
|
|
peer.card(target="")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_peer_chat_streaming(client_fixture: tuple[Honcho | AsyncHoncho, str]):
|
|
"""
|
|
Tests streaming chat with mocked response generator.
|
|
"""
|
|
|
|
honcho_client, client_type = client_fixture
|
|
if client_type == "async":
|
|
assert isinstance(honcho_client, AsyncHoncho)
|
|
peer = await honcho_client.peer(id="test-stream-async-peer")
|
|
session = await honcho_client.session(id="test-stream-async-session")
|
|
|
|
# Add some messages to create context
|
|
await session.add_messages([peer.message("I like pizza")])
|
|
|
|
# Mock the async streaming response
|
|
async def mock_aiter_lines():
|
|
yield 'data: {"delta": {"content": "Hello"}}'
|
|
yield 'data: {"delta": {"content": " async"}}'
|
|
yield 'data: {"done": true}'
|
|
|
|
mock_response = AsyncMock()
|
|
mock_response.iter_lines = mock_aiter_lines
|
|
mock_response.__aenter__ = AsyncMock(return_value=mock_response)
|
|
mock_response.__aexit__ = AsyncMock(return_value=None)
|
|
|
|
# Mock the with_streaming_response.chat call
|
|
with patch.object(
|
|
honcho_client.core.workspaces.peers.with_streaming_response,
|
|
"chat",
|
|
return_value=mock_response,
|
|
):
|
|
result = await peer.chat("Tell me something", stream=True)
|
|
assert isinstance(result, DialecticStreamResponse)
|
|
|
|
# Collect chunks
|
|
chunks: list[str] = []
|
|
async for chunk in result:
|
|
assert isinstance(chunk, str)
|
|
chunks.append(chunk)
|
|
assert chunks == ["Hello", " async"]
|
|
assert result.get_final_response()["content"] == "Hello async"
|
|
else:
|
|
assert isinstance(honcho_client, Honcho)
|
|
peer = honcho_client.peer(id="test-stream-peer")
|
|
session = honcho_client.session(id="test-stream-session")
|
|
|
|
# Add some messages to create context
|
|
session.add_messages([peer.message("I like pizza")])
|
|
|
|
# Mock the streaming response
|
|
def mock_iter_lines():
|
|
yield 'data: {"delta": {"content": "Hello"}}'
|
|
yield 'data: {"delta": {"content": " world"}}'
|
|
yield 'data: {"done": true}'
|
|
|
|
mock_response = Mock()
|
|
mock_response.iter_lines = mock_iter_lines
|
|
mock_response.__enter__ = Mock(return_value=mock_response)
|
|
mock_response.__exit__ = Mock(return_value=None)
|
|
|
|
# Mock the with_streaming_response.chat call
|
|
with patch.object(
|
|
honcho_client.core.workspaces.peers.with_streaming_response,
|
|
"chat",
|
|
return_value=mock_response,
|
|
):
|
|
result = peer.chat("Tell me something", stream=True)
|
|
assert isinstance(result, DialecticStreamResponse)
|
|
|
|
# Collect chunks
|
|
chunks = list(result)
|
|
assert chunks == ["Hello", " world"]
|
|
assert result.get_final_response()["content"] == "Hello world"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_peer_chat_non_streaming(
|
|
client_fixture: tuple[Honcho | AsyncHoncho, str],
|
|
):
|
|
"""
|
|
Tests non-streaming chat (already using core SDK).
|
|
"""
|
|
honcho_client, client_type = client_fixture
|
|
|
|
if client_type == "async":
|
|
assert isinstance(honcho_client, AsyncHoncho)
|
|
peer = await honcho_client.peer(id="test-non-stream-peer")
|
|
session = await honcho_client.session(id="test-non-stream-session")
|
|
|
|
# Add some messages
|
|
await session.add_messages([peer.message("I like pizza")])
|
|
|
|
# Non-streaming chat
|
|
response = await peer.chat("What do I like?", stream=False)
|
|
# Response can be None or a string
|
|
assert response is None or isinstance(response, str)
|
|
else:
|
|
assert isinstance(honcho_client, Honcho)
|
|
peer = honcho_client.peer(id="test-non-stream-peer")
|
|
session = honcho_client.session(id="test-non-stream-session")
|
|
|
|
# Add some messages
|
|
session.add_messages([peer.message("I like pizza")])
|
|
|
|
# Non-streaming chat
|
|
response = peer.chat("What do I like?", stream=False)
|
|
# Response can be None or a string
|
|
assert response is None or isinstance(response, str)
|