Skip to content

Commit 93cc81c

Browse files
committed
refactor: define terminal and interrupted task states once in a2a.utils.constants
The set of terminal task states was defined four times (active_task, default_request_handler, the cluster VersionedDatabaseTaskStore and TaskUpdater), and default_request_handler_v2 imported the task state sets from the ActiveTask module. Define TERMINAL_TASK_STATES and INTERRUPTED_TASK_STATES once, as frozensets, in a2a.utils.constants and use them everywhere. The previous public names (active_task.TERMINAL_TASK_STATES, active_task.INTERRUPTED_TASK_STATES and default_request_handler.TERMINAL_TASK_STATES) still resolve to the shared sets, so existing imports keep working. The private copies in the cluster store and TaskUpdater are removed. The legacy EventConsumer stop condition is left as is, since it intentionally covers a different set of states. a2a.utils.constants now imports a2a.types.a2a_pb2. a2a.types imports a2a.utils.errors, so errors must not import constants. Verification: - Every a2a module imported first in a fresh interpreter: no new failures compared with upstream/main (the same 3 pre-existing failures on both). - 27 first-import orders and ordered pairs of constants, errors, types and a2a.utils: all pass. - ./scripts/lint.sh: ruff and ty pass (3 pre-existing ty warnings in unrelated files). - pytest: 2487 passed, 203 skipped, 4 xfailed, 1 xpassed; the 4 cross-version integration errors also occur on upstream/main in this environment. - scripts/test_install_smoke.sh: all 6 profiles pass.
1 parent e615a8d commit 93cc81c

7 files changed

Lines changed: 48 additions & 40 deletions

File tree

‎src/a2a/server/agent_execution/active_task.py‎

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@
7575
TaskStatus,
7676
TaskStatusUpdateEvent,
7777
)
78+
from a2a.utils.constants import (
79+
INTERRUPTED_TASK_STATES,
80+
TERMINAL_TASK_STATES,
81+
)
7882
from a2a.utils.errors import (
7983
InternalError,
8084
InvalidAgentResponseError,
@@ -86,18 +90,6 @@
8690
logger = logging.getLogger(__name__)
8791

8892

89-
TERMINAL_TASK_STATES = {
90-
TaskState.TASK_STATE_COMPLETED,
91-
TaskState.TASK_STATE_CANCELED,
92-
TaskState.TASK_STATE_FAILED,
93-
TaskState.TASK_STATE_REJECTED,
94-
}
95-
INTERRUPTED_TASK_STATES = {
96-
TaskState.TASK_STATE_AUTH_REQUIRED,
97-
TaskState.TASK_STATE_INPUT_REQUIRED,
98-
}
99-
100-
10193
class _RequestStarted:
10294
def __init__(self, request_id: uuid.UUID, request_context: RequestContext):
10395
self.request_id = request_id

‎src/a2a/server/cluster/database_task_store.py‎

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,12 @@
4444
Task,
4545
TaskState,
4646
)
47+
from a2a.utils.constants import TERMINAL_TASK_STATES
4748
from a2a.utils.proto_utils import to_stream_response
4849

4950

5051
logger = logging.getLogger(__name__)
5152

52-
_TERMINAL_STATES = frozenset(
53-
{
54-
TaskState.TASK_STATE_COMPLETED,
55-
TaskState.TASK_STATE_CANCELED,
56-
TaskState.TASK_STATE_FAILED,
57-
TaskState.TASK_STATE_REJECTED,
58-
}
59-
)
60-
6153
# DB contention retry
6254
_MAX_ATTEMPTS = 5
6355
_RETRY_DELAY_S = 0.02
@@ -195,7 +187,10 @@ async def _save_once(
195187
if task.status.state == TaskState.TASK_STATE_CANCELED:
196188
stored = await self._lock_version(session, task.id, owner)
197189
current = await self._current_task(session, task.id, owner)
198-
if current is None or current.status.state in _TERMINAL_STATES:
190+
if (
191+
current is None
192+
or current.status.state in TERMINAL_TASK_STATES
193+
):
199194
raise ConcurrentTaskModificationError(task.id)
200195
new_version = (stored or 0) + 1
201196
if stored is None:

‎src/a2a/server/request_handlers/default_request_handler.py‎

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
TaskPushNotificationConfig,
5454
TaskState,
5555
)
56+
from a2a.utils.constants import TERMINAL_TASK_STATES
5657
from a2a.utils.errors import (
5758
ExtendedAgentCardNotConfiguredError,
5859
InternalError,
@@ -74,13 +75,6 @@
7475

7576
logger = logging.getLogger(__name__)
7677

77-
TERMINAL_TASK_STATES = {
78-
TaskState.TASK_STATE_COMPLETED,
79-
TaskState.TASK_STATE_CANCELED,
80-
TaskState.TASK_STATE_FAILED,
81-
TaskState.TASK_STATE_REJECTED,
82-
}
83-
8478

8579
@trace_class(kind=SpanKind.SERVER)
8680
class LegacyRequestHandler(RequestHandler):

‎src/a2a/server/request_handlers/default_request_handler_v2.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@
1515
RequestContextBuilder,
1616
SimpleRequestContextBuilder,
1717
)
18-
from a2a.server.agent_execution.active_task import (
19-
INTERRUPTED_TASK_STATES,
20-
TERMINAL_TASK_STATES,
21-
)
2218
from a2a.server.agent_execution.active_task_registry import ActiveTaskRegistry
2319
from a2a.server.agent_execution.plugin_agent_executor import (
2420
PluginAgentExecutor,
@@ -57,6 +53,10 @@
5753
TaskState,
5854
TaskStatusUpdateEvent,
5955
)
56+
from a2a.utils.constants import (
57+
INTERRUPTED_TASK_STATES,
58+
TERMINAL_TASK_STATES,
59+
)
6060
from a2a.utils.errors import (
6161
ExtendedAgentCardNotConfiguredError,
6262
InternalError,

‎src/a2a/server/tasks/task_updater.py‎

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
TaskStatus,
2222
TaskStatusUpdateEvent,
2323
)
24+
from a2a.utils.constants import TERMINAL_TASK_STATES
2425

