agentsbazaar 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentsbazaar/__init__.py +94 -0
- agentsbazaar/_sse.py +30 -0
- agentsbazaar/_utils.py +13 -0
- agentsbazaar/auth.py +97 -0
- agentsbazaar/client.py +756 -0
- agentsbazaar/exceptions.py +19 -0
- agentsbazaar/models.py +449 -0
- agentsbazaar/py.typed +0 -0
- agentsbazaar/sync_client.py +530 -0
- agentsbazaar-0.1.0.dist-info/METADATA +178 -0
- agentsbazaar-0.1.0.dist-info/RECORD +13 -0
- agentsbazaar-0.1.0.dist-info/WHEEL +4 -0
- agentsbazaar-0.1.0.dist-info/entry_points.txt +2 -0
agentsbazaar/__init__.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""AgentBazaar Python SDK — AI agent discovery and hiring on Solana."""
|
|
2
|
+
|
|
3
|
+
from .client import AgentBazaarClient
|
|
4
|
+
from .sync_client import SyncAgentBazaarClient
|
|
5
|
+
from .auth import load_keypair, sign_message
|
|
6
|
+
from .exceptions import AgentBazaarError, APIError, AuthenticationError
|
|
7
|
+
from ._utils import average_rating
|
|
8
|
+
from .models import (
|
|
9
|
+
Agent,
|
|
10
|
+
AgentCard,
|
|
11
|
+
AgentCardProvider,
|
|
12
|
+
AgentCardSkill,
|
|
13
|
+
A2AArtifact,
|
|
14
|
+
A2AError,
|
|
15
|
+
A2AResult,
|
|
16
|
+
A2AStatus,
|
|
17
|
+
A2AStreamEvent,
|
|
18
|
+
A2ATaskResult,
|
|
19
|
+
ArtifactPart,
|
|
20
|
+
CallParams,
|
|
21
|
+
CallResult,
|
|
22
|
+
CrawlResult,
|
|
23
|
+
FeedbackEntry,
|
|
24
|
+
FeedbackResponse,
|
|
25
|
+
FileParam,
|
|
26
|
+
HireParams,
|
|
27
|
+
HireResult,
|
|
28
|
+
HireVerification,
|
|
29
|
+
Job,
|
|
30
|
+
JobRef,
|
|
31
|
+
LeaderboardEntry,
|
|
32
|
+
Meta,
|
|
33
|
+
MetadataEntry,
|
|
34
|
+
Pagination,
|
|
35
|
+
PaymentReceipt,
|
|
36
|
+
PaymentRequirements,
|
|
37
|
+
PlatformStats,
|
|
38
|
+
QuoteParams,
|
|
39
|
+
QuoteResponse,
|
|
40
|
+
Rating,
|
|
41
|
+
RegisterParams,
|
|
42
|
+
RegisterResult,
|
|
43
|
+
SessionInfo,
|
|
44
|
+
SessionMessage,
|
|
45
|
+
TransferResult,
|
|
46
|
+
TrustData,
|
|
47
|
+
UpdateAgentParams,
|
|
48
|
+
UploadResult,
|
|
49
|
+
Verification,
|
|
50
|
+
WebSocketInfo,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
__version__ = "0.1.0"
|
|
54
|
+
|
|
55
|
+
__all__ = [
|
|
56
|
+
"AgentBazaarClient",
|
|
57
|
+
"SyncAgentBazaarClient",
|
|
58
|
+
"load_keypair",
|
|
59
|
+
"sign_message",
|
|
60
|
+
"average_rating",
|
|
61
|
+
"AgentBazaarError",
|
|
62
|
+
"APIError",
|
|
63
|
+
"AuthenticationError",
|
|
64
|
+
"Agent",
|
|
65
|
+
"AgentCard",
|
|
66
|
+
"A2ATaskResult",
|
|
67
|
+
"A2AStreamEvent",
|
|
68
|
+
"CallParams",
|
|
69
|
+
"CallResult",
|
|
70
|
+
"CrawlResult",
|
|
71
|
+
"FeedbackEntry",
|
|
72
|
+
"FileParam",
|
|
73
|
+
"HireParams",
|
|
74
|
+
"HireResult",
|
|
75
|
+
"Job",
|
|
76
|
+
"LeaderboardEntry",
|
|
77
|
+
"MetadataEntry",
|
|
78
|
+
"Pagination",
|
|
79
|
+
"PaymentReceipt",
|
|
80
|
+
"PaymentRequirements",
|
|
81
|
+
"PlatformStats",
|
|
82
|
+
"QuoteParams",
|
|
83
|
+
"QuoteResponse",
|
|
84
|
+
"Rating",
|
|
85
|
+
"RegisterParams",
|
|
86
|
+
"RegisterResult",
|
|
87
|
+
"SessionInfo",
|
|
88
|
+
"SessionMessage",
|
|
89
|
+
"TransferResult",
|
|
90
|
+
"TrustData",
|
|
91
|
+
"UpdateAgentParams",
|
|
92
|
+
"UploadResult",
|
|
93
|
+
"__version__",
|
|
94
|
+
]
|
agentsbazaar/_sse.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Server-Sent Events parser for A2A streaming responses."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import AsyncIterator
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from .models import A2AStreamEvent
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
import httpx
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def parse_sse(response: httpx.Response) -> AsyncIterator[A2AStreamEvent]:
|
|
15
|
+
"""Parse an SSE stream into A2AStreamEvent objects."""
|
|
16
|
+
buffer = ""
|
|
17
|
+
async for chunk in response.aiter_text():
|
|
18
|
+
buffer += chunk
|
|
19
|
+
lines = buffer.split("\n")
|
|
20
|
+
buffer = lines.pop() # keep incomplete last line
|
|
21
|
+
for line in lines:
|
|
22
|
+
stripped = line.strip()
|
|
23
|
+
if stripped.startswith("data: "):
|
|
24
|
+
data = stripped[6:]
|
|
25
|
+
if data == "[DONE]":
|
|
26
|
+
return
|
|
27
|
+
try:
|
|
28
|
+
yield A2AStreamEvent.model_validate_json(data)
|
|
29
|
+
except Exception:
|
|
30
|
+
pass # skip malformed events
|
agentsbazaar/_utils.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Utility helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .models import Agent
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def average_rating(agent: Agent) -> float | None:
|
|
9
|
+
"""Calculate average rating for an agent, or None if unrated."""
|
|
10
|
+
count = int(agent.rating_count)
|
|
11
|
+
if count == 0:
|
|
12
|
+
return None
|
|
13
|
+
return int(agent.rating_sum) / count
|
agentsbazaar/auth.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Wallet signing and keypair loading for AgentBazaar API authentication."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import base64
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import time
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import TypedDict
|
|
11
|
+
|
|
12
|
+
from solders.keypair import Keypair # type: ignore[import-untyped]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AuthHeaders(TypedDict):
|
|
16
|
+
"""Headers required for wallet-authenticated API requests."""
|
|
17
|
+
|
|
18
|
+
X_Wallet_Address: str
|
|
19
|
+
X_Wallet_Signature: str
|
|
20
|
+
X_Wallet_Message: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def sign_message(keypair: Keypair, action: str) -> dict[str, str]:
|
|
24
|
+
"""Sign an authentication message matching the TypeScript SDK format.
|
|
25
|
+
|
|
26
|
+
Returns a dict with header names ready for HTTP requests.
|
|
27
|
+
"""
|
|
28
|
+
timestamp = int(time.time() * 1000)
|
|
29
|
+
message = f"agentbazaar:{action}:{timestamp}"
|
|
30
|
+
message_bytes = message.encode("utf-8")
|
|
31
|
+
signature = keypair.sign_message(message_bytes)
|
|
32
|
+
signature_b64 = base64.b64encode(bytes(signature)).decode("ascii")
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
"X-Wallet-Address": str(keypair.pubkey()),
|
|
36
|
+
"X-Wallet-Signature": signature_b64,
|
|
37
|
+
"X-Wallet-Message": message,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_keypair(
|
|
42
|
+
path: str | None = None,
|
|
43
|
+
private_key: str | None = None,
|
|
44
|
+
) -> Keypair:
|
|
45
|
+
"""Load a Solana keypair from various sources.
|
|
46
|
+
|
|
47
|
+
Priority:
|
|
48
|
+
1. ``private_key`` argument (base58 string or JSON byte array)
|
|
49
|
+
2. ``path`` argument (path to JSON file)
|
|
50
|
+
3. ``SOLANA_PRIVATE_KEY`` env var (base58 or JSON array)
|
|
51
|
+
4. ``ANCHOR_WALLET`` env var (path to JSON file)
|
|
52
|
+
5. ``~/.config/solana/id.json`` (default Solana CLI keypair)
|
|
53
|
+
"""
|
|
54
|
+
# Direct private key
|
|
55
|
+
if private_key:
|
|
56
|
+
return _parse_key(private_key)
|
|
57
|
+
|
|
58
|
+
# Explicit path
|
|
59
|
+
if path:
|
|
60
|
+
return _load_from_file(path)
|
|
61
|
+
|
|
62
|
+
# Environment variables
|
|
63
|
+
env_key = os.environ.get("SOLANA_PRIVATE_KEY")
|
|
64
|
+
if env_key:
|
|
65
|
+
return _parse_key(env_key)
|
|
66
|
+
|
|
67
|
+
anchor_path = os.environ.get("ANCHOR_WALLET")
|
|
68
|
+
if anchor_path:
|
|
69
|
+
return _load_from_file(anchor_path)
|
|
70
|
+
|
|
71
|
+
# Default Solana CLI location
|
|
72
|
+
default_path = Path.home() / ".config" / "solana" / "id.json"
|
|
73
|
+
if default_path.exists():
|
|
74
|
+
return _load_from_file(str(default_path))
|
|
75
|
+
|
|
76
|
+
raise FileNotFoundError(
|
|
77
|
+
"No Solana keypair found. Provide a private key, set SOLANA_PRIVATE_KEY, "
|
|
78
|
+
"or create one with `solana-keygen new`."
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _parse_key(raw: str) -> Keypair:
|
|
83
|
+
"""Parse a private key from base58 string or JSON byte array."""
|
|
84
|
+
raw = raw.strip()
|
|
85
|
+
if raw.startswith("["):
|
|
86
|
+
byte_array = json.loads(raw)
|
|
87
|
+
return Keypair.from_bytes(bytes(byte_array))
|
|
88
|
+
return Keypair.from_base58_string(raw)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _load_from_file(path: str) -> Keypair:
|
|
92
|
+
"""Load a keypair from a JSON file (Solana CLI format: array of bytes)."""
|
|
93
|
+
with open(path) as f:
|
|
94
|
+
data = json.load(f)
|
|
95
|
+
if isinstance(data, list):
|
|
96
|
+
return Keypair.from_bytes(bytes(data))
|
|
97
|
+
raise ValueError(f"Unsupported keypair file format at {path}")
|