-
Notifications
You must be signed in to change notification settings - Fork 351
Description
What happened?
The current implementation of the Artifact model in a2a/types.py allows the parts list to be empty. However, the A2A Protocol Specification (Section 4.17) explicitly states that the parts array:
"Must contain at least one part"
This lack of validation allows agents (e.g., strands-agents) to emit invalid artifacts with parts=[], which can cause issues for downstream consumers expecting valid data.
from uuid import uuid4
from a2a.types import Artifact
# This executes successfully but creates an invalid artifact according to spec
invalid_artifact = Artifact(
artifact_id="test-id",
parts=[], # Empty list is currently allowed
metadata={}
)The Artifact model should enforce the specification constraints. Initializing an artifact with an empty parts list should raise a ValidationError.
Comparison with Other SDKs
The Java SDK already enforces this constraint. As seen in a2a-java, the validation is strictly applied:
@JsonInclude(JsonInclude.Include.NON_ABSENT)
@JsonIgnoreProperties(ignoreUnknown = true)
public record Artifact(String artifactId, String name, String description, List<Part<?>> parts, Map<String, Object> metadata,
List<String> extensions) {
public Artifact {
Assert.checkNotNullParam("artifactId", artifactId);
Assert.checkNotNullParam("parts", parts);
if (parts.isEmpty()) {
throw new IllegalArgumentException("Parts cannot be empty"); // !!HERE!!
}
}
...The Python SDK should maintain parity with the Java SDK and the official specification.
Suggested Solution
Since the project uses Pydantic V2, we should add a min_length=1 constraint to the field definition.
as-is
https://github.com/a2aproject/a2a-python/blob/v0.3.22/src/a2a/types.py#L1372-L1400
to-be
+ from typing import Annotated
+ from pydantic import Field
class Artifact(A2ABaseModel):
"""
Represents a file, data structure, or other resource generated by an agent during a task.
"""
artifact_id: str
"""
A unique identifier (e.g. UUID) for the artifact within the scope of the task.
"""
description: str | None = None
"""
An optional, human-readable description of the artifact.
"""
extensions: list[str] | None = None
"""
The URIs of extensions that are relevant to this artifact.
"""
metadata: dict[str, Any] | None = None
"""
Optional metadata for extensions. The key is an extension-specific identifier.
"""
name: str | None = None
"""
An optional, human-readable name for the artifact.
"""
- parts: list[Part]
+ parts: Annotated[list[Part], Field(min_length=1)]
"""
An array of content parts that make up the artifact.
"""Relevant log output
Code of Conduct
- I agree to follow this project's Code of Conduct