Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

- **Keychain Access Error Handling**: The app now distinguishes between missing credentials (first launch) and macOS blocking keychain access (e.g. after app rename/update). A dedicated error screen guides the user to re-enter credentials instead of silently dropping into onboarding.
- **Swarm Callback Concurrency**: Duplicate swarm done/error callbacks are now serialized per swarm item using the swarm and item IDs.
- **Empty Swarm Task Extraction**: Swarm filling now returns before querying Rapyer when there are no task IDs, preventing an empty variadic lookup from scanning the Redis database.

### 🔄 Changed

Expand Down
45 changes: 23 additions & 22 deletions libs/mageflow/mageflow/swarm/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,25 +105,26 @@ async def fill_running_tasks(
publish_state.task_ids.extend(task_ids_to_run)
swarm.tasks_left_to_run.remove_range(0, num_of_task_to_run)

if task_ids_to_run:
tasks = await rapyer.afind(*task_ids_to_run)
tasks = cast(list[Signature], tasks)

# Update the kwargs locally, so swarm kwargs wont be duplicated on redis but still sent to task
swarm_kwargs = swarm.kwargs.copy()
swarm_msg = swarm_kwargs.pop(SWARM_MESSAGE_PARAM_NAME, None)
for task in tasks:
task.kwargs.update(**swarm_kwargs)

await swarm.ClientAdapter.acall_signatures(
tasks,
swarm_msg,
set_return_field=swarm.config.send_swarm_message_to_return_field,
**pub_kwargs,
)

async with publish_state.apipeline():
publish_state.task_ids.clear()
swarm.current_running_tasks += num_of_task_to_run
return tasks
return []
if not task_ids_to_run:
return []

tasks = await rapyer.afind(*task_ids_to_run)
tasks = cast(list[Signature], tasks)

# Update the kwargs locally, so swarm kwargs wont be duplicated on redis but still sent to task
swarm_kwargs = swarm.kwargs.copy()
swarm_msg = swarm_kwargs.pop(SWARM_MESSAGE_PARAM_NAME, None)
for task in tasks:
task.kwargs.update(**swarm_kwargs)

await swarm.ClientAdapter.acall_signatures(
tasks,
swarm_msg,
set_return_field=swarm.config.send_swarm_message_to_return_field,
**pub_kwargs,
)

async with publish_state.apipeline():
publish_state.task_ids.clear()
swarm.current_running_tasks += num_of_task_to_run
return tasks
16 changes: 16 additions & 0 deletions libs/mageflow/tests/unit/workflows/test_fill_running_tasks.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from unittest.mock import patch

import pytest
import thirdmagic
from hatchet_sdk.clients.admin import TriggerWorkflowOptions
Expand All @@ -8,6 +10,20 @@
from tests.integration.hatchet.models import ContextMessage


@pytest.mark.asyncio
async def test_fill_running_tasks_does_not_extract_when_task_ids_are_empty(
empty_swarm, mock_adapter
):
# Act
with patch("rapyer.afind", side_effect=AssertionError) as mock_afind:
tasks = await fill_running_tasks(empty_swarm)

# Assert
assert tasks == []
mock_afind.assert_not_called()
mock_adapter.acall_signatures.assert_not_awaited()


@pytest.mark.asyncio
@pytest.mark.parametrize(
[
Expand Down
Loading