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.
@@ -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}")