@myapihq/cli 2.2.0 → 2.3.1

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.
@@ -47,6 +47,25 @@ async function defaultChatModel(apiKey, orgId) {
47
47
  }
48
48
  return chat.id;
49
49
  }
50
+ // Resolve the embed model from --model, else the live catalog. Unlike chat
51
+ // we don't silently pick a default when several are served — embedding
52
+ // vectors aren't cross-model compatible, so an ambiguous pick is a footgun.
53
+ // One served embed model → use it; none → catalog-truthful error (not a
54
+ // hardcoded "not available today"); many → make the caller choose.
55
+ async function resolveEmbedModel(apiKey, orgId, flags) {
56
+ if (typeof flags.model === 'string' && flags.model)
57
+ return flags.model;
58
+ const embeds = (await loadModels(apiKey, orgId)).filter(m => m.kind === 'embed');
59
+ if (embeds.length === 1)
60
+ return embeds[0].id;
61
+ if (embeds.length === 0) {
62
+ error('No embedding model is currently served — the catalog has none. Run "myapi llm models --kind embed" to check.');
63
+ }
64
+ else {
65
+ error(`Multiple embedding models available — pass --model <id>. Options: ${embeds.map(m => m.id).join(', ')}`);
66
+ }
67
+ throw new Error('unreachable'); // satisfy the type checker
68
+ }
50
69
  function readPromptArg(promptArg, flags) {
51
70
  if (typeof flags.file === 'string' && flags.file) {
52
71
  return fs.readFileSync(flags.file, 'utf-8');
@@ -140,10 +159,8 @@ async function complete(promptArg, flags) {
140
159
  }
141
160
  async function embed(inputArg, flags) {
142
161
  const config = requireConfig();
143
- const orgId = requireOrg(flags, config, 'myapi llm embed "<text>" --model <id> [--org <id>]');
144
- const model = typeof flags.model === 'string' ? flags.model : '';
145
- if (!model)
146
- error('Missing required flag: --model. Run "myapi llm models" to list embed models.');
162
+ const orgId = requireOrg(flags, config, 'myapi llm embed "<text>" [--model <id>] [--org <id>]');
163
+ const model = await resolveEmbedModel(config.api_key, orgId, flags);
147
164
  const text = readPromptArg(inputArg, flags);
148
165
  if (!text.trim())
149
166
  error('Empty input. Pass <text> as an argument, "-" to read stdin, or --file <path>.');
@@ -283,8 +300,7 @@ const SUBCOMMAND_USAGE = {
283
300
  [--stop <csv>] [--file <path>] [--org <id>] [--json]
284
301
 
285
302
  Raw chat completion against a self-hosted catalog model. Without --model,
286
- picks the first chat model from "myapi llm models" (today:
287
- Qwen/Qwen3-Coder-30B-A3B-Instruct).
303
+ picks the first chat model from "myapi llm models".
288
304
 
289
305
  Pass "-" as the prompt to read from stdin. The reply goes to stdout; a
290
306
  one-line usage footer (tokens + cost in cents) goes to stderr so it
@@ -293,10 +309,11 @@ const SUBCOMMAND_USAGE = {
293
309
  Examples:
294
310
  myapi llm complete "summarize: $(cat README.md)"
295
311
  cat draft.md | myapi llm complete - --system "You are an editor" --max-tokens 200`,
296
- embed: `myapi llm embed "<text>" --model <id> [--file <path>] [--org <id>] [--json]
312
+ embed: `myapi llm embed "<text>" [--model <id>] [--file <path>] [--org <id>] [--json]
297
313
 
298
- No embedding model is served on the raw catalog today — this returns
299
- EMBED_NOT_AVAILABLE until one is. Use a dedicated embedding API for now.`,
314
+ Embed text into a dense vector. --model is optional when the catalog serves
315
+ exactly one embedding model; otherwise pass one from "myapi llm models
316
+ --kind embed". Returns EMBED_NOT_AVAILABLE if no embedding model is served.`,
300
317
  models: `myapi llm models [--kind chat|embed] [--org <id>] [--json]
301
318
 
302
319
  Lists the live model catalog with per-1M-token pricing in cents.
@@ -358,7 +375,7 @@ Subcommands:
358
375
  classify Pick a label from a set
359
376
  complete Raw chat completion against a catalog model
360
377
  draft Write something (email | reply | message | …)
361
- embed Raw embeddings (no model served on raw today)
378
+ embed Raw embeddings against a catalog embed model
362
379
  extract Pull structured data conforming to a JSON Schema
363
380
  models List the live model catalog (id, kind, context, cents/1M)
364
381
  summarize Summarize text (brief | exec | bullet)
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: my-llm-api
3
- version: 1.1.0
3
+ version: 1.2.0
4
4
  description: >
5
5
  Two-surface LLM primitive. Raw chat completion against self-hosted
6
6
  open-source models (you pick the model), and objective verbs
@@ -14,7 +14,7 @@ checksum: sha256-pending
14
14
 
15
15
  A two-surface LLM gateway:
16
16
 
17
- - **Raw** — `complete` / `embed` / `models`. You pick a self-hosted catalog model (today: `Qwen/Qwen3-Coder-30B-A3B-Instruct`). Proprietary models are not callable here.
17
+ - **Raw** — `complete` / `embed` / `models`. You pick a self-hosted catalog model (run `myapi llm models` for the live list). Proprietary models are not callable here.
18
18
  - **Verbs** — `classify` / `extract` / `summarize` / `draft`. You ask for a task done; the model is implementation detail and is never named in the response.
19
19
 
20
20
  Pricing is in cents per 1M tokens at the actual upstream rate; cost is debited from your MyAPI balance.
@@ -33,7 +33,7 @@ Use this for workflow tasks — summarize a doc, classify an inbound email, extr
33
33
  ### Raw `complete` request
34
34
  ```json
35
35
  {
36
- "model": "Qwen/Qwen3-Coder-30B-A3B-Instruct",
36
+ "model": "<model-id>",
37
37
  "messages": [
38
38
  { "role": "system", "content": "You are a terse editor." },
39
39
  { "role": "user", "content": "Tighten this paragraph: ..." }
@@ -49,7 +49,7 @@ Roles: `system | user | assistant`. Multiple system messages collapse to one ins
49
49
  ### Raw `complete` response
50
50
  ```json
51
51
  {
52
- "model": "Qwen/Qwen3-Coder-30B-A3B-Instruct",
52
+ "model": "<model-id>",
53
53
  "content": "...assistant reply...",
54
54
  "finish_reason": "stop",
55
55
  "usage": { "input_tokens": 42, "output_tokens": 87, "cost_cents": 0.029 }
@@ -62,7 +62,7 @@ If `model` isn't in the self-hosted catalog the server returns `MODEL_NOT_IN_RAW
62
62
 
63
63
  ### Raw `embed`
64
64
 
65
- No embedding model is served on the raw catalog today; calls return `EMBED_NOT_AVAILABLE`. Use a dedicated embedding API for now.
65
+ Embed text into a dense vector. `--model` is optional when the catalog serves exactly one embed model; else pass one from `myapi llm models --kind embed`. Returns `EMBED_NOT_AVAILABLE` when none is served.
66
66
 
67
67
  ### Model catalog
68
68
  - Chat models: `id`, `kind: 'chat'`, `context_window`, `input_cost_per_1m_cents`, `output_cost_per_1m_cents`
@@ -99,11 +99,11 @@ client = OpenAI(
99
99
  api_key="hq_live_…",
100
100
  base_url="https://api.myapihq.com/llm/orgs/<org_id>/v1",
101
101
  )
102
- r = client.chat.completions.create(model="Qwen/Qwen3-Coder-30B-A3B-Instruct",
102
+ r = client.chat.completions.create(model="<model-id>",
103
103
  messages=[{"role":"user","content":"Hi"}])
104
104
  ```
105
105
 
106
- Use raw `complete` (MyAPI shape) for first-party integrations; use the OpenAI-compat path for compatibility with existing client code.
106
+ Use raw `complete` for first-party code; the compat path for existing OpenAI/LangChain tooling.
107
107
 
108
108
  <!-- llm:end -->
109
109
 
@@ -113,7 +113,7 @@ Use raw `complete` (MyAPI shape) for first-party integrations; use the OpenAI-co
113
113
  |---|---|
114
114
  | `myapi llm models [--kind chat\|embed] [--json]` | List the live model catalog with pricing (cents/1M) |
115
115
  | `myapi llm complete "<prompt>" [--model <id>] [--system "<s>"] [--max-tokens N] [--temperature 0..1] [--stop <csv>] [--file <path>] [--json]` | Raw chat completion; reply to stdout, usage to stderr. Defaults to the first chat model in the catalog |
116
- | `myapi llm embed "<text>" --model <id> [--json]` | Embed a string (no model served today returns EMBED_NOT_AVAILABLE) |
116
+ | `myapi llm embed "<text>" [--model <id>] [--json]` | Embed a string into a vector; `--model` optional when the catalog has one embed model |
117
117
  | `myapi llm classify "<input>" --labels <csv> [--multi] [--tier <t>] [--json]` | Pick a label from a set |
118
118
  | `myapi llm extract "<input>" --schema <path\|json> [--tier <t>] [--json]` | Pull structured data conforming to a JSON Schema |
119
119
  | `myapi llm summarize "<input>" [--style brief\|exec\|bullet] [--tier <t>] [--json]` | Summarize text |
@@ -132,9 +132,9 @@ myapi llm models --kind chat --json | jq '.models[].id'
132
132
  # Raw completion — picks the first chat model from the catalog
133
133
  myapi llm complete "Summarize in 12 words: $(cat README.md)"
134
134
 
135
- # Pin a specific model
135
+ # Pin a specific model (ids come from `myapi llm models`)
136
136
  myapi llm complete "Refactor this function: ..." \
137
- --model Qwen/Qwen3-Coder-30B-A3B-Instruct \
137
+ --model <model-id> \
138
138
  --system "You are a careful Go reviewer." \
139
139
  --max-tokens 600
140
140
 
@@ -163,5 +163,5 @@ INTENT=$(printf '%s' "$BODY" | myapi llm classify - \
163
163
 
164
164
  - **`draft` context safety.** `context` fields are quoted into the prompt verbatim; sensitive-named keys (`secret`, `api_key`, `password`, …) are NOT redacted. Two guards on top: (a) injection-defense strips `instructions`/`system`/`prompt`/`override` keys and surfaces them in `meta.warnings`; (b) output guardrail substring-scans fact values (length ≥ 4) in the response and lists matches in `meta.guardrails.facts_in_output` (signal, not redaction). Rule of thumb: never put credentials, PII, or internal metadata in `context` — pass identifiers, reference them indirectly.
165
165
  - **Self-hosted raw, server-picked verbs.** Raw runs on MyAPI's TPU; verbs route wherever the server picks.
166
- - **Cost + latency.** `usage.cost_cents` is authoritative — no markup. Qwen3-Coder-30B: 200–600 ms to first token, 1–3 s end-to-end.
166
+ - **Cost + latency.** `usage.cost_cents` is authoritative — no markup. Varies by tier: 200–600 ms to first token, 1–3 s end-to-end.
167
167
  - **Live catalog, no streaming, no BYOK.** Don't hard-code ids — `models` is truth (CLI auto-picks if `--model` omitted). Full reply only.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@myapihq/cli",
3
3
  "license": "Apache-2.0",
4
- "version": "2.2.0",
4
+ "version": "2.3.1",
5
5
  "description": "MyAPI command-line interface",
6
6
  "type": "module",
7
7
  "files": [
@@ -31,7 +31,7 @@
31
31
  "lint:skills:strict": "node scripts/copy-skills.js && node scripts/lint-skills.js --strict"
32
32
  },
33
33
  "dependencies": {
34
- "@myapihq/sdk": "^2.2.0"
34
+ "@myapihq/sdk": "^2.3.1"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^25.6.0",