feat: migrate agent core to pydantic-ai framework

This commit is contained in:
Michael
2026-08-02 17:04:44 +02:00
parent 4f7059788d
commit be0d8dc24b
10 changed files with 109 additions and 117 deletions
+1 -1
View File
@@ -107,7 +107,7 @@ async def test_dispatch_planning_and_coding_phases(mock_planning_class: MagicMoc
mock_client.issues.get_issue_comments.return_value = []
from gitea.models import UserModel
mock_client.get_authenticated_user.return_value = UserModel(login="unknown-ai")
mock_client.repos.get_authenticated_user.return_value = UserModel(login="unknown-ai")
mock_pr = PullRequestModel(
number=42,
+7 -1
View File
@@ -275,7 +275,13 @@ def _make_comment(login: str, body: str) -> CommentModel:
def _make_dispatcher_for_reply_tests() -> AgentDispatcher:
mock_client = MagicMock()
mock_client.repos.get_authenticated_user.return_value = UserModel(login="unknown-ai")
return AgentDispatcher(client=mock_client, tools=MagicMock())
return AgentDispatcher(
client=mock_client,
issue_tools=MagicMock(),
pr_tools=MagicMock(),
file_tools=MagicMock(),
git_tools=MagicMock(),
)
def test_is_awaiting_reply_no_comments() -> None:
+3 -7
View File
@@ -8,11 +8,9 @@ pytestmark = pytest.mark.anyio
@patch("main.load_dotenv")
@patch("main.os.chdir")
@patch("main.GiteaClient")
@patch("main.GiteaTools")
@patch("main.AgentOrchestrator")
async def test_main_startup_success(
mock_orchestrator_class: MagicMock,
mock_tools_class: MagicMock,
mock_client_class: MagicMock,
mock_chdir: MagicMock,
mock_load_dotenv: MagicMock
@@ -21,7 +19,7 @@ async def test_main_startup_success(
mock_client_class.return_value = mock_client
mock_user = MagicMock()
mock_user.login = "agent-test"
mock_client.get_authenticated_user.return_value = mock_user
mock_client.repos.get_authenticated_user.return_value = mock_user
mock_orchestrator = MagicMock()
mock_orchestrator.poll_and_dispatch = AsyncMock(side_effect=KeyboardInterrupt())
@@ -30,18 +28,16 @@ async def test_main_startup_success(
# Run main; it should exit gracefully on KeyboardInterrupt
await main()
mock_client.get_authenticated_user.assert_called_once()
mock_client.repos.get_authenticated_user.assert_called_once()
mock_orchestrator.poll_and_dispatch.assert_called_once()
@patch("main.load_dotenv")
@patch("main.os.chdir")
@patch("main.GiteaClient")
@patch("main.GiteaTools")
@patch("main.AgentOrchestrator")
async def test_main_startup_fails_no_authenticated_user(
mock_orchestrator_class: MagicMock,
mock_tools_class: MagicMock,
mock_client_class: MagicMock,
mock_chdir: MagicMock,
mock_load_dotenv: MagicMock
@@ -49,7 +45,7 @@ async def test_main_startup_fails_no_authenticated_user(
mock_client = MagicMock()
mock_client_class.return_value = mock_client
# Simulate no user returned
mock_client.get_authenticated_user.return_value = None
mock_client.repos.get_authenticated_user.return_value = None
with pytest.raises(SystemExit) as exc_info:
await main()
+19 -21
View File
@@ -4,7 +4,6 @@ import pytest
from unittest.mock import MagicMock, AsyncMock, patch
from core.orchestrator import AgentOrchestrator
from gitea.client import GiteaClient
from gitea.models import IssueModel, PullRequestModel, RepositoryModel
pytestmark = pytest.mark.anyio
@@ -29,16 +28,17 @@ async def test_poll_and_dispatch_no_notifications(
temp_state_file: Path
) -> None:
mock_get_path.return_value = temp_state_file
mock_client = MagicMock(spec=GiteaClient)
mock_tools = MagicMock()
mock_client = MagicMock()
# Return no notifications
mock_client.list_unread_notifications.return_value = []
mock_client.notifications.list_unread_notifications.return_value = []
orchestrator = AgentOrchestrator(mock_client, mock_tools)
orchestrator = AgentOrchestrator(
mock_client, MagicMock(), MagicMock(), MagicMock(), MagicMock()
)
await orchestrator.poll_and_dispatch()
mock_client.list_unread_notifications.assert_called_once_with(since=None)
mock_client.notifications.list_unread_notifications.assert_called_once_with(since=None)
assert not temp_state_file.exists()
@@ -66,8 +66,7 @@ async def test_poll_and_dispatch_with_notifications(
return "Decided"
mock_reader.decide_notification = AsyncMock(side_effect=mock_decide_notification)
mock_notification_reader_class.return_value = mock_reader
mock_client = MagicMock(spec=GiteaClient)
mock_tools = MagicMock()
mock_client = MagicMock()
# Set up mock Gitea notifications
notifications = [
@@ -98,13 +97,14 @@ async def test_poll_and_dispatch_with_notifications(
}
}
]
mock_client.list_unread_notifications.return_value = notifications
mock_client.notifications.list_unread_notifications.return_value = notifications
# Mock issue and PR get methods
# Mock issue and PR get methods on client
issue_model = IssueModel(number=42, title="Bug issue", repository=RepositoryModel(name="repo1", full_name="meeks/repo1"))
pr_model = PullRequestModel(number=10, title="Fix PR", repository=RepositoryModel(name="repo1", full_name="meeks/repo1"))
mock_client.get_issue.return_value = issue_model
mock_client.get_pull_request.return_value = pr_model
mock_client.issues.get_issue.return_value = issue_model
mock_client.prs.get_pull_request.return_value = pr_model
# Mock dispatcher and workspace path
mock_dispatcher_instance = MagicMock()
@@ -116,15 +116,13 @@ async def test_poll_and_dispatch_with_notifications(
mock_workspace_class.return_value = mock_workspace_instance
# Create orchestrator and poll
orchestrator = AgentOrchestrator(mock_client, mock_tools)
orchestrator = AgentOrchestrator(
mock_client, MagicMock(), MagicMock(), MagicMock(), MagicMock()
)
await orchestrator.poll_and_dispatch()
# Assert notifications were checked with None (first execution)
mock_client.list_unread_notifications.assert_called_once_with(since=None)
# Assert issue and PR details were fetched
mock_client.get_issue.assert_called_once_with("meeks", "repo1", 42)
mock_client.get_pull_request.assert_called_once_with("meeks", "repo1", 10)
mock_client.notifications.list_unread_notifications.assert_called_once_with(since=None)
# Assert work was processed by dispatcher
mock_dispatcher_instance.dispatch.assert_called_once()
@@ -136,9 +134,9 @@ async def test_poll_and_dispatch_with_notifications(
assert work_items[1].notification_id == 102
# Assert notifications were marked as read
mock_client.mark_notification_as_read.assert_any_call(101)
mock_client.mark_notification_as_read.assert_any_call(102)
assert mock_client.mark_notification_as_read.call_count == 2
mock_client.notifications.mark_notification_as_read.assert_any_call(101)
mock_client.notifications.mark_notification_as_read.assert_any_call(102)
assert mock_client.notifications.mark_notification_as_read.call_count == 2
# Assert checkpoint date was persisted
assert temp_state_file.exists()