@aeon-ai-pay/aigateway 0.2.1 → 0.2.2

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.
package/bin/cli.mjs CHANGED
@@ -127,7 +127,10 @@ sb
127
127
 
128
128
  sb
129
129
  .command("tools")
130
- .description("Fetch and display the AI tool catalog from the server (no caching, always live)")
130
+ .description("Fetch and display the AI tool catalog (with optional filters)")
131
+ .option("--model <id>", "Return only this model (+effectiveSchema)")
132
+ .option("--category <key>", "Return only this category (image / video / tts / etc.)")
133
+ .option("--tier <tier>", "Filter models by tier (price | quality | balanced)")
131
134
  .action(async (opts) => {
132
135
  const { sbTools } = await import("../src/commands/sb-tools.mjs");
133
136
  return sbTools(opts);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aeon-ai-pay/aigateway",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "AI Agents discover, invoke, and settle paid LLMs, APIs, and Skills — starting with Skill Boss. No manual key setup. No prepayment. Pay-per-call via x402 or Agent Card.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,7 +26,7 @@ description: >
26
26
  emoji: "🛰️"
27
27
  homepage: https://github.com/AEON-Project/aigateway
28
28
  metadata:
29
- version: "0.2.1"
29
+ version: "0.2.2"
30
30
  author: AEON-Project
31
31
  openclaw:
32
32
  requires:
@@ -49,13 +49,29 @@ compatibility: 需要 Node.js >= 25 和 npm
49
49
 
50
50
  完整工具索引(每个 `category` 含 `agentTrigger` / `defaultInputsSchema`;每个 `model` 含 `id` / `useCase` / `tier` / 可选 `inputsOverride`)由服务端集中维护,每次实时拉取。**无本地缓存** —— 服务端是 single source of truth,model 新增 / schema 改动立即生效。
51
51
 
52
- **Agent 在 Phase 3.2 选 model 时**:
52
+ **Agent 在 Phase 3.2 选 model 时**,**优先用 CLI 自带的过滤参数**,避免自己写代码解析 list:
53
53
 
54
54
  ```bash
55
- aigateway sb tools # 调服务端 → stdout 输出 envelope,data 字段即完整 catalog JSON
55
+ # 推荐:CLI 端过滤,直接拿你要的
56
+ aigateway sb tools --model replicate/black-forest-labs/flux-schnell # 单 model + effectiveSchema
57
+ aigateway sb tools --category image # 单类别(含所有 models 与 defaultInputsSchema)
58
+ aigateway sb tools --category image --tier price # 按 tier 过滤
59
+ aigateway sb tools --tier quality # 所有类别中 quality 档
60
+
61
+ # 备选:全量 catalog(少用,主要用于探索)
62
+ aigateway sb tools
63
+ ```
64
+
65
+ **如果必须自己解析**(jq 推荐):
66
+
67
+ ```bash
68
+ aigateway sb tools | jq '.data.categories[] | select(.key=="image") | .models[]'
69
+ aigateway sb tools | jq '.data.categories[].models[] | select(.id=="<model_id>")'
56
70
  ```
57
71
 
58
- stdout JSON parse 出来,按 `data.categories[].models[]` model。每次都从服务端实时获取,无本地缓存。
72
+ ⚠️ **不要在 Python list 上调 `.find()`** —— list 没有这个方法。用 `next(m for m in ...)` dict 索引化。但通常**根本不需要**自己解析,用 `aigateway sb tools --model X` 就够了。
73
+
74
+ 每次都从服务端实时获取,无本地缓存。
59
75
 
60
76
  价格不在 catalog 里:x402 第一阶段(402 响应)会实时返回本次调用的 USDT 金额,CLI 自动展示给用户。
61
77
 
@@ -236,16 +252,58 @@ Approve: {approveTx truncated or "already approved"}
236
252
 
237
253
  > 同一意图可能落到多个类别(如"做个含图的演示" = `image` + `ui_generation`),按用户**主诉求**选最匹配的一类先做。
238
254
 
239
- ### 3.2 选择模型挑选策略
255
+ ### 3.2 列出候选 model,等用户挑
256
+
257
+ ⭐ **默认模式**:**AI 不擅自选 model**,而是把候选 + 价格列给用户,让用户拍板。**推荐默认选最便宜的**(按 `tier: "price"` 优先排序)。
258
+
259
+ **例外**:用户原话已经指定了 model(`"用 flux-2-max 画"`) → 直接用,跳过列表。
260
+
261
+ #### 候选展示模板(按字面渲染)
262
+
263
+ 跑 `aigateway sb tools --category <key>` 拿到该类所有 model,**按 tier 排序**(price → balanced → quality)后用以下模板渲染:
264
+
265
+ ```
266
+ ✨ 可用 model({category 中文名})
267
+
268
+ # Model ID 价格 档位
269
+ 1 {model_id} ${price}{unit} ← 推荐
270
+ 2 {model_id} ${price}{unit}
271
+ 3 {model_id} ${price}{unit}
272
+ ...
273
+
274
+ 直接回车或输入 1 用推荐项;或输入序号 / 完整 model_id 选其它。
275
+ ```
276
+
277
+ **字段规则**:
278
+ - 第 1 行**永远**是 tier=`price` 档(最便宜),并加 `← 推荐` 后缀
279
+ - `${price}` 取 catalog 中 `model.price`(USD 数值,保留小数)
280
+ - `{unit}` 按 category 推断(见下表)
281
+ - 用户只输序号 `2` / 完整 `model_id` / 直接回车 都接受
282
+ - 用户没回应 → 用 #1(推荐项)
283
+
284
+ #### Category → 价格单位映射
285
+
286
+ | Category | 价格单位 `{unit}` |
287
+ | --- | --- |
288
+ | `image` | `/张` |
289
+ | `video` | `/秒` |
290
+ | `tts` | `/1K 字符` |
291
+ | `stt` | `/分钟` |
292
+ | `embeddings` | `/M tokens` |
293
+ | `search` / `scraper` / `social_data` / `email` / `sms` / `document` / `ui_generation` / `financial` / `news` / `geolocation` / `utility` / `apify` | `/次` |
294
+
295
+ > 价格是**预估单价**,**最终金额**以 x402 第一阶段(402 响应)的实时数据为准(CLI 会在调用后展示 `💰 Charged`)。
240
296
 
241
- 按用户偏好挑 `model_id`(`price` / `quality` / `balanced` 三档策略):
297
+ #### 用户偏好覆盖
242
298
 
243
- | 用户表达偏好 | 策略 |
299
+ | 用户原话 | AI 动作 |
244
300
  | --- | --- |
245
- | "用便宜的 / 快速试一下" | 选**最便宜**的 model(如 image `flux-schnell`、search `tavily/search`) |
246
- | "用最好的 / 高质量" | 选该类**旗舰** model(如 image 选 `flux-2-max`、video 选 `seedance/seedance-2.0`) |
247
- | 没说偏好(默认) | 选**平衡**款(如 image 默认 `flux-schnell`、search 默认 `perplexity/search`) |
248
- | 用户直接指定 model | **直接用**,跳过挑选 |
301
+ | (无偏好)"画一只猫" | 列候选 等用户选 用户选完才调 `sb invoke` |
302
+ | "用便宜的画一只猫" | 直接用候选 #1(最便宜),跳过等待 |
303
+ | "用最好的画一只猫" | 直接用 `tier: "quality"` 档第一个,跳过等待 |
304
+ | "用 flux-2-max 画一只猫" | 直接用 `flux-2-max`,**完全跳过候选展示** |
305
+ | 用户输入序号 (e.g. "2") | 用候选列表第 2 行的 model |
306
+ | 用户输入完整 model_id | 用该 model_id |
249
307
 
250
308
  **查询 model 清单**:跑 `aigateway sb tools` 拿到完整 catalog,从 stdout envelope 的 `data.categories[].models[]` 中按 `tier` 挑 `model_id`。
251
309
 
@@ -531,6 +589,9 @@ aigateway clean # 卸载 skill、清缓存
531
589
  | Phase 2 第一行 | `> Topping up wallet...` |
532
590
  | Phase 2 成功 header | `✅ Wallet prepared` |
533
591
  | Phase 4.1 sb invoke 第一行 | `> Invoking {model_id}...` |
592
+ | Phase 3.2 候选列表 header | `✨ 可用 model({category 中文名})` |
593
+ | Phase 3.2 推荐项后缀 | `← 推荐` |
594
+ | Phase 3.2 候选行格式 | `{#} {model_id} ${price}{unit}` |
534
595
  | Phase 5.2 通用成功 header | `✅ Done` |
535
596
  | Phase 5.2 Powered 行 | `🧩 Powered by Skillboss · {model_id}` |
536
597
  | Phase 5.2 Path 行 | `📁 Path {localPath}` |
@@ -1,17 +1,21 @@
1
1
  /**
2
2
  * sb-tools: fetch and display the AI tool catalog from the server.
3
3
  *
4
- * aigateway sb tools
4
+ * aigateway sb tools # full catalog
5
+ * aigateway sb tools --model <id> # single model (+effectiveSchema)
6
+ * aigateway sb tools --category <key> # single category (+ all models)
7
+ * aigateway sb tools --tier <price|quality|balanced> # filter by tier
8
+ * (--category and --tier can combine)
5
9
  *
6
- * No cachingalways hits the server. Server-side `tools-catalog.json` is the
7
- * single source of truth. Stdout is the full envelope with `data` = catalog.
10
+ * Filters are server-agnostic applied client-side after fetching full catalog.
11
+ * Letting Agents target exactly what they need (via --model / --category) avoids
12
+ * the typical `.find()`-on-list type confusion when parsing the JSON manually.
8
13
  *
9
- * Endpoint: GET {serviceUrl}/open/api/skillBoss/tools-catalog
10
- * (free, no x402; price fields are stripped server-side)
14
+ * Endpoint: GET {serviceUrl}/open/api/skillBoss/tools-catalog (no x402)
11
15
  */
12
16
  import { resolve } from "../config.mjs";
13
17
  import { emitOk, emitErr, logInfo } from "../output.mjs";
14
- import { fetchCatalog } from "../catalog.mjs";
18
+ import { fetchCatalog, findModel } from "../catalog.mjs";
15
19
 
16
20
  export async function sbTools(opts) {
17
21
  const serviceUrl = resolve(opts.serviceUrl, "AIGATEWAY_SERVICE_URL", "serviceUrl");
@@ -21,9 +25,9 @@ export async function sbTools(opts) {
21
25
  }
22
26
 
23
27
  logInfo("Fetching tools catalog from server...");
24
- let data;
28
+ let catalog;
25
29
  try {
26
- data = await fetchCatalog(serviceUrl);
30
+ catalog = await fetchCatalog(serviceUrl);
27
31
  } catch (e) {
28
32
  emitErr("sb-tools", "CATALOG_FETCH_FAILED", {
29
33
  message: `Failed to fetch tools catalog: ${e.message}`,
@@ -33,5 +37,59 @@ export async function sbTools(opts) {
33
37
  return;
34
38
  }
35
39
 
36
- emitOk("sb-tools", data, { success: true, ...data });
40
+ // ─── --model <id> : return single model with effectiveSchema ────────────
41
+ if (opts.model) {
42
+ const found = findModel(catalog, opts.model);
43
+ if (!found) {
44
+ emitErr("sb-tools", "INVALID_MODEL_ID", {
45
+ message: `Model "${opts.model}" not found in catalog.`,
46
+ model: opts.model,
47
+ availableCategories: catalog.categories.map((c) => c.key),
48
+ });
49
+ return;
50
+ }
51
+ emitOk("sb-tools", {
52
+ mode: "single-model",
53
+ category: found.category.key,
54
+ model: found.model,
55
+ effectiveSchema: found.effectiveSchema,
56
+ });
57
+ return;
58
+ }
59
+
60
+ // ─── --category <key> : return single category ──────────────────────────
61
+ if (opts.category) {
62
+ const cat = catalog.categories.find((c) => c.key === opts.category);
63
+ if (!cat) {
64
+ emitErr("sb-tools", "CATEGORY_NOT_FOUND", {
65
+ message: `Category "${opts.category}" not found.`,
66
+ category: opts.category,
67
+ availableCategories: catalog.categories.map((c) => c.key),
68
+ });
69
+ return;
70
+ }
71
+ const filtered = applyTierFilter(cat, opts.tier);
72
+ emitOk("sb-tools", { mode: "single-category", category: filtered });
73
+ return;
74
+ }
75
+
76
+ // ─── --tier alone : filter all categories ───────────────────────────────
77
+ if (opts.tier) {
78
+ const cats = catalog.categories
79
+ .map((c) => applyTierFilter(c, opts.tier))
80
+ .filter((c) => c.models.length > 0);
81
+ emitOk("sb-tools", { ...catalog, categories: cats, mode: "tier-filtered", tier: opts.tier });
82
+ return;
83
+ }
84
+
85
+ // ─── default: full catalog ──────────────────────────────────────────────
86
+ emitOk("sb-tools", catalog, { success: true, ...catalog });
87
+ }
88
+
89
+ function applyTierFilter(cat, tier) {
90
+ if (!tier) return cat;
91
+ return {
92
+ ...cat,
93
+ models: cat.models.filter((m) => m.tier === tier),
94
+ };
37
95
  }
@@ -25,6 +25,7 @@ export const ERROR_CODES = {
25
25
  INVALID_INPUTS: { exit: 1, message: "Inputs failed schema validation." },
26
26
  INPUTS_FILE_NOT_FOUND: { exit: 1, message: "Inputs file (passed via --inputs @path) not found." },
27
27
  INVALID_MODEL_ID: { exit: 1, message: "Server rejected the model id." },
28
+ CATEGORY_NOT_FOUND: { exit: 1, message: "Category not found in catalog." },
28
29
  MODEL_PRICING_NOT_CONFIGURED: { exit: 1, message: "This model is not yet priced on the gateway. Ask the operator to add it to skillboss-pricing.json." },
29
30
  INVALID_BODY: { exit: 1, message: "Server rejected the request body." },
30
31
  TOPUP_REQUIRED: { exit: 1, message: "Wallet top-up required. Choose an amount and rerun with --topup-amount <usdt>." },