Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .claude/skills/model-sync/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ There is no exemption list. A model the API does not return is not a ZeroGPU mod
| `Text Generation` | pricing, `chat --model`, the `chat` Models tables and routing sentences |
| every other task — `Summarization`, `Text Classification`, `Text Moderation`, `PII`, `Text Embedding`, and any new one | pricing only; a command calls it only when its `MODEL` constant already names it |

The endpoint commands — `responses`, `chat_completions`, `moderations`, `embeddings` — take the model from `-m` and hold no model list. That is deliberate: it is what keeps the agent plugins working across model changes without a CLI release. Never add a model list, route, or validation to them, and never delete them in [loop 3](#3-remove-what-is-gone); they have no model to lose. Model ids in their doc examples follow the normal rename and removal rules.

## Never invent

API-sourced facts only: id, task, `maxTokens`, input/output price, parameter count, use cases. Architecture details (`MoE`, `13B active`), language counts, and provider comparisons may be carried over from an existing notes cell while still true, or taken from `pricing.description` — never generated. Comparisons that follow from the payload's own prices ("its priciest") are allowed. Never invent a command, a flag, an example output, or a route.
Expand Down
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ The official command-line interface for [ZeroGPU](https://zerogpu.ai) — run fa
- [`extract_json`](#extract_json)
- [`extract_pii`](#extract_pii)
- [`redact_pii`](#redact_pii)
- [Endpoints](#endpoints)
- [`responses`](#responses)
- [`chat_completions`](#chat_completions)
- [`moderations`](#moderations)
- [`embeddings`](#embeddings)
- [Environment Variables](#environment-variables)
- [Output](#output)
- [Troubleshooting](#troubleshooting)
Expand Down Expand Up @@ -301,6 +306,63 @@ zerogpu redact_pii "Call Sarah at 415-555-0100 or email sarah@acme.com."

---

### Endpoints

Call a ZeroGPU API endpoint directly, with any model. These commands keep no model list: the model you pass is sent as-is, so a model the platform adds or renames works without a CLI update. They are the stable surface the ZeroGPU agent plugins build on.

Text comes from the positional argument, or from stdin when there isn't one, which keeps very large prompts off the command line.

| Option | Applies to | Description |
|---|---|---|
| `-m, --model <model>` | all | **Required.** Model id, sent exactly as given. |
| `-i, --instructions <text>` | `responses`, `chat_completions` | `responses`: the `instructions` field. `chat_completions`: a system message ahead of the text. |
| `--metadata <json>` | `responses`, `chat_completions` | JSON object sent as `metadata` — the per-model options such as `usecase`, `labels`, `schema`, `threshold`. |
| `--body <json>` | all | Extra top-level request fields, e.g. `'{"max_output_tokens":256}'`. Fields set by the other options take precedence. |
| `--raw` | `responses`, `chat_completions` | Print the full API response instead of only the model's text. |

`responses` and `chat_completions` print the model's text, pretty-printed when it is JSON. `moderations` and `embeddings` print the full response. Every call is recorded for `cost_savings`, like the task commands.

#### `responses`

POST to `/v1/responses`.

```bash
zerogpu responses "Email John Smith at john@acme.com." -m gliner-multi-pii-v1 \
--metadata '{"usecase":"redact","mask":"label"}'

# Long input from a file, on stdin
zerogpu responses -m gpt-oss-120b -i "Summarize this report." < report.txt
```

#### `chat_completions`

POST to `/v1/chat/completions`. Also available as `chat-completions`.

```bash
zerogpu chat_completions "Explique la mise en cache en une phrase." -m qwen3-30b-a3b-fp8

zerogpu chat_completions "The app uses Python 3.11 and PostgreSQL 15." -m gliner2-base-v1 \
--metadata '{"usecase":"ner","labels":["programming language","database"],"threshold":0.3}'
```

#### `moderations`

POST to `/v1/moderations`.

```bash
zerogpu moderations "Screen this comment before we publish it." -m zlm-v1-moderation-edge
```

#### `embeddings`

POST to `/v1/embeddings`.

```bash
zerogpu embeddings "ZeroGPU runs small models at the edge." -m all-minilm-l6-v2
```

---

## Environment Variables

| Variable | Purpose |
Expand Down
3 changes: 3 additions & 0 deletions docs/ADDING_COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

This guide explains how to add a new CLI command to the ZeroGPU CLI.

**First, check whether you need one.** The endpoint commands — `responses`, `chat_completions`, `moderations`, and `embeddings` — already reach every model on those endpoints: they send whatever `--model`, `--metadata`, and `--body` the caller gives. The Claude Code and OpenClaw plugins call them, so a new model needs no CLI change for the plugins to use it. Add a task command only when a dedicated, discoverable command is worth having for people using the CLI directly. Keep the endpoint commands free of model lists; that is what keeps the plugins independent of CLI releases.

## Layout

- `src/commands/` — one file per command, each exporting a `register<Name>Command(program)` function.
- `src/cli.ts` — wires every command into the root program.
- `src/lib/responses.ts` — shared `RESPONSES_ENDPOINT`, `ResponsesApiResponse`, and the `extractOutputText` / `extractReasoningText` helpers for `/v1/responses` calls.
- `src/lib/chatCompletions.ts` — the same for `/v1/chat/completions`, used by models the platform serves only there (currently `qwen3-30b-a3b-fp8`, `glm-5.2`, and `deepseek-v4-flash`), plus `toResponsesUsage` to normalize token counts for savings tracking.
- `src/lib/auth.ts` — `getApiKey()` for authenticated requests.
- `src/lib/request.ts` — plumbing for the endpoint commands: `requireApiKey`, `resolveInput` (argument or stdin), `parseJsonObject`, `postJson`, and the print helpers.

## Steps

Expand Down
165 changes: 165 additions & 0 deletions docs/DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ The CLI exposes the following commands:
| [`summarize`](#413-summarize) | Summarize text with `llama-3.1-8b-instruct-fast` |
| [`generate_followups`](#414-generate_followups) | Generate follow-up questions |
| [`classify_domain`](#415-classify_domain) | Domain-level IAB classification |
| [`responses`](#416-responses) | Call `/v1/responses` with any model |
| [`chat_completions`](#417-chat_completions) | Call `/v1/chat/completions` with any model |
| [`moderations`](#418-moderations) | Call `/v1/moderations` with any model |
| [`embeddings`](#419-embeddings) | Call `/v1/embeddings` with any model |

Commands 4.1–4.15 each wrap one task and pin its model. The endpoint commands, 4.16–4.19, take the model from `--model` and keep no model list, so a model the platform adds or renames works with them without a CLI release.

### Common exit codes
| Code | Meaning |
Expand Down Expand Up @@ -674,8 +680,167 @@ zerogpu classify_domain nytimes.com

---

### 4.16 `responses`

Call the Responses API with any model. The model id is sent exactly as given — the CLI does not check it against a list, so the API decides whether it exists.

**Synopsis**
```
zerogpu responses [text] -m <model> [-i <instructions>] [--metadata <json>] [--body <json>] [--raw]
```

**Parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `text` (positional) | string | optional | Input text. When omitted, read from stdin, with trailing newlines dropped. |
| `-m`, `--model <model>` | string | **yes** | Model id. |
| `-i`, `--instructions <instructions>` | string | optional | Sent as `instructions`. |
| `--metadata <json>` | JSON object | optional | Sent as `metadata` — the model's options, e.g. `{"usecase":"redact","mask":"label"}`. |
| `--body <json>` | JSON object | optional | Extra top-level request fields. `model`, `input`, `instructions`, and `metadata` from the other options take precedence. |
| `--raw` | boolean | optional | Print the full response instead of the output text. |

**Request body**
```jsonc
{ /* ...--body */ "model": "<--model>", "input": "<text>", "instructions": "<-i>", "metadata": { /* --metadata */ } }
```

**Example**
```bash
zerogpu responses "Email John Smith at john@acme.com about invoice 12345." \
-m gliner-multi-pii-v1 --metadata '{"usecase":"redact","mask":"label"}'

zerogpu responses -m zlm-v1-iab-classify-edge < article.txt
```

**Expected output**
The output text (the `output_text` part of the `message` item, which skips any reasoning item), pretty-printed when it parses as JSON. With `--raw`, the whole response as JSON.
```
Email [PERSON] at [EMAIL] about invoice 12345.
```

**Outcomes**

| Outcome | Exit |
|---|---|
| Success — output printed | `0` |
| Not signed in | `1` |
| `--model` missing | `1` — `error: required option '-m, --model <model>' not specified` |
| `--metadata` or `--body` is not a JSON object | `1` — `Invalid --metadata JSON: <message>` or `--metadata must be a JSON object.` |
| No text in the argument or on stdin | `1` — `No input text. Pass it as an argument or pipe it on stdin.` |
| Network error (fetch threw) | `1` — `Request failed: <message>` |
| HTTP non-2xx, including an unknown model | `1` — `Request failed with status <code>.` + body |
| Response missing output text (without `--raw`) | `1` — `Response did not contain any output text.` + raw JSON dump |

---

### 4.17 `chat_completions`

Call the Chat Completions API with any model. Alias: `chat-completions`.

**Synopsis**
```
zerogpu chat_completions [text] -m <model> [-i <instructions>] [--metadata <json>] [--body <json>] [--raw]
```

**Parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `text` (positional) | string | optional | Sent as the `user` message. When omitted, read from stdin, with trailing newlines dropped. |
| `-m`, `--model <model>` | string | **yes** | Model id. |
| `-i`, `--instructions <instructions>` | string | optional | Sent as a `system` message ahead of the user message. |
| `--metadata <json>` | JSON object | optional | Sent as `metadata`, e.g. `{"usecase":"ner","labels":["database"],"threshold":0.3}`. |
| `--body <json>` | JSON object | optional | Extra top-level request fields. `model`, `messages`, and `metadata` from the other options take precedence. |
| `--raw` | boolean | optional | Print the full response instead of the message content. |

**Request body**
```jsonc
{ /* ...--body */ "model": "<--model>", "messages": [{ "role": "system", "content": "<-i>" }, { "role": "user", "content": "<text>" }], "metadata": { /* --metadata */ } }
```

**Example**
```bash
zerogpu chat_completions "The application is built with Python 3.11 and uses PostgreSQL 15." \
-m gliner2-base-v1 \
--metadata '{"usecase":"ner","labels":["programming language","database"],"threshold":0.3}'
```

**Expected output**
`choices[0].message.content`, pretty-printed when it parses as JSON. The reasoning trace is not printed; use `--raw` to see it.
```json
{
"entities": {
"programming language": ["Python 3.11"],
"database": ["PostgreSQL 15"]
}
}
```

**Outcomes** — as for [`responses`](#416-responses), except a response with no message content exits `1` with `Response did not contain any message content.` + raw JSON dump.

---

### 4.18 `moderations`

Call the Moderations API with any model and print the response.

**Synopsis**
```
zerogpu moderations [text] -m <model> [--body <json>]
```

**Parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `text` (positional) | string | optional | Text to screen. When omitted, read from stdin. |
| `-m`, `--model <model>` | string | **yes** | Model id, e.g. `zlm-v1-moderation-edge`. |
| `--body <json>` | JSON object | optional | Extra top-level request fields. `model` and `input` take precedence. |

**Example**
```bash
zerogpu moderations "Screen this comment before we publish it." -m zlm-v1-moderation-edge
```

**Expected output** — the moderations response as JSON: `results[].flagged`, `results[].categories`, and `results[].category_scores`.

**Outcomes** — as for [`responses`](#416-responses); there is no content to extract, so a successful response is always printed.

---

### 4.19 `embeddings`

Call the Embeddings API with any model and print the response.

**Synopsis**
```
zerogpu embeddings [text] -m <model> [--body <json>]
```

**Parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `text` (positional) | string | optional | Text to embed. When omitted, read from stdin. |
| `-m`, `--model <model>` | string | **yes** | Model id, e.g. `all-minilm-l6-v2` or `bge-small-en-v1.5`. |
| `--body <json>` | JSON object | optional | Extra top-level request fields. `model` and `input` take precedence. |

**Example**
```bash
zerogpu embeddings "ZeroGPU runs small models at the edge." -m all-minilm-l6-v2
```

**Expected output** — the embeddings response as JSON: `data[].embedding` holds the vector and `usage` the input tokens.

**Outcomes** — as for [`moderations`](#418-moderations).

---

## 5. Network & API Contract

The endpoint commands (4.16–4.19) POST to the endpoint they are named for — `/v1/responses`, `/v1/chat/completions`, `/v1/moderations`, `/v1/embeddings` — with the headers below. The rest of this section describes the task commands.

All inference commands POST to:

```
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zerogpu-cli",
"version": "3.7.2",
"version": "3.8.0",
"description": "Command-line interface for ZeroGPU.",
"type": "module",
"bin": {
Expand Down
8 changes: 8 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@ import { fileURLToPath } from "node:url";
import { Command } from "commander";
import updateNotifier from "update-notifier";
import { registerChatCommand } from "./commands/chat.js";
import { registerChatCompletionsCommand } from "./commands/chatCompletions.js";
import { registerChatThinkingCommand } from "./commands/chatThinking.js";
import { registerClassifyDomainCommand } from "./commands/classifyDomain.js";
import { registerClassifyIabCommand } from "./commands/classifyIab.js";
import { registerClassifyIabEnrichedCommand } from "./commands/classifyIabEnriched.js";
import { registerClassifyStructuredCommand } from "./commands/classifyStructured.js";
import { registerClassifyZeroShotCommand } from "./commands/classifyZeroShot.js";
import { registerCostSavingsCommand } from "./commands/costSavings.js";
import { registerEmbeddingsCommand } from "./commands/embeddings.js";
import { registerExtractEntitiesCommand } from "./commands/extractEntities.js";
import { registerExtractJsonCommand } from "./commands/extractJson.js";
import { registerExtractPiiCommand } from "./commands/extractPii.js";
import { registerGenerateFollowupsCommand } from "./commands/generateFollowups.js";
import { registerLoginCommand } from "./commands/login.js";
import { registerModerationsCommand } from "./commands/moderations.js";
import { registerRedactPiiCommand } from "./commands/redactPii.js";
import { registerResponsesCommand } from "./commands/responses.js";
import { registerStatusCommand } from "./commands/status.js";
import { registerSummarizeCommand } from "./commands/summarize.js";

Expand Down Expand Up @@ -107,6 +111,10 @@ export function buildProgram(): Command {
registerSummarizeCommand(program);
registerChatCommand(program);
registerChatThinkingCommand(program);
registerResponsesCommand(program);
registerChatCompletionsCommand(program);
registerModerationsCommand(program);
registerEmbeddingsCommand(program);
registerCostSavingsCommand(program);

return program;
Expand Down
Loading