chore: initial import of standalone agentscope project
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_en (ubuntu-latest, 3.10) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_zh (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.12) (push) Has been cancelled

This commit is contained in:
2026-03-02 18:21:40 +08:00
commit a842f1861f
561 changed files with 91892 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
"""The A2A related modules."""
from ._base import AgentCardResolverBase
from ._file_resolver import FileAgentCardResolver
from ._well_known_resolver import WellKnownAgentCardResolver
from ._nacos_resolver import NacosAgentCardResolver
__all__ = [
"AgentCardResolverBase",
"FileAgentCardResolver",
"WellKnownAgentCardResolver",
"NacosAgentCardResolver",
]

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
"""The A2A agent card resolver base class."""
from abc import abstractmethod
from typing import Any, TYPE_CHECKING
if TYPE_CHECKING:
from a2a.types import AgentCard
else:
AgentCard = "a2a.types.AgentCard"
class AgentCardResolverBase:
"""Base class for A2A agent card resolvers, responsible for fetching
agent cards from various sources. Implementations must provide the
`get_agent_card` method to retrieve the agent card.
"""
@abstractmethod
async def get_agent_card(self, *args: Any, **kwargs: Any) -> AgentCard:
"""Get Agent Card from the configured source.
Returns:
`AgentCard`:
The resolved agent card object.
"""

View File

@@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
"""The JSON file based A2A agent card resolver."""
import json
from pathlib import Path
from typing import TYPE_CHECKING
from ._base import AgentCardResolverBase
if TYPE_CHECKING:
from a2a.types import AgentCard
else:
AgentCard = "a2a.types.AgentCard"
class FileAgentCardResolver(AgentCardResolverBase):
"""Agent card resolver that loads AgentCard from a JSON file.
The JSON file should contain an AgentCard object with the following
required fields:
- name (str): The name of the agent
- url (str): The URL of the agent
- version (str): The version of the agent
- capabilities (dict): The capabilities of the agent
- default_input_modes (list[str]): Default input modes
- default_output_modes (list[str]): Default output modes
- skills (list): List of agent skills
Example JSON file content:
.. code-block:: json
{
"name": "RemoteAgent",
"url": "http://localhost:8000",
"description": "A remote A2A agent",
"version": "1.0.0",
"capabilities": {},
"default_input_modes": ["text/plain"],
"default_output_modes": ["text/plain"],
"skills": []
}
"""
def __init__(
self,
file_path: str,
) -> None:
"""Initialize the FileAgentCardResolver with the path to the JSON file.
Args:
file_path (`str`):
The path to the JSON file containing the agent card.
"""
self._file_path = file_path
async def get_agent_card(self) -> AgentCard:
"""Get the agent card from the JSON file.
Returns:
`AgentCard`:
The agent card loaded from the file.
"""
from a2a.types import AgentCard
path = Path(self._file_path)
if not path.exists():
raise FileNotFoundError(
f"Agent card file not found: {self._file_path}",
)
if not path.is_file():
raise ValueError(f"Path is not a file: {self._file_path}")
with path.open("r", encoding="utf-8") as f:
agent_json_data = json.load(f)
return AgentCard.model_validate(agent_json_data)

View File

