Skip to content
Merged
3 changes: 1 addition & 2 deletions .github/actions/spelling/excludes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@
\.xz$
\.zip$
^\.github/actions/spelling/
^\Q.github/workflows/spelling.yaml\E$
^\Q.github/workflows/linter.yaml\E$
^\.github/workflows/
\.gitignore\E$
\.vscode/
noxfile.py
Expand Down
6 changes: 6 additions & 0 deletions .github/linters/.mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[mypy]
exclude = examples/
disable_error_code = import-not-found

[mypy-examples.*]
follow_imports = skip
9 changes: 5 additions & 4 deletions .github/workflows/linter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ name: Lint Code Base
#############################
# Start the job on all push #
#############################
# on:
# pull_request:
# branches: [main]
on: workflow_dispatch
on:
pull_request:
branches: [main]

###############
# Set the Job #
Expand Down Expand Up @@ -64,3 +63,5 @@ jobs:
VALIDATE_TYPESCRIPT_STANDARD: false
VALIDATE_GIT_COMMITLINT: false
MARKDOWN_CONFIG_FILE: .markdownlint.json
PYTHON_MYPY_CONFIG_FILE: .mypy.ini
FILTER_REGEX_EXCLUDE: "^examples/.*"
5 changes: 1 addition & 4 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:

- name: Build
run: uv build

- name: Upload distributions
uses: actions/upload-artifact@v4
with:
Expand All @@ -49,6 +49,3 @@ jobs:
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/



Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# mypy: ignore-errors
import asyncio
import logging

Expand Down
3 changes: 2 additions & 1 deletion examples/google_adk/calendar_agent/adk_agent_executor.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# mypy: ignore-errors
import asyncio
import logging

Expand Down Expand Up @@ -53,7 +54,7 @@ def __init__(self, runner: Runner, card: AgentCard):

def _run_agent(
self, session_id, new_message: types.Content
) -> AsyncGenerator[Event, None]:
) -> AsyncGenerator[Event]:
return self.runner.run_async(
session_id=session_id, user_id='self', new_message=new_message
)
Expand Down
6 changes: 6 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,15 @@ def format(session):
'pyupgrade',
'autoflake',
'ruff',
'no_implicit_optional',
)