2526

2627
class TaskUpdater:
@@ -51,12 +52,6 @@ def __init__(
5152
self.context_id = context_id
5253
self._lock = asyncio.Lock()
5354
self._terminal_state_reached = False
54-
self._terminal_states = {
55-
TaskState.TASK_STATE_COMPLETED,
56-
TaskState.TASK_STATE_CANCELED,
57-
TaskState.TASK_STATE_FAILED,
58-
TaskState.TASK_STATE_REJECTED,
59-
}
6055
self._artifact_id_generator = (
6156
artifact_id_generator if artifact_id_generator else UUIDGenerator()
6257
)
@@ -105,7 +100,7 @@ async def update_status(
105100
metadata=metadata,
106101
status=status,
107102
)
108-
if state in self._terminal_states:
103+
if state in TERMINAL_TASK_STATES:
109104
self._terminal_state_reached = True
110105
await self.event_queue.enqueue_event(event)
111106

‎src/a2a/utils/constants.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from enum import Enum
44

5+
from a2a.types.a2a_pb2 import TaskState
6+
57

68
AGENT_CARD_WELL_KNOWN_PATH = '/.well-known/agent-card.json'
79
DEFAULT_RPC_URL = '/'
@@ -29,3 +31,19 @@ class TransportProtocol(str, Enum):
2931
PROTOCOL_VERSION_1_0 = '1.0'
3032
PROTOCOL_VERSION_0_3 = '0.3'
3133
PROTOCOL_VERSION_CURRENT = PROTOCOL_VERSION_1_0
34+
35+
TERMINAL_TASK_STATES: frozenset[TaskState.ValueType] = frozenset(
36+
{
37+
TaskState.TASK_STATE_COMPLETED,
38+
TaskState.TASK_STATE_CANCELED,
39+
TaskState.TASK_STATE_FAILED,
40+
TaskState.TASK_STATE_REJECTED,
41+
}
42+
)
43+
44+
INTERRUPTED_TASK_STATES: frozenset[TaskState.ValueType] = frozenset(
45+
{
46+
TaskState.TASK_STATE_INPUT_REQUIRED,
47+
TaskState.TASK_STATE_AUTH_REQUIRED,
48+
}
49+
)

‎tests/utils/test_constants.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Tests for a2a.utils.constants module."""
22

3+
from a2a.server.agent_execution import active_task
4+
from a2a.server.request_handlers import default_request_handler
35
from a2a.utils import constants
46

57

@@ -24,3 +26,15 @@ def test_protocol_versions():
2426
"""Test protocol version constants."""
2527
assert constants.PROTOCOL_VERSION_1_0 == '1.0'
2628
assert constants.PROTOCOL_VERSION_CURRENT == '1.0'
29+
30+
31+
def test_task_state_sets_keep_their_previous_import_paths():
32+
"""Test that the previous public names still resolve to the shared sets."""
33+
assert active_task.TERMINAL_TASK_STATES is constants.TERMINAL_TASK_STATES
34+
assert (
35+
active_task.INTERRUPTED_TASK_STATES is constants.INTERRUPTED_TASK_STATES
36+
)
37+
assert (
38+
default_request_handler.TERMINAL_TASK_STATES
39+
is constants.TERMINAL_TASK_STATES
40+
)

0 commit comments

Comments
 (0)