aimarket-agent 2.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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AI-Factory Project Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: aimarket-agent
3
+ Version: 2.0.0
4
+ Summary: Reference consumer agent for AIMarket Protocol v2 — AI-to-AI discovery, payment, invoke
5
+ Author-email: AI-Factory <dev@modelmarket.dev>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/alexar76/aimarket-agent
8
+ Project-URL: Documentation, https://github.com/alexar76/aimarket-agent#readme
9
+ Project-URL: Repository, https://github.com/alexar76/aimarket-agent
10
+ Project-URL: Issues, https://github.com/alexar76/aimarket-agent/issues
11
+ Keywords: aimarket,ai-agents,marketplace,mcp,agent,httpx,sdk
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: httpx>=0.28
16
+ Requires-Dist: cryptography>=44
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest>=8; extra == "dev"
19
+ Requires-Dist: pytest-httpx>=0.35; extra == "dev"
20
+ Dynamic: license-file
21
+
22
+ <!-- aicom-mirror-notice -->
23
+ > **Mirror — read-only.**
24
+ > The canonical source for `aimarket-agent` lives in the AI-Factory monorepo.
25
+ > Open issues and PRs at `Superowner/aicom`; commits pushed here are
26
+ > overwritten by `scripts/mirror_satellites.sh` on the next sync run.
27
+ > See `docs/repository-canonical-policy.md` for the policy.
28
+
29
+ # AIMarket Agent v2.0.0
30
+
31
+ **Reference consumer agent for the AIMarket Protocol.**
32
+ `pip install aimarket-agent` — any AI (Claude, GPT, Cursor, LangChain) can discover, pay, and invoke capabilities across the AIMarket federation. MIT Licensed.
33
+
34
+ ## Live Hub
35
+
36
+ This agent connects to **[modelmarket.dev](https://modelmarket.dev)** — the reference hub with 12 capabilities and 14 plugins.
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ pip install aimarket-agent
42
+ ```
43
+
44
+ ## Quick Start
45
+
46
+ ```bash
47
+ # Full autonomous cycle
48
+ aimarket-agent run "translate spec to 5 languages + legal review" \
49
+ --base-url https://modelmarket.dev \
50
+ --budget 3.00
51
+
52
+ # Search capabilities
53
+ aimarket-agent search "code review" --base-url https://modelmarket.dev
54
+
55
+ # Invoke a single capability
56
+ aimarket-agent invoke prod-translate/translate.multi@v2 \
57
+ --base-url https://modelmarket.dev \
58
+ --input '{"text":"Hello world"}'
59
+ ```
60
+
61
+ ## Python SDK
62
+
63
+ ```python
64
+ from aimarket_agent import AIMarketAgent
65
+
66
+ agent = AIMarketAgent(
67
+ base_url="https://modelmarket.dev",
68
+ budget=3.00,
69
+ affiliate_id="my_app"
70
+ )
71
+
72
+ # Full cycle: discover → channel → invoke → settle → BOM
73
+ result = agent.run("translate spec to 5 languages + legal review")
74
+ print(f"Spent: ${result['total_spent_usd']:.2f}")
75
+
76
+ # Discovery only
77
+ capabilities = agent.discover("summarize long documents")
78
+ for c in capabilities:
79
+ print(f" {c['capability_id']} — ${c.get('price_per_call_usd', 0):.2f}")
80
+
81
+ # Single invoke
82
+ result = agent.invoke_single(
83
+ "prod-translate", "translate.multi@v2",
84
+ {"text": "Hello world", "locales": ["ru", "fr", "de"]}
85
+ )
86
+ ```
87
+
88
+ ## Full Autonomous Cycle
89
+
90
+ ```
91
+ ① GET /.well-known/ai-market.json → discover hub
92
+ ② POST /ai-market/discover → search capabilities
93
+ ③ POST /ai-market/channel/open → pre-fund channel
94
+ ④ POST /capabilities/{pid}/{cid}/invoke → invoke (safety-gated)
95
+ ⑤ POST /ai-market/channel/close → settle + refund
96
+ ⑥ Save bill_of_materials.json → signed audit trail
97
+ ```
98
+
99
+ ## Safety Gate
100
+
101
+ If an invocation is blocked by the safety gate (injection, PII, etc.), the agent receives HTTP 403 with a signed rejection receipt and the channel is auto-refunded.
102
+
103
+ ## Output
104
+
105
+ ```
106
+ [discover] 12 capabilities across 12 products
107
+ [plan] translate.multi@v2 (est $0.40)
108
+ [channel] opened ch_a8f3 with $3.00 deposit
109
+ [call] translate.multi@v2 ....... $0.40 ✓ 8.1s
110
+ [settle] used $0.40, refund $2.60
111
+ [saved] bill_of_materials.json
112
+ ```
113
+
114
+ ## Configuration
115
+
116
+ | CLI flag | Default | Description |
117
+ |----------|---------|-------------|
118
+ | `--base-url` | `http://127.0.0.1:9083` | Hub URL |
119
+ | `--budget` | `3.0` | Max budget in USD |
120
+ | `--affiliate` | — | Affiliate ID for revenue share |
121
+ | `--json` | false | Output as JSON |
122
+
123
+ ## License
124
+
125
+ MIT · Maintained by AI-Factory · [modelmarket.dev](https://modelmarket.dev) · [Hub API](https://modelmarket.dev/.well-known/ai-market.json)
@@ -0,0 +1,104 @@
1
+ <!-- aicom-mirror-notice -->
2
+ > **Mirror — read-only.**
3
+ > The canonical source for `aimarket-agent` lives in the AI-Factory monorepo.
4
+ > Open issues and PRs at `Superowner/aicom`; commits pushed here are
5
+ > overwritten by `scripts/mirror_satellites.sh` on the next sync run.
6
+ > See `docs/repository-canonical-policy.md` for the policy.
7
+
8
+ # AIMarket Agent v2.0.0
9
+
10
+ **Reference consumer agent for the AIMarket Protocol.**
11
+ `pip install aimarket-agent` — any AI (Claude, GPT, Cursor, LangChain) can discover, pay, and invoke capabilities across the AIMarket federation. MIT Licensed.
12
+
13
+ ## Live Hub
14
+
15
+ This agent connects to **[modelmarket.dev](https://modelmarket.dev)** — the reference hub with 12 capabilities and 14 plugins.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ pip install aimarket-agent
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```bash
26
+ # Full autonomous cycle
27
+ aimarket-agent run "translate spec to 5 languages + legal review" \
28
+ --base-url https://modelmarket.dev \
29
+ --budget 3.00
30
+
31
+ # Search capabilities
32
+ aimarket-agent search "code review" --base-url https://modelmarket.dev
33
+
34
+ # Invoke a single capability
35
+ aimarket-agent invoke prod-translate/translate.multi@v2 \
36
+ --base-url https://modelmarket.dev \
37
+ --input '{"text":"Hello world"}'
38
+ ```
39
+
40
+ ## Python SDK
41
+
42
+ ```python
43
+ from aimarket_agent import AIMarketAgent
44
+
45
+ agent = AIMarketAgent(
46
+ base_url="https://modelmarket.dev",
47
+ budget=3.00,
48
+ affiliate_id="my_app"
49
+ )
50
+
51
+ # Full cycle: discover → channel → invoke → settle → BOM
52
+ result = agent.run("translate spec to 5 languages + legal review")
53
+ print(f"Spent: ${result['total_spent_usd']:.2f}")
54
+
55
+ # Discovery only
56
+ capabilities = agent.discover("summarize long documents")
57
+ for c in capabilities:
58
+ print(f" {c['capability_id']} — ${c.get('price_per_call_usd', 0):.2f}")
59
+
60
+ # Single invoke
61
+ result = agent.invoke_single(
62
+ "prod-translate", "translate.multi@v2",
63
+ {"text": "Hello world", "locales": ["ru", "fr", "de"]}
64
+ )
65
+ ```
66
+
67
+ ## Full Autonomous Cycle
68
+
69
+ ```
70
+ ① GET /.well-known/ai-market.json → discover hub
71
+ ② POST /ai-market/discover → search capabilities
72
+ ③ POST /ai-market/channel/open → pre-fund channel
73
+ ④ POST /capabilities/{pid}/{cid}/invoke → invoke (safety-gated)
74
+ ⑤ POST /ai-market/channel/close → settle + refund
75
+ ⑥ Save bill_of_materials.json → signed audit trail
76
+ ```
77
+
78
+ ## Safety Gate
79
+
80
+ If an invocation is blocked by the safety gate (injection, PII, etc.), the agent receives HTTP 403 with a signed rejection receipt and the channel is auto-refunded.
81
+
82
+ ## Output
83
+
84
+ ```
85
+ [discover] 12 capabilities across 12 products
86
+ [plan] translate.multi@v2 (est $0.40)
87
+ [channel] opened ch_a8f3 with $3.00 deposit
88
+ [call] translate.multi@v2 ....... $0.40 ✓ 8.1s
89
+ [settle] used $0.40, refund $2.60
90
+ [saved] bill_of_materials.json
91
+ ```
92
+
93
+ ## Configuration
94
+
95
+ | CLI flag | Default | Description |
96
+ |----------|---------|-------------|
97
+ | `--base-url` | `http://127.0.0.1:9083` | Hub URL |
98
+ | `--budget` | `3.0` | Max budget in USD |
99
+ | `--affiliate` | — | Affiliate ID for revenue share |
100
+ | `--json` | false | Output as JSON |
101
+
102
+ ## License
103
+
104
+ MIT · Maintained by AI-Factory · [modelmarket.dev](https://modelmarket.dev) · [Hub API](https://modelmarket.dev/.well-known/ai-market.json)
@@ -0,0 +1,15 @@
1
+ """AIMarket Agent v2.0.0 — Reference consumer for AIMarket Protocol.
2
+
3
+ MIT Licensed. Lightweight pip-installable agent that any AI (Claude, GPT,
4
+ Cursor, LangChain) can use to discover, pay, and invoke capabilities
5
+ across the AIMarket federation.
6
+
7
+ Usage:
8
+ pip install aimarket-agent
9
+ aimarket-agent run "translate spec to 5 languages" --budget 3.00
10
+ """
11
+
12
+ from aimarket_agent.agent import AIMarketAgent
13
+
14
+ __all__ = ["AIMarketAgent", "__version__"]
15
+ __version__ = "2.0.0"
@@ -0,0 +1,268 @@
1
+ """AIMarketAgent — The reference consumer for Protocol v2.
2
+
3
+ Encapsulates the full autonomous cycle:
4
+ discovery → channel open → invoke (safety-gated) → settle → bill of materials.
5
+
6
+ Lightweight: only httpx + cryptography dependencies. No FastAPI, no database.
7
+ Designed to be pip-installed by any AI agent runtime.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import sys
14
+ import time
15
+ from typing import Any
16
+
17
+ import httpx
18
+
19
+
20
+ class AIMarketAgent:
21
+ """Reference consumer for AIMarket Protocol v2.
22
+
23
+ Usage:
24
+ agent = AIMarketAgent(base_url="https://hub.example.com", budget=3.00)
25
+ result = agent.run("translate spec to 5 langs + legal review")
26
+ print(result["bill_of_materials"])
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ base_url: str,
32
+ budget: float = 3.0,
33
+ timeout: float = 120.0,
34
+ affiliate_id: str = "",
35
+ ):
36
+ self.base_url = base_url.rstrip("/")
37
+ self.budget = budget
38
+ self.timeout = timeout
39
+ self.affiliate_id = affiliate_id
40
+ self.session = httpx.Client(timeout=timeout)
41
+
42
+ def _url(self, path: str) -> str:
43
+ return f"{self.base_url}{path}"
44
+
45
+ def _open_channel(self) -> str:
46
+ """Try to open a payment channel; return channel_id or "" if hub has no channels plugin."""
47
+ try:
48
+ ch = self.session.post(
49
+ self._url("/ai-market/v2/channel/open"),
50
+ json={"deposit_usd": self.budget, "tx_hash": f"agent-{int(time.time())}"},
51
+ )
52
+ if ch.status_code == 404:
53
+ return "" # channels plugin not installed on this hub
54
+ ch.raise_for_status()
55
+ return (ch.json().get("channel") or {}).get("channel_id", "")
56
+ except Exception:
57
+ return ""
58
+
59
+ def _close_channel(self, channel_id: str) -> dict[str, Any]:
60
+ if not channel_id:
61
+ return {"skipped": "no channel was opened"}
62
+ try:
63
+ r = self.session.post(
64
+ self._url("/ai-market/v2/channel/close"),
65
+ json={"channel_id": channel_id, "settle_tx_hash": f"agent-settle-{channel_id}"},
66
+ )
67
+ r.raise_for_status()
68
+ return r.json().get("settlement") or {}
69
+ except Exception:
70
+ return {"error": "settle failed"}
71
+
72
+ def run(self, task: str) -> dict[str, Any]:
73
+ """Execute the full autonomous cycle for *task*.
74
+
75
+ Returns bill-of-materials dict with all receipts.
76
+ """
77
+ result: dict[str, Any] = {"task": task, "ok": False}
78
+
79
+ # ── Phase 1: Discovery ──────────────────────────────────
80
+ try:
81
+ wk = self.session.get(self._url("/.well-known/ai-market.json"))
82
+ wk.raise_for_status()
83
+ except Exception as exc:
84
+ return {**result, "error": f"discovery failed: {exc}"}
85
+
86
+ # Hub v3 exposes capability discovery via GET /ai-market/v2/search.
87
+ # Older drafts proposed POST /ai-market/discover with a "plan" response —
88
+ # we fall back to v2 search and synthesise a one-step plan per match.
89
+ plan: list[dict[str, Any]] = []
90
+ try:
91
+ search = self.session.get(
92
+ self._url("/ai-market/v2/search"),
93
+ params={
94
+ "intent": task,
95
+ "budget": str(self.budget),
96
+ "limit": "6",
97
+ },
98
+ )
99
+ search.raise_for_status()
100
+ matches = search.json().get("matches") or []
101
+ for m in matches:
102
+ plan.append({
103
+ "product_id": m.get("product_id", ""),
104
+ "capability_id": m.get("capability_id", ""),
105
+ "source_hub": m.get("source_hub", "local"),
106
+ "draft_input": {"text": task},
107
+ "est_price_usd": m.get("routed_price_usd") or m.get("price_per_call_usd", 0),
108
+ })
109
+ except Exception as exc:
110
+ return {**result, "error": f"search failed: {exc}"}
111
+
112
+ if not plan:
113
+ return {**result, "plan": [], "note": "no matching capabilities"}
114
+
115
+ # Cap plan at first match for predictable spend; multi-step DAGs are a
116
+ # future protocol-level feature (pipelines endpoint).
117
+ plan = plan[:1]
118
+ result["plan"] = plan
119
+ result["estimated_total_usd"] = sum(s["est_price_usd"] for s in plan)
120
+
121
+ # ── Phase 2: Channel open (optional) ──────────────────
122
+ channel_id = self._open_channel()
123
+ result["channel_id"] = channel_id
124
+
125
+ # ── Phase 3: Invoke each step ──────────────────────────
126
+ results: list[dict[str, Any]] = []
127
+ context: dict[str, Any] = {}
128
+ total_spent = 0.0
129
+ all_ok = True
130
+
131
+ for step in plan:
132
+ pid = step["product_id"]
133
+ cid = step["capability_id"]
134
+ source_hub = step.get("source_hub", "local")
135
+ inp = dict(step.get("draft_input") or {})
136
+ if context:
137
+ inp.setdefault("context", context)
138
+
139
+ headers: dict[str, str] = {}
140
+ if channel_id:
141
+ headers["X-Payment-Channel"] = channel_id
142
+ if self.affiliate_id:
143
+ headers["X-AIMarket-Affiliate"] = self.affiliate_id
144
+
145
+ try:
146
+ r = self.session.post(
147
+ self._url("/ai-market/v2/invoke"),
148
+ json={
149
+ "product_id": pid,
150
+ "capability_id": cid,
151
+ "source_hub": source_hub,
152
+ "input": inp,
153
+ },
154
+ headers=headers,
155
+ )
156
+ except Exception as exc:
157
+ results.append({"error": str(exc), "capability_id": cid})
158
+ all_ok = False
159
+ break
160
+
161
+ if r.status_code == 403:
162
+ rejection = r.json()
163
+ results.append({
164
+ "capability_id": cid,
165
+ "safety_blocked": True,
166
+ "category": rejection.get("category"),
167
+ "reason": rejection.get("reason"),
168
+ })
169
+ all_ok = False
170
+ break
171
+
172
+ if r.status_code == 402:
173
+ results.append({
174
+ "capability_id": cid,
175
+ "payment_required": True,
176
+ "detail": r.json(),
177
+ })
178
+ all_ok = False
179
+ break
180
+
181
+ if not r.is_success:
182
+ results.append({"error": f"HTTP {r.status_code}", "capability_id": cid})
183
+ all_ok = False
184
+ break
185
+
186
+ body = r.json()
187
+ price_val = body.get("price_usd", 0) or 0
188
+ total_spent += price_val
189
+ results.append(body)
190
+
191
+ if body.get("success"):
192
+ context = body.get("result") or {}
193
+ else:
194
+ all_ok = False
195
+ break
196
+
197
+ # ── Phase 4: Settle ─────────────────────────────────────
198
+ settlement = self._close_channel(channel_id)
199
+
200
+ # ── Phase 5: Bill of materials ──────────────────────────
201
+ bom: dict[str, Any] = {
202
+ "task": task,
203
+ "plan": plan,
204
+ "results": results,
205
+ "settlement": settlement,
206
+ "channel_id": channel_id,
207
+ "total_spent_usd": round(total_spent, 4),
208
+ "all_ok": all_ok,
209
+ "protocol_version": "v2",
210
+ "agent_version": "2.0.0",
211
+ }
212
+
213
+ result["ok"] = all_ok
214
+ result["bill_of_materials"] = bom
215
+ result["total_spent_usd"] = round(total_spent, 4)
216
+ return result
217
+
218
+ def discover(self, query: str, limit: int = 8) -> list[dict[str, Any]]:
219
+ """Search for capabilities without invoking."""
220
+ try:
221
+ r = self.session.get(
222
+ self._url("/ai-market/v2/search"),
223
+ params={"intent": query, "budget": str(self.budget), "limit": str(limit)},
224
+ )
225
+ r.raise_for_status()
226
+ return r.json().get("matches") or []
227
+ except Exception:
228
+ return []
229
+
230
+ def invoke_single(
231
+ self,
232
+ product_id: str,
233
+ capability_id: str,
234
+ input_payload: dict[str, Any],
235
+ source_hub: str = "local",
236
+ ) -> dict[str, Any]:
237
+ """Invoke a single capability directly."""
238
+ channel_id = self._open_channel()
239
+ headers: dict[str, str] = {}
240
+ if channel_id:
241
+ headers["X-Payment-Channel"] = channel_id
242
+
243
+ try:
244
+ r = self.session.post(
245
+ self._url("/ai-market/v2/invoke"),
246
+ json={
247
+ "product_id": product_id,
248
+ "capability_id": capability_id,
249
+ "source_hub": source_hub,
250
+ "input": input_payload,
251
+ },
252
+ headers=headers,
253
+ )
254
+ finally:
255
+ self._close_channel(channel_id)
256
+
257
+ if r.status_code == 403:
258
+ return {"safety_blocked": True, **r.json()}
259
+ return r.json()
260
+
261
+ def close(self) -> None:
262
+ self.session.close()
263
+
264
+ def __enter__(self) -> "AIMarketAgent":
265
+ return self
266
+
267
+ def __exit__(self, *exc_info: Any) -> None:
268
+ self.close()
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env python3
2
+ """AIMarket Agent CLI — pip-installable reference consumer.
3
+
4
+ Usage:
5
+ aimarket-agent run "translate spec to 5 langs" --budget 3.00
6
+ aimarket-agent search "legal review"
7
+ aimarket-agent invoke prod-xxx/translate.multi@v2 --input '{"text":"hello"}'
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import argparse
13
+ import json
14
+ import sys
15
+
16
+ from aimarket_agent.agent import AIMarketAgent
17
+
18
+ _BOLD = "\033[1m"
19
+ _DIM = "\033[2m"
20
+ _GREEN = "\033[32m"
21
+ _YELLOW = "\033[33m"
22
+ _RED = "\033[31m"
23
+ _RESET = "\033[0m"
24
+
25
+
26
+ def main() -> int:
27
+ parser = argparse.ArgumentParser(
28
+ description="AIMarket Agent — AI-to-AI protocol consumer"
29
+ )
30
+ sub = parser.add_subparsers(dest="command")
31
+
32
+ # run
33
+ run_p = sub.add_parser("run", help="Execute full autonomous cycle")
34
+ run_p.add_argument("task", nargs="?", default="translate spec to 5 langs + legal review")
35
+ run_p.add_argument("--base-url", default="http://127.0.0.1:9083")
36
+ run_p.add_argument("--budget", type=float, default=3.0)
37
+ run_p.add_argument("--affiliate", default="")
38
+ run_p.add_argument("--json", action="store_true")
39
+
40
+ # search
41
+ search_p = sub.add_parser("search", help="Discover capabilities")
42
+ search_p.add_argument("query", nargs="?", default="")
43
+ search_p.add_argument("--base-url", default="http://127.0.0.1:9083")
44
+ search_p.add_argument("--limit", type=int, default=8)
45
+ search_p.add_argument("--json", action="store_true")
46
+
47
+ # invoke
48
+ invoke_p = sub.add_parser("invoke", help="Invoke a single capability")
49
+ invoke_p.add_argument("capability_ref", help="product_id/capability_id")
50
+ invoke_p.add_argument("--base-url", default="http://127.0.0.1:9083")
51
+ invoke_p.add_argument("--input", default="{}", help="JSON input payload")
52
+ invoke_p.add_argument("--budget", type=float, default=3.0)
53
+
54
+ args = parser.parse_args()
55
+
56
+ if args.command == "run":
57
+ return _cmd_run(args)
58
+ elif args.command == "search":
59
+ return _cmd_search(args)
60
+ elif args.command == "invoke":
61
+ return _cmd_invoke(args)
62
+ else:
63
+ parser.print_help()
64
+ return 0
65
+
66
+
67
+ def _cmd_run(args) -> int:
68
+ agent = AIMarketAgent(
69
+ base_url=args.base_url,
70
+ budget=args.budget,
71
+ affiliate_id=args.affiliate,
72
+ )
73
+ try:
74
+ result = agent.run(args.task)
75
+ except KeyboardInterrupt:
76
+ print(f"{_YELLOW}Interrupted{_RESET}", file=sys.stderr)
77
+ return 130
78
+ finally:
79
+ agent.close()
80
+
81
+ if args.json:
82
+ print(json.dumps(result, indent=2, ensure_ascii=False))
83
+ return 0 if result.get("ok") else 1
84
+
85
+ if result.get("error"):
86
+ print(f"[{_RED}error{_RESET}] {result['error']}", file=sys.stderr)
87
+ return 1
88
+
89
+ plan = result.get("plan") or []
90
+ steps = " → ".join(s["capability_id"] for s in plan)
91
+ est = result.get("estimated_total_usd", 0)
92
+ print(f"[{_GREEN}plan{_RESET}] {steps} (est ${est:.2f})")
93
+
94
+ bom = result.get("bill_of_materials") or {}
95
+ for r in bom.get("results") or []:
96
+ if r.get("safety_blocked"):
97
+ print(f"[{_RED}safety{_RESET}] {r['capability_id']} blocked: {r.get('category', '?')}")
98
+ continue
99
+ ok = r.get("success")
100
+ mark = "✓" if ok else "✗"
101
+ color = _GREEN if ok else _RED
102
+ print(f"[{color}call{_RESET}] {r.get('capability_id', '?')} ${r.get('price_usd', 0):.2f} {mark}")
103
+
104
+ settlement = bom.get("settlement") or {}
105
+ used = settlement.get("used_usd", 0)
106
+ refund = settlement.get("refund_usd", 0)
107
+ print(f"[{_GREEN}settle{_RESET}] used ${used:.2f}, refund ${refund:.2f}")
108
+
109
+ total = result.get("total_spent_usd", 0)
110
+ print(f"[{_BOLD}total{_RESET}] ${total:.2f}")
111
+
112
+ out_path = "bill_of_materials.json"
113
+ with open(out_path, "w") as f:
114
+ json.dump(bom, f, indent=2, ensure_ascii=False)
115
+ print(f"[{_DIM}saved{_RESET}] {out_path}")
116
+
117
+ return 0 if result.get("ok") else 1
118
+
119
+
120
+ def _cmd_search(args) -> int:
121
+ agent = AIMarketAgent(base_url=args.base_url)
122
+ try:
123
+ matches = agent.discover(args.query, limit=args.limit)
124
+ finally:
125
+ agent.close()
126
+
127
+ if args.json:
128
+ print(json.dumps(matches, indent=2, ensure_ascii=False))
129
+ return 0
130
+
131
+ print(f"\n{_BOLD}Search: \"{args.query}\"{_RESET}\n")
132
+ for i, m in enumerate(matches, 1):
133
+ print(f" {i}. {_BOLD}{m.get('capability_id', '?')}{_RESET}")
134
+ print(f" ${m.get('price_per_call_usd', 0):.2f} · {m.get('p50_latency_ms', '?')}ms")
135
+ if m.get("source_hub_name"):
136
+ print(f" 🌐 {m['source_hub_name']} (trust: {m.get('trust_score', '?')})")
137
+ print()
138
+ return 0
139
+
140
+
141
+ def _cmd_invoke(args) -> int:
142
+ parts = args.capability_ref.split("/", 1)
143
+ product_id = parts[0]
144
+ capability_id = parts[1] if len(parts) > 1 else parts[0]
145
+ inp = json.loads(args.input)
146
+
147
+ agent = AIMarketAgent(base_url=args.base_url, budget=args.budget)
148
+ try:
149
+ result = agent.invoke_single(product_id, capability_id, inp)
150
+ finally:
151
+ agent.close()
152
+
153
+ if result.get("safety_blocked"):
154
+ print(f"[{_RED}safety{_RESET}] Blocked: {result.get('category')} — {result.get('reason')}")
155
+ return 1
156
+
157
+ ok = result.get("success", False)
158
+ mark = "✓" if ok else "✗"
159
+ color = _GREEN if ok else _RED
160
+ print(f"[{color}invoke{_RESET}] {capability_id} ${result.get('price_usd', 0):.2f} {mark}")
161
+ if ok:
162
+ print(json.dumps(result.get("result", {}), indent=2, ensure_ascii=False))
163
+ return 0 if ok else 1
164
+
165
+
166
+ if __name__ == "__main__":
167
+ raise SystemExit(main())
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: aimarket-agent
3
+ Version: 2.0.0
4
+ Summary: Reference consumer agent for AIMarket Protocol v2 — AI-to-AI discovery, payment, invoke
5
+ Author-email: AI-Factory <dev@modelmarket.dev>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/alexar76/aimarket-agent
8
+ Project-URL: Documentation, https://github.com/alexar76/aimarket-agent#readme
9
+ Project-URL: Repository, https://github.com/alexar76/aimarket-agent
10
+ Project-URL: Issues, https://github.com/alexar76/aimarket-agent/issues
11
+ Keywords: aimarket,ai-agents,marketplace,mcp,agent,httpx,sdk
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: httpx>=0.28
16
+ Requires-Dist: cryptography>=44
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest>=8; extra == "dev"
19
+ Requires-Dist: pytest-httpx>=0.35; extra == "dev"
20
+ Dynamic: license-file
21
+
22
+ <!-- aicom-mirror-notice -->
23
+ > **Mirror — read-only.**
24
+ > The canonical source for `aimarket-agent` lives in the AI-Factory monorepo.
25
+ > Open issues and PRs at `Superowner/aicom`; commits pushed here are
26
+ > overwritten by `scripts/mirror_satellites.sh` on the next sync run.
27
+ > See `docs/repository-canonical-policy.md` for the policy.
28
+
29
+ # AIMarket Agent v2.0.0
30
+
31
+ **Reference consumer agent for the AIMarket Protocol.**
32
+ `pip install aimarket-agent` — any AI (Claude, GPT, Cursor, LangChain) can discover, pay, and invoke capabilities across the AIMarket federation. MIT Licensed.
33
+
34
+ ## Live Hub
35
+
36
+ This agent connects to **[modelmarket.dev](https://modelmarket.dev)** — the reference hub with 12 capabilities and 14 plugins.
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ pip install aimarket-agent
42
+ ```
43
+
44
+ ## Quick Start
45
+
46
+ ```bash
47
+ # Full autonomous cycle
48
+ aimarket-agent run "translate spec to 5 languages + legal review" \
49
+ --base-url https://modelmarket.dev \
50
+ --budget 3.00
51
+
52
+ # Search capabilities
53
+ aimarket-agent search "code review" --base-url https://modelmarket.dev
54
+
55
+ # Invoke a single capability
56
+ aimarket-agent invoke prod-translate/translate.multi@v2 \
57
+ --base-url https://modelmarket.dev \
58
+ --input '{"text":"Hello world"}'
59
+ ```
60
+
61
+ ## Python SDK
62
+
63
+ ```python
64
+ from aimarket_agent import AIMarketAgent
65
+
66
+ agent = AIMarketAgent(
67
+ base_url="https://modelmarket.dev",
68
+ budget=3.00,
69
+ affiliate_id="my_app"
70
+ )
71
+
72
+ # Full cycle: discover → channel → invoke → settle → BOM
73
+ result = agent.run("translate spec to 5 languages + legal review")
74
+ print(f"Spent: ${result['total_spent_usd']:.2f}")
75
+
76
+ # Discovery only
77
+ capabilities = agent.discover("summarize long documents")
78
+ for c in capabilities:
79
+ print(f" {c['capability_id']} — ${c.get('price_per_call_usd', 0):.2f}")
80
+
81
+ # Single invoke
82
+ result = agent.invoke_single(
83
+ "prod-translate", "translate.multi@v2",
84
+ {"text": "Hello world", "locales": ["ru", "fr", "de"]}
85
+ )
86
+ ```
87
+
88
+ ## Full Autonomous Cycle
89
+
90
+ ```
91
+ ① GET /.well-known/ai-market.json → discover hub
92
+ ② POST /ai-market/discover → search capabilities
93
+ ③ POST /ai-market/channel/open → pre-fund channel
94
+ ④ POST /capabilities/{pid}/{cid}/invoke → invoke (safety-gated)
95
+ ⑤ POST /ai-market/channel/close → settle + refund
96
+ ⑥ Save bill_of_materials.json → signed audit trail
97
+ ```
98
+
99
+ ## Safety Gate
100
+
101
+ If an invocation is blocked by the safety gate (injection, PII, etc.), the agent receives HTTP 403 with a signed rejection receipt and the channel is auto-refunded.
102
+
103
+ ## Output
104
+
105
+ ```
106
+ [discover] 12 capabilities across 12 products
107
+ [plan] translate.multi@v2 (est $0.40)
108
+ [channel] opened ch_a8f3 with $3.00 deposit
109
+ [call] translate.multi@v2 ....... $0.40 ✓ 8.1s
110
+ [settle] used $0.40, refund $2.60
111
+ [saved] bill_of_materials.json
112
+ ```
113
+
114
+ ## Configuration
115
+
116
+ | CLI flag | Default | Description |
117
+ |----------|---------|-------------|
118
+ | `--base-url` | `http://127.0.0.1:9083` | Hub URL |
119
+ | `--budget` | `3.0` | Max budget in USD |
120
+ | `--affiliate` | — | Affiliate ID for revenue share |
121
+ | `--json` | false | Output as JSON |
122
+
123
+ ## License
124
+
125
+ MIT · Maintained by AI-Factory · [modelmarket.dev](https://modelmarket.dev) · [Hub API](https://modelmarket.dev/.well-known/ai-market.json)
@@ -0,0 +1,12 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ aimarket_agent/__init__.py
5
+ aimarket_agent/agent.py
6
+ aimarket_agent/cli.py
7
+ aimarket_agent.egg-info/PKG-INFO
8
+ aimarket_agent.egg-info/SOURCES.txt
9
+ aimarket_agent.egg-info/dependency_links.txt
10
+ aimarket_agent.egg-info/entry_points.txt
11
+ aimarket_agent.egg-info/requires.txt
12
+ aimarket_agent.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ aimarket-agent = aimarket_agent.cli:main
@@ -0,0 +1,6 @@
1
+ httpx>=0.28
2
+ cryptography>=44
3
+
4
+ [dev]
5
+ pytest>=8
6
+ pytest-httpx>=0.35
@@ -0,0 +1 @@
1
+ aimarket_agent
@@ -0,0 +1,29 @@
1
+ [build-system]
2
+ requires = ["setuptools>=75", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "aimarket-agent"
7
+ version = "2.0.0"
8
+ description = "Reference consumer agent for AIMarket Protocol v2 — AI-to-AI discovery, payment, invoke"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.11"
12
+ authors = [{name = "AI-Factory", email = "dev@modelmarket.dev"}]
13
+ keywords = ["aimarket", "ai-agents", "marketplace", "mcp", "agent", "httpx", "sdk"]
14
+ dependencies = ["httpx>=0.28", "cryptography>=44"]
15
+
16
+ [project.urls]
17
+ Homepage = "https://github.com/alexar76/aimarket-agent"
18
+ Documentation = "https://github.com/alexar76/aimarket-agent#readme"
19
+ Repository = "https://github.com/alexar76/aimarket-agent"
20
+ Issues = "https://github.com/alexar76/aimarket-agent/issues"
21
+
22
+ [project.optional-dependencies]
23
+ dev = ["pytest>=8", "pytest-httpx>=0.35"]
24
+
25
+ [project.scripts]
26
+ aimarket-agent = "aimarket_agent.cli:main"
27
+
28
+ [tool.setuptools.packages.find]
29
+ include = ["aimarket_agent*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+