@@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
"""The Nacos-based A2A Agent Card resolver."""
from typing import TYPE_CHECKING
from ._base import AgentCardResolverBase
from .._logging import logger
if TYPE_CHECKING:
from a2a.types import AgentCard
from v2.nacos.common.client_config import ClientConfig
else:
AgentCard = "a2a.types.AgentCard"
ClientConfig = "v2.nacos.common.client_config.ClientConfig"
class NacosAgentCardResolver(AgentCardResolverBase):
"""Nacos-based A2A Agent Card resolver.
Nacos is a dynamic service discovery, configuration and service
management platform for building cloud native applications. This resolver
fetches the agent card from a Nacos server and subscribes to updates.
"""
def __init__(
self,
remote_agent_name: str,
nacos_client_config: ClientConfig,
version: str | None = None,
) -> None:
"""Initialize the nacos agent card resolver.
Args:
remote_agent_name (`str`):
Name of the remote agent in Nacos.
nacos_client_config (`ClientConfig | None`, optional):
Nacos client configuration, where a `server_addresses`
parameter is required.
version (`str | None`, optional):
Version of the agent card to fetch. If None, fetches the
latest version. This version is also used when subscribing
to agent card updates.
Defaults to None (latest version).
"""
if not remote_agent_name:
raise ValueError(
"The remote_agent_name cannot be empty.",
)
if not nacos_client_config:
raise ValueError(
"The nacos_client_config cannot be None.",
)
self._nacos_client_config = nacos_client_config
self._remote_agent_name = remote_agent_name
self._version = version
async def get_agent_card(self) -> AgentCard:
"""Get agent card from Nacos with lazy initialization.
Returns:
`AgentCard`:
The resolved agent card from Nacos.
"""
try:
from v2.nacos.ai.model.ai_param import GetAgentCardParam
from v2.nacos.ai.nacos_ai_service import NacosAIService
except ImportError as e:
raise ImportError(
"Please install the nacos sdk by running `pip install "
"nacos-sdk-python>=3.0.0` first.",
) from e
client = None
try:
client = await NacosAIService.create_ai_service(
self._nacos_client_config,
)
await client.start()
return await client.get_agent_card(
GetAgentCardParam(
agent_name=self._remote_agent_name,
version=self._version,
),
)
finally:
if client:
# Close the Nacos client to free resources
try:
await client.shutdown()
except Exception as e:
logger.warning(
"Failed to shutdown Nacos client: %s",
str(e),
)

View File

@@ -0,0 +1,90 @@
# -*- coding: utf-8 -*-
"""The A2A well-known agent card resolver."""
from typing import TYPE_CHECKING
from urllib.parse import urlparse
from ._base import AgentCardResolverBase
from .._logging import logger
if TYPE_CHECKING:
from a2a.types import AgentCard
else:
AgentCard = "a2a.types.AgentCard"
class WellKnownAgentCardResolver(AgentCardResolverBase):
"""Agent card resolver that loads AgentCard from a well-known URL."""
def __init__(
self,
base_url: str,
agent_card_path: str | None = None,
) -> None:
"""Initialize the WellKnownAgentCardResolver.
Args:
base_url (`str`):
The base URL to resolve the agent card from.
agent_card_path (`str | None`, optional):
The path to the agent card relative to the base URL.
Defaults to AGENT_CARD_WELL_KNOWN_PATH from a2a.utils.
"""
self._base_url = base_url
self._agent_card_path = agent_card_path
async def get_agent_card(self) -> AgentCard:
"""Get the agent card from the well-known URL.
Returns:
`AgentCard`:
The agent card loaded from the URL.
"""
import httpx
from a2a.client import A2ACardResolver
from a2a.utils import AGENT_CARD_WELL_KNOWN_PATH
try:
parsed_url = urlparse(self._base_url)
if not parsed_url.scheme or not parsed_url.netloc:
logger.error(
"[%s] Invalid URL format: %s",
self.__class__.__name__,
self._base_url,
)
raise ValueError(
f"Invalid URL format: {self._base_url}",
)
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
relative_card_path = parsed_url.path
# Use default path if not specified
agent_card_path = (
self._agent_card_path
if self._agent_card_path is not None
else AGENT_CARD_WELL_KNOWN_PATH
)
# Use async context manager to ensure proper cleanup
async with httpx.AsyncClient(
timeout=httpx.Timeout(timeout=600),
) as _http_client:
resolver = A2ACardResolver(
httpx_client=_http_client,
base_url=base_url,
agent_card_path=agent_card_path,
)
return await resolver.get_agent_card(
relative_card_path=relative_card_path,
)
except Exception as e:
logger.error(
"[%s] Failed to resolve agent card from URL %s: %s",
self.__class__.__name__,
self._base_url,
e,
)
raise RuntimeError(
f"Failed to resolve AgentCard from URL "
f"{self._base_url}: {e}",
) from e