@neta-art/cohub-cli 6.10.0 → 6.11.0
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/README.md +2 -0
- package/dist/commands/models.d.ts +26 -0
- package/dist/commands/models.js +103 -5
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -227,6 +227,8 @@ cohub search "query" --limit 20 --json
|
|
|
227
227
|
|
|
228
228
|
## Models and multimodal generation
|
|
229
229
|
|
|
230
|
+
`models ls` prints each LLM's per-million-token cost (`$3 /M input · $15 /M output`, with cache rates when declared) and hides models marked `hidden`. `models ls --model-type multimodal` prints each model's unit price (`$0.04 / image`, `$0.10–$0.50 / second`); use `--json` for the raw `pricing` object (`unit`, `amount` or `min`/`max`, optional `note`).
|
|
231
|
+
|
|
230
232
|
```bash
|
|
231
233
|
cohub models ls --json
|
|
232
234
|
cohub models ls --model-type multimodal --json
|
|
@@ -1,2 +1,28 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
+
import { type PublicGenerationDeclaration } from "@neta-art/cohub";
|
|
3
|
+
type MultimodalModelSummary = Pick<PublicGenerationDeclaration, "model" | "title" | "description" | "pricing">;
|
|
4
|
+
export declare function toMultimodalModelSummary(model: PublicGenerationDeclaration): MultimodalModelSummary;
|
|
5
|
+
export type GenerationModelPricing = NonNullable<PublicGenerationDeclaration["pricing"]>;
|
|
6
|
+
/**
|
|
7
|
+
* Render a display-only unit price, e.g. `$0.04 / image` or `$0.10–$0.50 / second`,
|
|
8
|
+
* optionally suffixed with a qualifier note.
|
|
9
|
+
*
|
|
10
|
+
* The raw `pricing` object is preserved in `--json` for machines; this string
|
|
11
|
+
* targets humans and agents reading the table output.
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatGenerationPrice(pricing: GenerationModelPricing): string;
|
|
14
|
+
/**
|
|
15
|
+
* Render per-million-token LLM cost, e.g. `$3 /M input · $15 /M output`,
|
|
16
|
+
* appending cache rates only when the model declares them.
|
|
17
|
+
*/
|
|
18
|
+
export declare function formatLlmModelCost(model: Record<string, unknown>): string;
|
|
19
|
+
/**
|
|
20
|
+
* Drop hidden models from LLM discovery, mirroring generation models and the web
|
|
21
|
+
* picker. Providers left with no visible model are removed so the catalog shape
|
|
22
|
+
* matches what is actually shown. Explicit `true` is the only hidden signal.
|
|
23
|
+
*/
|
|
24
|
+
export declare function filterHiddenLlmModels<T extends {
|
|
25
|
+
model: Record<string, unknown>;
|
|
26
|
+
}>(catalog: Record<string, T[]>): Record<string, T[]>;
|
|
2
27
|
export declare function registerModels(program: Command): void;
|
|
28
|
+
export {};
|
package/dist/commands/models.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, getAllowedGenerationModelIds, parseGenerationPolicyFromEnv, } from "@neta-art/cohub";
|
|
2
2
|
import { createClient } from "../client.js";
|
|
3
3
|
import { table, json as outJson, jsonRequested, error, handleHttp } from "../output.js";
|
|
4
|
-
function toMultimodalModelSummary(model) {
|
|
4
|
+
export function toMultimodalModelSummary(model) {
|
|
5
5
|
return {
|
|
6
6
|
model: model.model,
|
|
7
7
|
...(model.title ? { title: model.title } : {}),
|
|
8
8
|
...(model.description ? { description: model.description } : {}),
|
|
9
|
+
// Keep pricing structured for `--json`; the human table formats it separately.
|
|
10
|
+
...(model.pricing ? { pricing: model.pricing } : {}),
|
|
9
11
|
};
|
|
10
12
|
}
|
|
11
13
|
function printSection(title, lines) {
|
|
@@ -15,6 +17,88 @@ function printSection(title, lines) {
|
|
|
15
17
|
for (const line of lines)
|
|
16
18
|
console.log(` ${line}`);
|
|
17
19
|
}
|
|
20
|
+
const PRICE_UNIT_LABELS = {
|
|
21
|
+
image: "image",
|
|
22
|
+
second: "second",
|
|
23
|
+
request: "request",
|
|
24
|
+
"1m_tokens": "1M tokens",
|
|
25
|
+
};
|
|
26
|
+
/** USD amounts stay compact while preserving the precision the value actually needs. */
|
|
27
|
+
function formatUsdAmount(value) {
|
|
28
|
+
const magnitude = Math.abs(value);
|
|
29
|
+
const isWholeDollar = magnitude >= 1 && Number.isInteger(value);
|
|
30
|
+
const fractionDigits = value === 0 || isWholeDollar ? 0 : magnitude < 0.01 ? 4 : 2;
|
|
31
|
+
return `$${value.toLocaleString("en-US", { minimumFractionDigits: fractionDigits, maximumFractionDigits: fractionDigits })}`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Render a display-only unit price, e.g. `$0.04 / image` or `$0.10–$0.50 / second`,
|
|
35
|
+
* optionally suffixed with a qualifier note.
|
|
36
|
+
*
|
|
37
|
+
* The raw `pricing` object is preserved in `--json` for machines; this string
|
|
38
|
+
* targets humans and agents reading the table output.
|
|
39
|
+
*/
|
|
40
|
+
export function formatGenerationPrice(pricing) {
|
|
41
|
+
const unit = PRICE_UNIT_LABELS[pricing.unit];
|
|
42
|
+
const value = typeof pricing.amount === "number"
|
|
43
|
+
? formatUsdAmount(pricing.amount)
|
|
44
|
+
: pricing.min === pricing.max
|
|
45
|
+
? formatUsdAmount(pricing.min)
|
|
46
|
+
: `${formatUsdAmount(pricing.min)}\u2013${formatUsdAmount(pricing.max)}`;
|
|
47
|
+
const base = `${value} / ${unit}`;
|
|
48
|
+
return pricing.note ? `${base} \u00b7 ${pricing.note}` : base;
|
|
49
|
+
}
|
|
50
|
+
/** LLM catalog entries carry cost as an untyped `model.cost` bag; validate it before use. */
|
|
51
|
+
function readLlmModelCost(model) {
|
|
52
|
+
const cost = model.cost;
|
|
53
|
+
if (!cost || typeof cost !== "object")
|
|
54
|
+
return null;
|
|
55
|
+
const { input, output, cacheRead, cacheWrite } = cost;
|
|
56
|
+
if (typeof input !== "number" || typeof output !== "number")
|
|
57
|
+
return null;
|
|
58
|
+
return {
|
|
59
|
+
input,
|
|
60
|
+
output,
|
|
61
|
+
...(typeof cacheRead === "number" ? { cacheRead } : {}),
|
|
62
|
+
...(typeof cacheWrite === "number" ? { cacheWrite } : {}),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** Zero or absent per-token components carry no signal, so they are omitted. */
|
|
66
|
+
function formatLlmCostValue(value) {
|
|
67
|
+
return typeof value === "number" && Number.isFinite(value) && value !== 0 ? formatUsdAmount(value) : null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Render per-million-token LLM cost, e.g. `$3 /M input · $15 /M output`,
|
|
71
|
+
* appending cache rates only when the model declares them.
|
|
72
|
+
*/
|
|
73
|
+
export function formatLlmModelCost(model) {
|
|
74
|
+
const cost = readLlmModelCost(model);
|
|
75
|
+
if (!cost)
|
|
76
|
+
return "";
|
|
77
|
+
const parts = [
|
|
78
|
+
[cost.input, "input"],
|
|
79
|
+
[cost.output, "output"],
|
|
80
|
+
[cost.cacheRead, "cache read"],
|
|
81
|
+
[cost.cacheWrite, "cache write"],
|
|
82
|
+
];
|
|
83
|
+
return parts.flatMap(([value, label]) => {
|
|
84
|
+
const formatted = formatLlmCostValue(value);
|
|
85
|
+
return formatted ? [`${formatted} /M ${label}`] : [];
|
|
86
|
+
}).join(" \u00b7 ");
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Drop hidden models from LLM discovery, mirroring generation models and the web
|
|
90
|
+
* picker. Providers left with no visible model are removed so the catalog shape
|
|
91
|
+
* matches what is actually shown. Explicit `true` is the only hidden signal.
|
|
92
|
+
*/
|
|
93
|
+
export function filterHiddenLlmModels(catalog) {
|
|
94
|
+
const visible = {};
|
|
95
|
+
for (const [provider, entries] of Object.entries(catalog)) {
|
|
96
|
+
const models = entries.filter((entry) => entry.model.hidden !== true);
|
|
97
|
+
if (models.length > 0)
|
|
98
|
+
visible[provider] = models;
|
|
99
|
+
}
|
|
100
|
+
return visible;
|
|
101
|
+
}
|
|
18
102
|
function formatContentSpec(spec) {
|
|
19
103
|
const details = [];
|
|
20
104
|
const roles = spec.roles;
|
|
@@ -58,6 +142,8 @@ function printMultimodalModel(model) {
|
|
|
58
142
|
printSection("Model", [model.model]);
|
|
59
143
|
if (model.description)
|
|
60
144
|
printSection("Description", [model.description]);
|
|
145
|
+
if (model.pricing)
|
|
146
|
+
printSection("Pricing", [formatGenerationPrice(model.pricing)]);
|
|
61
147
|
printSection("Input", model.content.input.map(formatContentSpec));
|
|
62
148
|
const parameterLines = Object.entries(model.parameters ?? {}).flatMap(([name, spec]) => formatParameter(name, spec));
|
|
63
149
|
printSection("Parameters", parameterLines);
|
|
@@ -103,6 +189,11 @@ Examples:
|
|
|
103
189
|
table(models, [
|
|
104
190
|
{ key: "model", label: "Model" },
|
|
105
191
|
{ key: "title", label: "Title" },
|
|
192
|
+
{
|
|
193
|
+
key: "pricing",
|
|
194
|
+
label: "Price",
|
|
195
|
+
format: (value) => (value ? formatGenerationPrice(value) : ""),
|
|
196
|
+
},
|
|
106
197
|
{ key: "description", label: "Description" },
|
|
107
198
|
]);
|
|
108
199
|
return;
|
|
@@ -111,15 +202,22 @@ Examples:
|
|
|
111
202
|
return error("Invalid model type", "Use --model-type llm or --model-type multimodal");
|
|
112
203
|
}
|
|
113
204
|
const catalog = await client.models.list();
|
|
205
|
+
const visibleCatalog = filterHiddenLlmModels(catalog);
|
|
114
206
|
if (jsonRequested(opts))
|
|
115
|
-
return outJson(
|
|
116
|
-
|
|
117
|
-
|
|
207
|
+
return outJson(visibleCatalog);
|
|
208
|
+
if (Object.keys(visibleCatalog).length === 0)
|
|
209
|
+
return console.log(" (empty)");
|
|
210
|
+
for (const [provider, entries] of Object.entries(visibleCatalog)) {
|
|
118
211
|
console.log(`\n ${provider}`);
|
|
119
212
|
console.log(` ${"─".repeat(provider.length)}`);
|
|
120
|
-
table(entries
|
|
213
|
+
table(entries.map((entry) => ({
|
|
214
|
+
id: entry.id,
|
|
215
|
+
provider: entry.provider,
|
|
216
|
+
cost: formatLlmModelCost(entry.model),
|
|
217
|
+
})), [
|
|
121
218
|
{ key: "id", label: "ID" },
|
|
122
219
|
{ key: "provider", label: "Provider" },
|
|
220
|
+
{ key: "cost", label: "Cost" },
|
|
123
221
|
]);
|
|
124
222
|
}
|
|
125
223
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neta-art/cohub-cli",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.11.0",
|
|
4
4
|
"description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
"NOTICE"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@neta-art/generation": "^0.1.
|
|
18
|
+
"@neta-art/generation": "^0.1.27",
|
|
19
19
|
"commander": "^15.0.0",
|
|
20
20
|
"pixi.js": "^8.20.1",
|
|
21
21
|
"sharp": "^0.35.4",
|
|
22
|
-
"@neta-art/cohub": "8.
|
|
22
|
+
"@neta-art/cohub": "8.14.0"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|