if lint_paths_py:
session.run(
'no_implicit_optional',
'--use-union-or',
*lint_paths_py,
)
if not format_all:
session.run(
'pyupgrade',
Expand Down
4 changes: 3 additions & 1 deletion src/a2a/server/agent_execution/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from a2a.types import (
InvalidParamsError,
Message,
MessageSendParams,
MessageSendConfiguration,
MessageSendParams,
Task,
)
from a2a.utils import get_message_text
Expand Down Expand Up @@ -82,6 +82,8 @@ def context_id(self) -> str | None:

@property
def configuration(self) -> MessageSendConfiguration | None:
if not self._params:
return None
return self._params.configuration

def _check_or_generate_task_id(self) -> None:
Expand Down
29 changes: 18 additions & 11 deletions src/a2a/server/request_handlers/default_request_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,14 @@ async def on_message_send(
if task:
task = task_manager.update_with_message(params.message, task)
if self.should_add_push_info(params):
assert isinstance(self._push_notifier, PushNotifier) # For typechecker
assert isinstance(params.configuration, MessageSendConfiguration) # For typechecker
assert isinstance(params.configuration.pushNotificationConfig, PushNotificationConfig) # For typechecker
assert isinstance(self._push_notifier, PushNotifier)
assert isinstance(
params.configuration, MessageSendConfiguration
)
assert isinstance(
params.configuration.pushNotificationConfig,
PushNotificationConfig,
)
await self._push_notifier.set_info(
task.id, params.configuration.pushNotificationConfig
)
Expand Down Expand Up @@ -193,9 +198,14 @@ async def on_message_send_stream(
task = task_manager.update_with_message(params.message, task)

if self.should_add_push_info(params):
assert isinstance(self._push_notifier, PushNotifier) # For typechecker
assert isinstance(params.configuration, MessageSendConfiguration) # For typechecker
assert isinstance(params.configuration.pushNotificationConfig, PushNotificationConfig) # For typechecker
assert isinstance(self._push_notifier, PushNotifier)
assert isinstance(
params.configuration, MessageSendConfiguration
)
assert isinstance(
params.configuration.pushNotificationConfig,
PushNotificationConfig,
)
await self._push_notifier.set_info(
task.id, params.configuration.pushNotificationConfig
)
Expand Down Expand Up @@ -324,11 +334,8 @@ async def on_resubscribe_to_task(
yield event

def should_add_push_info(self, params: MessageSendParams) -> bool:
if (
return bool(
self._push_notifier
and params.configuration
and params.configuration.pushNotificationConfig
):
return True
else:
return False
)
10 changes: 4 additions & 6 deletions src/a2a/server/tasks/task_updater.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import uuid

from typing import Any

from a2a.server.events import EventQueue
from a2a.types import (
Artifact,
Expand Down Expand Up @@ -42,7 +44,7 @@ def add_artifact(
parts: list[Part],
artifact_id=str(uuid.uuid4()),
name: str | None = None,
metadata: dict[str, any] | None = None,
metadata: dict[str, Any] | None = None,
):
"""Add an artifact to the task."""
self.event_queue.enqueue_event(
Expand All @@ -68,11 +70,7 @@ def complete(self, message: Message | None = None):

def failed(self, message: Message | None = None):
"""Mark the task as failed."""
self.update_status(
TaskState.failed,
message=message,
final=True
)
self.update_status(TaskState.failed, message=message, final=True)

def submit(self, message: Message | None = None):
"""Mark the task as submitted."""
Expand Down
16 changes: 8 additions & 8 deletions src/a2a/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,34 @@
)
from a2a.utils.helpers import (
append_artifact_to_task,
are_modalities_compatible,
build_text_artifact,
create_task_obj,
are_modalities_compatible,
)
from a2a.utils.message import (
get_message_text,
get_text_parts,
new_agent_text_message,
new_agent_parts_message,
new_agent_text_message,
)
from a2a.utils.task import (
new_task,
completed_task,
new_task,
)


__all__ = [
'append_artifact_to_task',
'are_modalities_compatible',
'build_text_artifact',
'completed_task',
'create_task_obj',
'get_message_text',
'get_text_parts',
'new_agent_text_message',
'new_task',
'new_text_artifact',
'new_agent_parts_message',
'completed_task',
'new_agent_text_message',
'new_artifact',
'new_data_artifact',
'are_modalities_compatible',
'new_task',
'new_text_artifact',
]
16 changes: 12 additions & 4 deletions src/a2a/utils/artifact.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import uuid

from a2a.types import Artifact, Part, TextPart
from typing import Any

from a2a.types import Artifact, DataPart, Part, TextPart


def new_artifact(
Expand All @@ -13,20 +15,26 @@ def new_artifact(
description=description,
)


def new_text_artifact(
name: str,
text: str,
description: str = '',
) -> Artifact:
return new_artifact(
[Part(root=TextPart(text=text))], name, description,
[Part(root=TextPart(text=text))],
name,
description,
)


def new_data_artifact(
name: str,
data: dict[str, any],
data: dict[str, Any],
description: str = '',
):
return new_artifact(
[Part(root=DataPart(data=data))], name, description,
[Part(root=DataPart(data=data))],
name,
description,
)
2 changes: 2 additions & 0 deletions src/a2a/utils/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def new_agent_text_message(
contextId=context_id,
)


def new_agent_parts_message(
parts: list[Part],
context_id: str | None,
Expand All @@ -35,6 +36,7 @@ def new_agent_parts_message(
contextId=context_id,
)


def get_text_parts(parts: list[Part]) -> list[str]:
"""Return all text parts from a list of parts."""
return [part.root.text for part in parts if isinstance(part.root, TextPart)]
Expand Down
4 changes: 3 additions & 1 deletion src/a2a/utils/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ def completed_task(
task_id: str,
context_id: str,
artifacts: list[Artifact],
history: list[Message] = [],
history: list[Message] | None = None,
) -> Task:
if history is None:
history = []
return Task(
status=TaskStatus(state=TaskState.completed),
id=task_id,
Expand Down
Loading