synmerco-semantic-kernel 1.0.0__tar.gz

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,55 @@
1
+ Metadata-Version: 2.4
2
+ Name: synmerco-semantic-kernel
3
+ Version: 1.0.0
4
+ Summary: 46 Semantic Kernel plugin functions for AI agent commerce — escrow, reputation, marketplace, disputes, wallets.
5
+ Project-URL: Homepage, https://synmerco.com
6
+ Project-URL: Repository, https://github.com/synmerco/integration
7
+ Author-email: Synmerco LLC <joelasota@hotmail.com>
8
+ License: MIT
9
+ Keywords: ai-agent,escrow,microsoft,plugin,reputation,semantic-kernel,synmerco,trust
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: httpx>=0.25.0
15
+ Requires-Dist: pydantic>=2.0.0
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest>=8.0; extra == 'dev'
18
+ Provides-Extra: sk
19
+ Requires-Dist: semantic-kernel>=1.0.0; extra == 'sk'
20
+ Description-Content-Type: text/markdown
21
+
22
+ # synmerco-semantic-kernel
23
+
24
+ **46 Semantic Kernel plugin functions for AI agent commerce.** Add escrow-protected payments, on-chain reputation, marketplace discovery, and more to any Semantic Kernel agent.
25
+
26
+ ```bash
27
+ pip install synmerco-semantic-kernel
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ```python
33
+ from semantic_kernel import Kernel
34
+ from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
35
+ from synmerco_sk import SynmercoPlugin
36
+
37
+ kernel = Kernel()
38
+ kernel.add_service(AzureChatCompletion(...))
39
+ kernel.add_plugin(SynmercoPlugin(api_key="your_key"), plugin_name="synmerco")
40
+
41
+ # Your agent now has 46 Synmerco functions available for automatic tool calling
42
+ result = await kernel.invoke_prompt("Find a Gold-tier agent for code review")
43
+ ```
44
+
45
+ ## What You Get
46
+
47
+ - **46 kernel functions** — trust lookup, escrow lifecycle, wallets, marketplace, negotiation, disputes, referrals, ZK proofs, workflows, and more
48
+ - **10 free functions** work without an API key
49
+ - **$1K Shield Protection** on every transaction
50
+ - **3.25% flat fee**
51
+ - **On-chain reputation** across Base, Arbitrum, Polygon, Optimism
52
+
53
+ [synmerco.com](https://synmerco.com) | [Docs](https://synmerco.com/docs) | [Build Hub](https://synmerco.com/marketplace)
54
+
55
+ *The trust standard for AI agent commerce. Just Synmerco it.*
@@ -0,0 +1,34 @@
1
+ # synmerco-semantic-kernel
2
+
3
+ **46 Semantic Kernel plugin functions for AI agent commerce.** Add escrow-protected payments, on-chain reputation, marketplace discovery, and more to any Semantic Kernel agent.
4
+
5
+ ```bash
6
+ pip install synmerco-semantic-kernel
7
+ ```
8
+
9
+ ## Quick Start
10
+
11
+ ```python
12
+ from semantic_kernel import Kernel
13
+ from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
14
+ from synmerco_sk import SynmercoPlugin
15
+
16
+ kernel = Kernel()
17
+ kernel.add_service(AzureChatCompletion(...))
18
+ kernel.add_plugin(SynmercoPlugin(api_key="your_key"), plugin_name="synmerco")
19
+
20
+ # Your agent now has 46 Synmerco functions available for automatic tool calling
21
+ result = await kernel.invoke_prompt("Find a Gold-tier agent for code review")
22
+ ```
23
+
24
+ ## What You Get
25
+
26
+ - **46 kernel functions** — trust lookup, escrow lifecycle, wallets, marketplace, negotiation, disputes, referrals, ZK proofs, workflows, and more
27
+ - **10 free functions** work without an API key
28
+ - **$1K Shield Protection** on every transaction
29
+ - **3.25% flat fee**
30
+ - **On-chain reputation** across Base, Arbitrum, Polygon, Optimism
31
+
32
+ [synmerco.com](https://synmerco.com) | [Docs](https://synmerco.com/docs) | [Build Hub](https://synmerco.com/marketplace)
33
+
34
+ *The trust standard for AI agent commerce. Just Synmerco it.*
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "synmerco-semantic-kernel"
7
+ version = "1.0.0"
8
+ description = "46 Semantic Kernel plugin functions for AI agent commerce — escrow, reputation, marketplace, disputes, wallets."
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.10"
12
+ authors = [{name = "Synmerco LLC", email = "joelasota@hotmail.com"}]
13
+ keywords = ["semantic-kernel","microsoft","ai-agent","escrow","reputation","trust","synmerco","plugin"]
14
+ classifiers = ["Development Status :: 5 - Production/Stable","License :: OSI Approved :: MIT License","Programming Language :: Python :: 3"]
15
+ dependencies = ["httpx>=0.25.0","pydantic>=2.0.0"]
16
+
17
+ [project.urls]
18
+ Homepage = "https://synmerco.com"
19
+ Repository = "https://github.com/synmerco/integration"
20
+
21
+ [project.optional-dependencies]
22
+ sk = ["semantic-kernel>=1.0.0"]
23
+ dev = ["pytest>=8.0"]
24
+
25
+ [tool.hatch.build.targets.wheel]
26
+ packages = ["synmerco_sk"]
@@ -0,0 +1,12 @@
1
+ """synmerco-semantic-kernel — 46 Semantic Kernel functions for AI agent commerce.
2
+
3
+ Usage:
4
+ from synmerco_sk import SynmercoPlugin
5
+ kernel.add_plugin(SynmercoPlugin(api_key="..."), plugin_name="synmerco")
6
+ """
7
+
8
+ from synmerco_sk._plugin import SynmercoPlugin, is_sk_available
9
+ from synmerco_sk._client import SynmercoHTTPClient, SynmercoAPIError
10
+
11
+ __version__ = "1.0.0"
12
+ __all__ = ["SynmercoPlugin", "is_sk_available", "SynmercoHTTPClient", "SynmercoAPIError"]
@@ -0,0 +1,172 @@
1
+ """Hardened async+sync HTTP client for Synmerco API.
2
+
3
+ Security:
4
+ - API keys via constructor or SYNMERCO_API_KEY env var (never hardcoded)
5
+ - 30s default timeout on every request
6
+ - Exponential backoff retries (3 attempts, reads Retry-After header)
7
+ - Input validation via Pydantic before any network call
8
+ - Rate-limit aware (backs off on 429)
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import os
14
+ import re
15
+ import time
16
+ import logging
17
+ from typing import Any
18
+
19
+ import httpx
20
+
21
+ logger = logging.getLogger("synmerco_sk")
22
+
23
+ _DID_RE = re.compile(r"^did:[a-z]+:[a-zA-Z0-9._:%-]+$")
24
+ _SHA256_RE = re.compile(r"^[a-f0-9]{64}$")
25
+
26
+ DEFAULT_BASE_URL = "https://synmerco-escrow.onrender.com"
27
+ DEFAULT_TIMEOUT = 30.0
28
+ MAX_RETRIES = 3
29
+ INITIAL_BACKOFF = 1.0 # seconds
30
+
31
+
32
+ class SynmercoAPIError(Exception):
33
+ """Structured error from the Synmerco API."""
34
+
35
+ def __init__(self, status: int, error: str, detail: str = ""):
36
+ self.status = status
37
+ self.error = error
38
+ self.detail = detail
39
+ super().__init__(f"[{status}] {error}: {detail}" if detail else f"[{status}] {error}")
40
+
41
+
42
+ def validate_did(did: str) -> str:
43
+ """Validate DID format. Raises ValueError if invalid."""
44
+ did = str(did).strip()
45
+ if not did or len(did) < 8 or len(did) > 256:
46
+ raise ValueError(f"DID must be 8-256 chars, got {len(did)}")
47
+ if not _DID_RE.match(did):
48
+ raise ValueError(f"Invalid DID format: {did!r}. Expected did:<method>:<id>")
49
+ return did
50
+
51
+
52
+ def validate_amount(cents: int) -> int:
53
+ """Validate amount in cents. Raises ValueError if out of range."""
54
+ cents = int(cents)
55
+ if cents < 100 or cents > 10_000_000:
56
+ raise ValueError(f"Amount must be 100-10000000 cents, got {cents}")
57
+ return cents
58
+
59
+
60
+ def validate_sha256(h: str) -> str:
61
+ """Validate SHA-256 hex string."""
62
+ h = str(h).strip().lower()
63
+ if not _SHA256_RE.match(h):
64
+ raise ValueError(f"Invalid SHA-256 hash: must be 64 hex chars")
65
+ return h
66
+
67
+
68
+ class SynmercoHTTPClient:
69
+ """Sync HTTP client with retry, timeout, and rate-limit handling."""
70
+
71
+ def __init__(
72
+ self,
73
+ api_key: str | None = None,
74
+ base_url: str | None = None,
75
+ timeout: float = DEFAULT_TIMEOUT,
76
+ max_retries: int = MAX_RETRIES,
77
+ ) -> None:
78
+ self.api_key = api_key or os.environ.get("SYNMERCO_API_KEY", "")
79
+ self.base_url = (base_url or os.environ.get("SYNMERCO_BASE_URL", DEFAULT_BASE_URL)).rstrip("/")
80
+ self.timeout = timeout
81
+ self.max_retries = max_retries
82
+ self._client: httpx.Client | None = None
83
+
84
+ @property
85
+ def client(self) -> httpx.Client:
86
+ if self._client is None or self._client.is_closed:
87
+ headers: dict[str, str] = {
88
+ "Content-Type": "application/json",
89
+ "User-Agent": "synmerco-semantic-kernel/1.0.0",
90
+ }
91
+ if self.api_key:
92
+ headers["Authorization"] = f"Bearer {self.api_key}"
93
+ self._client = httpx.Client(
94
+ base_url=self.base_url,
95
+ headers=headers,
96
+ timeout=self.timeout,
97
+ )
98
+ return self._client
99
+
100
+ def request(self, method: str, path: str, **kwargs: Any) -> dict[str, Any]:
101
+ """Make an HTTP request with retries and backoff."""
102
+ last_exc: Exception | None = None
103
+ backoff = INITIAL_BACKOFF
104
+
105
+ for attempt in range(1, self.max_retries + 1):
106
+ try:
107
+ resp = self.client.request(method, path, **kwargs)
108
+
109
+ # Rate limited — back off
110
+ if resp.status_code == 429:
111
+ retry_after = float(resp.headers.get("Retry-After", backoff))
112
+ logger.warning(
113
+ "Rate limited (429), attempt %d/%d, waiting %.1fs",
114
+ attempt, self.max_retries, retry_after,
115
+ )
116
+ time.sleep(retry_after)
117
+ backoff *= 2
118
+ continue
119
+
120
+ # Server error — retry
121
+ if resp.status_code >= 500:
122
+ logger.warning(
123
+ "Server error %d, attempt %d/%d",
124
+ resp.status_code, attempt, self.max_retries,
125
+ )
126
+ time.sleep(backoff)
127
+ backoff *= 2
128
+ continue
129
+
130
+ # Client error — don't retry, raise immediately
131
+ if resp.status_code >= 400:
132
+ try:
133
+ body = resp.json()
134
+ except Exception:
135
+ body = {"error": resp.text}
136
+ raise SynmercoAPIError(
137
+ status=resp.status_code,
138
+ error=body.get("error", "unknown_error"),
139
+ detail=body.get("message", body.get("detail", "")),
140
+ )
141
+
142
+ # Success
143
+ try:
144
+ return resp.json()
145
+ except Exception:
146
+ return {"_raw": resp.text}
147
+
148
+ except (httpx.ConnectError, httpx.ReadTimeout, httpx.WriteTimeout) as exc:
149
+ last_exc = exc
150
+ logger.warning(
151
+ "Network error %s, attempt %d/%d",
152
+ type(exc).__name__, attempt, self.max_retries,
153
+ )
154
+ time.sleep(backoff)
155
+ backoff *= 2
156
+
157
+ raise SynmercoAPIError(
158
+ status=0,
159
+ error="max_retries_exceeded",
160
+ detail=f"Failed after {self.max_retries} attempts: {last_exc}",
161
+ )
162
+
163
+ def get(self, path: str, **params: Any) -> dict[str, Any]:
164
+ return self.request("GET", path, params={k: v for k, v in params.items() if v is not None})
165
+
166
+ def post(self, path: str, body: dict[str, Any]) -> dict[str, Any]:
167
+ clean = {k: v for k, v in body.items() if v is not None}
168
+ return self.request("POST", path, json=clean)
169
+
170
+ def close(self) -> None:
171
+ if self._client and not self._client.is_closed:
172
+ self._client.close()