@dan-ai-studio/dshopencodego 0.1.43 → 0.1.45
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.en.md +48 -28
- package/README.md +47 -27
- package/lib/client.js +47 -1
- package/lib/index.js +288 -123
- package/lib/types/adapter.d.ts +26 -3
- package/lib/types/catalog/go-doc.d.ts +9 -5
- package/lib/types/catalog/index.d.ts +22 -7
- package/lib/types/catalog/service.d.ts +44 -1
- package/lib/types/config.d.ts +4 -3
- package/lib/types/conversion/replay.d.ts +30 -1
- package/lib/types/models.d.ts +14 -0
- package/lib/types/usage/meter.d.ts +89 -2
- package/package.json +20 -21
- package/lib/types/adapter.js +0 -384
- package/lib/types/catalog/constants.js +0 -18
- package/lib/types/catalog/contract.js +0 -147
- package/lib/types/catalog/gateway.js +0 -59
- package/lib/types/catalog/go-doc.js +0 -192
- package/lib/types/catalog/index.js +0 -377
- package/lib/types/catalog/json-response.js +0 -73
- package/lib/types/catalog/metadata.js +0 -278
- package/lib/types/catalog/protocol.js +0 -87
- package/lib/types/catalog/reading.js +0 -73
- package/lib/types/catalog/service.js +0 -45
- package/lib/types/config.js +0 -91
- package/lib/types/conversion/context.js +0 -212
- package/lib/types/conversion/index.js +0 -7
- package/lib/types/conversion/replay.js +0 -242
- package/lib/types/conversion/stream.js +0 -221
- package/lib/types/go-limits.js +0 -91
- package/lib/types/index.js +0 -198
- package/lib/types/models.js +0 -86
- package/lib/types/remotes.js +0 -29
- package/lib/types/session-header.js +0 -49
- package/lib/types/update/check.js +0 -138
- package/lib/types/update/contract.js +0 -64
- package/lib/types/update/index.js +0 -10
- package/lib/types/update/service.js +0 -39
- package/lib/types/update/version.js +0 -16
- package/lib/types/usage/contract.js +0 -106
- package/lib/types/usage/index.js +0 -8
- package/lib/types/usage/meter.js +0 -65
- package/lib/types/usage/service.js +0 -74
- package/lib/types/usage/windows.js +0 -84
package/README.en.md
CHANGED
|
@@ -27,11 +27,14 @@ Prerequisites: a DSH `0.2.0` release (`0.2.0-rc.1` or later; see "Compatibility"
|
|
|
27
27
|
dsh plugin --profile web add @dan-ai-studio/dshopencodego@<version>
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
**After installing, confirm it actually loaded** — `--dump-config` cannot prove that:
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
1. The startup output should carry the plugin's own registration line, `dshopencodego: route "opencode-go" registered as OpenCode Go` (with `enabled: false` it is `dshopencodego: disabled by configuration; …`). No line means no load — when the compatibility gate refuses the plugin, the same output stream carries `Plugin … is incompatible with dsh …`.
|
|
33
|
+
2. A profile without HMR reports `restart-required` (`restartReason: no-hmr`) after the install: the plugin takes effect only after you **restart DSH**.
|
|
34
|
+
3. Where the logs are: a failed startup writes the full diagnostic to `$DSH_HOME/logs/startup-<timestamp>-<uuid>.log` and prints that path; a failed `dsh plugin` run prints `dsh: plugin command failed; diagnostics: <path>` (a file under the profile's `.plugin-manager/logs/operation-*/pnpm.log`).
|
|
35
|
+
4. Whether the session header really goes out: `node tools/verify/live-check.mjs --check-only` is a **zero-cost** local pre-flight that prints the `x-opencode-session` and `x-session-affinity` this run would carry; dropping `--check-only` and adding `--allow-live` sends **one** minimal request instead (it costs money; rules in `tools/verify/README.md`), after which the script asserts and prints `OK x-opencode-session: <session id>`.
|
|
36
|
+
|
|
37
|
+
> `dsh --profile web --dump-config | grep dshopencodego` is **not** load evidence: a plugin row the gate denied still appears in the dump (`packages/boot/app-boot/README.md`). It only proves the row was written into the composition.
|
|
35
38
|
|
|
36
39
|
### Option 2: GitHub Release
|
|
37
40
|
|
|
@@ -83,27 +86,35 @@ Configuration lives in the profile's `cordis.patch.yml`:
|
|
|
83
86
|
config:
|
|
84
87
|
enabled: true # false withdraws the route only
|
|
85
88
|
apiKeyEnv: OPENCODE_GO_API_KEY # default
|
|
86
|
-
baseURL: https://opencode.ai/inference/go/v1 # default (OpenCode Go endpoint
|
|
87
|
-
# or
|
|
88
|
-
|
|
89
|
+
baseURL: https://opencode.ai/inference/go/v1 # default (OpenCode Go endpoint)
|
|
90
|
+
# http(s) only, and no query or fragment; writing ?x=1 is refused when the profile is saved
|
|
91
|
+
# or https://opencode.ai/inference/v1 (Console main endpoint, pay-as-you-go/free models)
|
|
92
|
+
refreshMinutes: 60 # live catalog TTL, 1 ~ 10080 (7 days)
|
|
89
93
|
streamIdleTimeoutMs: 300000 # stream idle timeout: longest silence between events (ms)
|
|
90
94
|
maxRequestImageBytes: 6291456 # whole-request image payload cap (after base64, bytes)
|
|
91
95
|
requestImagePixelBudget: 1440000 # total-pixel budget for one image
|
|
92
96
|
requestImageMaxBytes: 400000 # encoded-byte target for one image (before base64)
|
|
93
|
-
modelVisibility: # per-model switches
|
|
97
|
+
modelVisibility: # per-model switches; unlisted models follow the default rule (see "The model list in the settings page")
|
|
94
98
|
glm-5: true
|
|
95
99
|
modelLimits: # per-model capacity overrides
|
|
96
100
|
kimi-k3:
|
|
97
101
|
contextWindow: 262144
|
|
102
|
+
maxTokens: 32768 # optional: this model's output cap
|
|
103
|
+
some-legacy-model: null # a whole-entry null: both fields explicitly fall back to the catalog values
|
|
98
104
|
modelProtocols: # last resort: per-model protocol override
|
|
99
|
-
some-new-model: openai-responses
|
|
105
|
+
some-new-model: openai-responses # the only legal values are anthropic-messages / openai-completions / openai-responses
|
|
100
106
|
retryPolicy: # optional: this route's retry policy, executed by dsh-llm-retry
|
|
101
|
-
mode: normal
|
|
102
|
-
maxRetries: 2
|
|
107
|
+
mode: normal # or always (retry every failure; maxRetries is ignored)
|
|
108
|
+
maxRetries: 2 # default 5
|
|
109
|
+
retryableCodes: [RATE_LIMIT, TIMEOUT] # optional: retry only these codes (defaults RATE_LIMIT / SERVER / TIMEOUT / TRANSPORT / EMPTY_RESPONSE)
|
|
103
110
|
backoff:
|
|
104
|
-
initialDelayMs: 500
|
|
111
|
+
initialDelayMs: 500 # default 500
|
|
112
|
+
maxDelayMs: 10000 # default 10000, ceiling 2147483647
|
|
113
|
+
jitterRatio: 0.1 # default 0.1, range 0 ~ 1
|
|
105
114
|
```
|
|
106
115
|
|
|
116
|
+
**Validated on save**: those constraints are checked when the profile is **written**, not at the first request — `baseURL` must be http(s) with no query or fragment; `refreshMinutes` ranges from 1 to 10080; every `modelLimits` field may be `null`, meaning "select the catalog value explicitly" (`modelLimits.<id>: null` sends both fields back); `modelProtocols` accepts only those three values, and a wrong one makes that model appear as *configuration missing* (switch off, not selectable) rather than falling back to a guess; `retryPolicy` is a union (`normal` reads `maxRetries`/`retryableCodes`/`backoff`, `always` reads only `backoff`) and unknown keys are rejected.
|
|
117
|
+
|
|
107
118
|
The API key comes from the Harness credential store (reference name `OPENCODE_GO_API_KEY`), or from `export OPENCODE_GO_API_KEY=...`.
|
|
108
119
|
|
|
109
120
|
**Stream timeout**: `streamIdleTimeoutMs` bounds the **longest silence between two stream events**, not the total request duration — a long-thinking model that keeps producing events is never cut off. Default 5 minutes; the ceiling is `2147483647` (about 24.8 days). Exceeding it raises `TIMEOUT`, which is retryable.
|
|
@@ -154,45 +165,52 @@ The "OpenCode Go" section shows the live gateway catalog (the count follows the
|
|
|
154
165
|
- **Input modalities**: a separate line lists the input modes models.dev declares (text / image / audio / video / PDF). These are the **model's own metadata**, not what the Harness can forward — the Harness sends text and images natively; audio/video/PDF travel as attachments and tool reads. The line is absent when the document declares none.
|
|
155
166
|
- filtering (by name/id, enabled-only, show-deprecated — **deprecated hidden by default**), sorting (newest first, monthly requests, input price, context, name, enabled first); the list is the only view, one model per row.
|
|
156
167
|
|
|
157
|
-
**Default switches**:
|
|
168
|
+
**Default switches**: `modelVisibility` is **per model** — an id listed with `true`/`false` follows that explicit value, and **ids you do not list still follow the default rule**: the **top five models by published monthly request estimate** are enabled (deprecated, unconfigurable, and training-"contributor" models never qualify) and the rest stay off. That top-five set is recomputed unconditionally on every catalog refresh, whether or not any explicit entry exists. The Host picker and the settings page compute this from one rule, so they never disagree. **The consequence**: once you write any explicit entry, a model the gateway adds later is **not** automatically visible unless it lands in the top five — add `id: true` for it explicitly.
|
|
158
169
|
|
|
159
170
|
## Usage
|
|
160
171
|
|
|
161
172
|
- **Quota windows**: `GET /usage` reports 5-hour / weekly / monthly percentages with reset times and rate-limit flags; a failed read keeps the last reading and marks it stale, and an account or endpoint change invalidates it.
|
|
162
173
|
- **Actual spend**: the in-conversation button reports calls and tokens (input/output/cache-read) this process spent since it started, from the usage the provider returns on each call. The gateway publishes no per-model tokens, so the numbers are labelled for what they are instead of faking a per-model quota.
|
|
174
|
+
- **Estimated cost**: models.dev per-million-token rates multiplied by the tokens the provider returned — an **estimate, not a bill**. On the subscription endpoint (Go) it is indicative only; it is only reconcilable on the metered Console endpoint. The rate is captured per call, so a catalog refresh never reprices tokens already spent; when the catalog publishes no separate cache rate, cached tokens are estimated at the **input** rate (an upper bound). **A model with no published rate shows "unpriced" with its name — never `0`, which would read as free.** Very small amounts render as `<$0.0001`.
|
|
175
|
+
- **Reasoning tokens**: the thinking tokens a provider reports are a **subset of `outputTokens`**, so they are shown on their own line but **excluded from the total** (adding them would count the same tokens twice). A model that reports no breakdown hides the line — that means "no data", not `0`.
|
|
163
176
|
|
|
164
177
|
## Compatibility
|
|
165
178
|
|
|
166
|
-
**Supported**: DSH `0.2.0` releases (`0.2.0-rc.1` and later) and the `0.2.1` line.
|
|
179
|
+
**Supported**: DSH `0.2.0` releases (`0.2.0-rc.1` and later) and the `0.2.1` line. `engines.dsh` and every `@deepseek-ai/dsh-*` peer dependency declare `>=0.2.0-rc.1 <0.3.0-0`.
|
|
167
180
|
|
|
168
|
-
|
|
181
|
+
**Anything before `0.2.0` is no longer supported**: support for the `0.1.7` line and older was removed in v0.1.36 — the runtime seam across that range differs from the one this plugin is built against, and continuing to declare it would only let the gate admit an untested combination. (`0.1.8` and `0.1.9` were never published.)
|
|
169
182
|
|
|
170
|
-
**The mechanism (not a hard pin)**: before mounting a plugin, DSH reads its `package.json`
|
|
183
|
+
**The mechanism (not a hard pin)**: before mounting a plugin, DSH reads its `package.json` declarations and semver-checks the **running DSH version** against them (prereleases participate, i.e. `includePrerelease`). There are two levels, first match wins:
|
|
171
184
|
|
|
172
|
-
|
|
173
|
-
|
|
185
|
+
1. **`engines.dsh` takes precedence and decides alone**: when a plugin declares it, only that range is consulted and `peerDependencies` plays no part at all (DSH source `packages/boot/app-boot/src/plugin-compatibility.ts` returns as soon as it finds `engines.dsh`).
|
|
186
|
+
2. Only when a plugin declares **no** `engines.dsh` are the `@deepseek-ai/dsh` / `@deepseek-ai/dsh-*` peer ranges checked, one by one.
|
|
187
|
+
|
|
188
|
+
The verdict:
|
|
189
|
+
|
|
190
|
+
- satisfied → the plugin loads;
|
|
191
|
+
- not satisfied → **that plugin row is disabled with a printed reason** (`Plugin … is incompatible with dsh …`), leaving other plugins untouched;
|
|
174
192
|
- to take the risk anyway, grant an **exact-version exemption**: `dsh plugin --profile <profile> allow-version <pkg>@<version> --dsh-version <exact> --accept-risk` (applies only to that package and that exact runtime version); `dsh plugin --profile <profile> version-exemptions` lists what a profile holds.
|
|
175
193
|
|
|
176
|
-
|
|
194
|
+
**This plugin declares `engines.dsh`, so that is the range the gate enforces**; the peer ranges are a declaration for readers and package managers, and only take over the verdict if `engines.dsh` is removed. The upper bound is written `<0.3.0-0` rather than `<0.3.0` precisely because the check includes prereleases: `<0.3.0` would judge `0.3.0-rc.1` and its siblings **compatible** and let them load, while `<0.3.0-0` closes the whole `0.3.0` line. `@deepseek-ai/cordis` is declared separately as `4.0.4 || 4.0.5-alpha.1 || 4.0.5-alpha.2 || 4.0.5` — the runtimes used by DSH `0.2.0-rc.1`/`rc.2`, `0.2.1-alpha.1`/`alpha.2`, `0.2.1-alpha.3`–`alpha.5`, and `0.2.1-rc.2` respectively (`4.0.2`/`4.0.3` were dropped along with pre-`0.2.0` support).
|
|
177
195
|
|
|
178
|
-
**Outside the declared range**: everything before `0.2.0-rc.1`, and `0.3.0
|
|
196
|
+
**Outside the declared range**: everything before `0.2.0-rc.1`, and the whole `0.3.0` line (prereleases included), are refused. If the seam is compatible, widen or drop this plugin's **`engines.dsh`** in your own build — widening the peer range does nothing, since it takes no part in the verdict — or wait for a plugin release.
|
|
179
197
|
|
|
180
198
|
### Version-binding notes
|
|
181
199
|
|
|
182
200
|
| Binding layer | Declared | Enforced by |
|
|
183
201
|
| --- | --- | --- |
|
|
184
|
-
| `
|
|
185
|
-
| `@deepseek-ai/
|
|
186
|
-
| `
|
|
202
|
+
| `engines.dsh` | `>=0.2.0-rc.1 <0.3.0-0` | **DSH's gate at plugin load time; declaring it skips the peer verdict entirely** |
|
|
203
|
+
| `peerDependencies` (17 `@deepseek-ai/dsh-*` packages) | all `>=0.2.0-rc.1 <0.3.0-0` | takes over only when `engines.dsh` is absent; this plugin declares it, so the peers take no part today |
|
|
204
|
+
| `@deepseek-ai/cordis` | `4.0.4 \|\| 4.0.5-alpha.1 \|\| 4.0.5-alpha.2 \|\| 4.0.5` | the same peer channel |
|
|
187
205
|
| `engines.node` | `^22.19.0 \|\| >=24.0.0` | the package manager |
|
|
188
206
|
| bundled `@earendil-works/pi-ai` | `~1.3.0` (within 1.3.x), **compiled into `lib/`** (devDependency) | an independent coupling: a change in pi-ai's request construction changes this plugin's wire behaviour, and its opencode-go builtin catalog is this plugin's fallback data. Bundling leaves the plugin with **no runtime dependencies** for an installer to resolve |
|
|
189
207
|
| bundled `@deepseek-ai/schemastery` | `~3.18.5-alpha.2`, **compiled into `lib/`** (devDependency) | a build-time dependency |
|
|
190
208
|
|
|
191
209
|
Things to keep in mind when maintaining this:
|
|
192
210
|
|
|
193
|
-
- **Required versus optional peers**: six of the eighteen are marked `optional` (`dsh-api-remotes`, `dsh-client-locale`, `dsh-client-store`, `dsh-client-ui-model-selection`, `dsh-client-ui-settings`, `dsh-client-ui-slots`). The twelve
|
|
194
|
-
- **Declared range ≠ verified range**: the declaration is `>=0.2.0-rc.1 <0.3.0`. What was actually verified here, in order, is `0.2.0-rc.1` (the dev-dependency baseline: gate verdict + typecheck + full suite), `0.2.0-rc.2` (the same plus a `prepareCall` generation freeze), `0.2.1-alpha.3` (the first target release), `0.2.1-alpha.4` (T-24), `0.2.1-alpha.5` (T-26), and `0.2.1-rc.2` (T-31, where the runtime moved to cordis `4.0.5`; the upstream contract layer had zero source changes). Other versions inside the range were not verified one by one.
|
|
195
|
-
- **The upper bound is a tracking line**: `<0.3.0` means that the moment DSH ships `0.3.0
|
|
211
|
+
- **Required versus optional peers**: six of the eighteen are marked `optional` (`dsh-api-remotes`, `dsh-client-locale`, `dsh-client-store`, `dsh-client-ui-model-selection`, `dsh-client-ui-settings`, `dsh-client-ui-slots`). The other twelve are required at runtime: `cordis`, `dsh-attachment`, `dsh-brand`, `dsh-client-ui-conversation`, `dsh-client-ui-renderer`, `dsh-credentials`, `dsh-fs`, `dsh-launch-environment`, `dsh-llm`, `dsh-timeout`, `dsh-typert-protocol`, and `dsh-typert-registry`. None of the eighteen takes part in the load verdict today (see above: `engines.dsh` decides alone); their value is declaring the dependency for the package manager and for readers. `dsh-llm` is the heaviest coupling — more than twenty imports across the source.
|
|
212
|
+
- **Declared range ≠ verified range**: the declaration is `>=0.2.0-rc.1 <0.3.0-0`. What was actually verified here, in order, is `0.2.0-rc.1` (the dev-dependency baseline: gate verdict + typecheck + full suite), `0.2.0-rc.2` (the same plus a `prepareCall` generation freeze), `0.2.1-alpha.3` (the first target release), `0.2.1-alpha.4` (T-24), `0.2.1-alpha.5` (T-26), and `0.2.1-rc.2` (T-31, where the runtime moved to cordis `4.0.5`; the upstream contract layer had zero source changes). Other versions inside the range were not verified one by one.
|
|
213
|
+
- **The upper bound is a tracking line**: `<0.3.0-0` means that the moment DSH ships `0.3.0-0` (the first prerelease on the `0.3.0` line), this plugin must ship a new release in the same window or every user loses the plugin. `tests/peer-coverage.spec.ts` asserts both ends — the lower bound accepts the installed version, the upper bound rejects `0.3.0-0` — so a drift here fails locally instead of failing on a user's machine. After changing a range, `npm test` is the verification (local mock gateway, no network, no tokens) — ranges follow what the interfaces declare, and are never "measured" with live probes.
|
|
196
214
|
- **Peer coverage is now guarded both ways**: `dsh-typert-registry`, `dsh-client-ui-conversation`, and `dsh-client-ui-renderer` were imported by the source but absent from `peerDependencies`, invisible to the gate; they are declared now, and `tests/peer-coverage.spec.ts` asserts both directions — anything the source imports must be declared, and anything declared but unreached must be removed (`dsh-settings` went that way: the settings form is actually provided by `dsh-client-ui-settings`).
|
|
197
215
|
- **Exemptions are per profile**: only a profile that ran `dsh plugin allow-version` has one, and no record means the plugin must stay inside the declared range. `dsh plugin version-exemptions` lists a profile's exemptions.
|
|
198
216
|
|
|
@@ -217,11 +235,13 @@ Things to keep in mind when maintaining this:
|
|
|
217
235
|
npm ci
|
|
218
236
|
npm run lint # oxlint: static checks (the release workflow runs this too, and fails on it)
|
|
219
237
|
npm run typecheck # both halves: host and client
|
|
238
|
+
npm run build # build first: emits lib/index.js and lib/client.js
|
|
220
239
|
npm test # contract tests against a real local HTTP mock gateway
|
|
221
|
-
npm run build # emits lib/index.js and lib/client.js
|
|
222
240
|
```
|
|
223
241
|
|
|
224
|
-
|
|
242
|
+
**Order matters**: `npm run build` must come before `npm test` — `tests/pack-contents.spec.ts` walks every `exports` target, and whether `lib/` exists decides whether that check runs or is silently skipped (it does not fail, it just stops protecting you). `package.json` no longer carries `prepare`, so `npm ci` will not build for you; all three CI workflows run `npm run build` explicitly.
|
|
243
|
+
|
|
244
|
+
The suite covers configuration validation, the protocol ladder, gateway and online metadata, the catalog projection, the adapter on the wire (all three protocols), Remote contracts and services, session-header invariants, usage windows and the meter, the client controller and usage pill, both halves' mounting and registration, the error mapping for stream idle timeouts and aborts, the exit-code contract of the verification scripts, and several **structural invariants** (inject declarations match the source's reads, peer coverage holds both ways, the READMEs match the manifest). That is **43 test files** — a number `tests/readme-consistency.spec.ts` checks against the `tests/` directory, so it cannot drift.
|
|
225
245
|
|
|
226
246
|
## Releasing (maintainers)
|
|
227
247
|
|
package/README.md
CHANGED
|
@@ -27,11 +27,14 @@
|
|
|
27
27
|
dsh plugin --profile web add @dan-ai-studio/dshopencodego@<版本>
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
**安装后确认真实加载**——`--dump-config` 证明不了这一点:
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
1. 启动输出里应出现插件自己的注册日志 `dshopencodego: route "opencode-go" registered as OpenCode Go`(`enabled: false` 时是 `dshopencodego: disabled by configuration; …`)。没有它就没加载——被版本门禁拒绝时,同一条输出流里会打印 `Plugin … is incompatible with dsh …`。
|
|
33
|
+
2. 没有 HMR 的 profile 安装后会回报 `restart-required`(`restartReason: no-hmr`):**重启 DSH** 后插件才生效。
|
|
34
|
+
3. 日志位置:启动失败会把完整诊断写到 `$DSH_HOME/logs/startup-<时间戳>-<uuid>.log` 并打印该路径;`dsh plugin` 失败会打印 `dsh: plugin command failed; diagnostics: <路径>`(profile 下的 `.plugin-manager/logs/operation-*/pnpm.log`)。
|
|
35
|
+
4. 会话头是否真的发出去了:`node tools/verify/live-check.mjs --check-only` 是**零成本**本地预检,会打印本次将会携带的 `x-opencode-session` 与 `x-session-affinity`;去掉 `--check-only`、加 `--allow-live` 才会真发**一次**最小请求(花钱,规则见 `tools/verify/README.md`),脚本随后断言并打印 `OK x-opencode-session: <会话 id>`。
|
|
36
|
+
|
|
37
|
+
> `dsh --profile web --dump-config | grep dshopencodego` **不构成加载证据**:被门禁拒绝的插件行同样会出现在 dump 里(`packages/boot/app-boot/README.md`)。它只能证明"这一行被写进了组合"。
|
|
35
38
|
|
|
36
39
|
### 方式二:GitHub Release
|
|
37
40
|
|
|
@@ -83,27 +86,35 @@ DSH 桌面端是 Electron 壳 + 完整的 dsh Web 应用,本插件对它同样
|
|
|
83
86
|
config:
|
|
84
87
|
enabled: true # false 仅撤下路由,插件保持挂载
|
|
85
88
|
apiKeyEnv: OPENCODE_GO_API_KEY # 默认值
|
|
86
|
-
baseURL: https://opencode.ai/inference/go/v1 # 默认值(OpenCode Go
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
+
baseURL: https://opencode.ai/inference/go/v1 # 默认值(OpenCode Go 端点)
|
|
90
|
+
# 只能是不带 query / fragment 的 http(s) 地址;写 ?x=1 会在保存配置时被拒
|
|
91
|
+
# 也可指定为 https://opencode.ai/inference/v1(Console 主端点,含按量/免费模型)
|
|
92
|
+
refreshMinutes: 60 # 目录缓存时长,1 ~ 10080(7 天)
|
|
89
93
|
streamIdleTimeoutMs: 300000 # 流空闲超时:两次流事件之间的最长静默(毫秒)
|
|
90
94
|
maxRequestImageBytes: 6291456 # 整个请求的图片载荷上限(base64 之后,字节)
|
|
91
95
|
requestImagePixelBudget: 1440000 # 单张图的总像素预算
|
|
92
96
|
requestImageMaxBytes: 400000 # 单张图编码前的字节目标(base64 之前)
|
|
93
|
-
modelVisibility: #
|
|
97
|
+
modelVisibility: # 逐模型开关;未写到的模型走默认规则(见「设置页里的模型列表」)
|
|
94
98
|
glm-5: true
|
|
95
99
|
modelLimits: # 逐模型容量覆盖
|
|
96
100
|
kimi-k3:
|
|
97
101
|
contextWindow: 262144
|
|
102
|
+
maxTokens: 32768 # 可选:该模型的输出上限
|
|
103
|
+
some-legacy-model: null # 整条 null:该模型两个字段都显式回退到目录值
|
|
98
104
|
modelProtocols: # 最后兜底:逐模型协议覆盖
|
|
99
|
-
some-new-model: openai-responses
|
|
105
|
+
some-new-model: openai-responses # 合法值只有 anthropic-messages / openai-completions / openai-responses
|
|
100
106
|
retryPolicy: # 可选:本路由的重试策略(由 dsh-llm-retry 执行)
|
|
101
|
-
mode: normal
|
|
102
|
-
maxRetries: 2
|
|
107
|
+
mode: normal # 也可用 always(对所有失败重试,maxRetries 被忽略)
|
|
108
|
+
maxRetries: 2 # 默认 5
|
|
109
|
+
retryableCodes: [RATE_LIMIT, TIMEOUT] # 可选:只对这些错误码重试(默认 RATE_LIMIT / SERVER / TIMEOUT / TRANSPORT / EMPTY_RESPONSE)
|
|
103
110
|
backoff:
|
|
104
|
-
initialDelayMs: 500
|
|
111
|
+
initialDelayMs: 500 # 默认 500
|
|
112
|
+
maxDelayMs: 10000 # 默认 10000,上限 2147483647
|
|
113
|
+
jitterRatio: 0.1 # 默认 0.1,取值 0 ~ 1
|
|
105
114
|
```
|
|
106
115
|
|
|
116
|
+
**保存即校验**:上面这些约束在**写入 profile 时**就检查,不会拖到第一次请求——`baseURL` 必须是 http(s) 且不带 query/fragment;`refreshMinutes` 取值 1 ~ 10080;`modelLimits` 的每个字段都可以写 `null`,语义是"显式选回目录值"(`modelLimits.<模型>: null` 表示两个字段一起回退);`modelProtocols` 只接受那三个合法值,写错会让该模型以"配置缺失"出现(开关关闭、不可选),而不是回退到猜测;`retryPolicy` 是联合类型(`normal` 读 `maxRetries`/`retryableCodes`/`backoff`,`always` 只读 `backoff`),未知字段会被拒绝。
|
|
117
|
+
|
|
107
118
|
API Key 通过 Harness 凭证库提供(引用名 `OPENCODE_GO_API_KEY`),也可以直接 `export OPENCODE_GO_API_KEY=...`。
|
|
108
119
|
|
|
109
120
|
**流超时**:`streamIdleTimeoutMs` 约束的是**两次流事件之间的最长静默**,不是请求总时长——长思考模型只要持续产出事件就不会被误判。默认 5 分钟,上限为 `2147483647`(约 24.8 天)。超时抛出 `TIMEOUT`(可重试类)。
|
|
@@ -154,45 +165,52 @@ API Key 通过 Harness 凭证库提供(引用名 `OPENCODE_GO_API_KEY`),
|
|
|
154
165
|
- **输入模态**:单独一行列出 models.dev 声明的输入方式(文本 / 图像 / 音频 / 视频 / PDF)。这是**模型自身的元数据**,不等于 DSH 能直传:DSH 的模型输入只原生支持文本与图像,音视频/PDF 走附件与工具读取路径;文档未声明时该行不显示。
|
|
155
166
|
- 筛选(按名称/ID、仅已启用、显示已弃用——**默认隐藏已弃用**)、排序(新发布优先 / 每月预估次数 / 输入单价 / 上下文 / 名称 / 已启用优先);列表是唯一视图,逐模型纵向排列。
|
|
156
167
|
|
|
157
|
-
|
|
168
|
+
**默认开关规则**:`modelVisibility` 是**逐模型**的——写了 `id: true`/`id: false` 的模型按显式值走,**没写到的模型仍走默认规则**:默认启用**每月预估次数最高的 5 个**(跳过已弃用、无法配置、以及"贡献者版"这类以数据换折扣的模型),其余关闭。这份 top5 名单每轮目录刷新都会无条件重算,与是否写过显式条目无关。同一规则同时作用于模型选择器与设置页,两处不会出现不同答案。**后果**:一旦写过显式配置,网关新增的模型若不在 top5 内,下次刷新**不会自动可见**,要显式加一条 `id: true`。
|
|
158
169
|
|
|
159
170
|
## 用量
|
|
160
171
|
|
|
161
172
|
- **额度窗口**:`GET /usage` 返回 5 小时 / 本周 / 本月三个百分比,含重置时间与限流状态;失败时保留上次读数并标注陈旧,账号或端点变化时不保留。
|
|
162
173
|
- **实际消耗**:会话内按钮显示本进程自启动以来的调用次数与 token(输入/输出/缓存读),数据来自 provider 每次调用回传的 usage。网关不公布分模型 token,因此这里如实标注口径,不伪造分模型配额。
|
|
174
|
+
- **估算花费**:按 models.dev 的每百万 token 单价乘 provider 回传的 token 估算,**不是账单**——订阅端点(Go)下仅供参考,只有按量端点(Console)才有对账意义。单价随每次调用一起记录,目录刷新不会用新价重算已经花掉的 token;目录未单独给出缓存单价时,缓存部分**按输入价估算**(上界)。**没有单价的模型显示「未定价」并列出模型名,绝不以 0 冒充免费**;极小金额显示为 `<$0.0001`。
|
|
175
|
+
- **推理 token**:provider 报告的思考 token 是 `outputTokens` 的**子集**,因此单独显示但**不计入总量**(否则同一批 token 会被算两次)。未上报该项的模型不显示该行——那是「没有数据」,不是 0。
|
|
163
176
|
|
|
164
177
|
## 兼容性
|
|
165
178
|
|
|
166
|
-
**支持**:DSH `0.2.0` 系列(`0.2.0-rc.1` 及之后的正式版)与 `0.2.1`
|
|
179
|
+
**支持**:DSH `0.2.0` 系列(`0.2.0-rc.1` 及之后的正式版)与 `0.2.1` 系列。插件在 `engines.dsh` 与每个 `@deepseek-ai/dsh-*` 的 `peerDependencies` 上声明 `>=0.2.0-rc.1 <0.3.0-0`。
|
|
167
180
|
|
|
168
181
|
**不再支持 `0.2.0` 之前**:`0.1.7` 系列及更早的版本自 v0.1.36 起被移除支持——那段范围内的运行时接口与本插件当前依赖的接口已经不同,继续声明只会让门禁放行一个未经验证的组合。`0.1.8`、`0.1.9` 从未发布。
|
|
169
182
|
|
|
170
|
-
**机制(不是写死)**:DSH 在挂载插件前会读取插件 `package.json`
|
|
183
|
+
**机制(不是写死)**:DSH 在挂载插件前会读取插件 `package.json` 的声明,对**运行时的 DSH 版本**做 semver 判定(含预发布,即 `includePrerelease`)。判定分两级,先命中先赢:
|
|
171
184
|
|
|
172
|
-
-
|
|
173
|
-
|
|
185
|
+
1. **`engines.dsh` 优先且独占**:插件只要声明了它,就**只**由它决定,`peerDependencies` 完全不参与判定(DSH 源码 `packages/boot/app-boot/src/plugin-compatibility.ts`:命中 `engines.dsh` 即返回)。
|
|
186
|
+
2. 插件**没有**声明 `engines.dsh` 时,才逐个检查 `@deepseek-ai/dsh` / `@deepseek-ai/dsh-*` 的 peer 范围。
|
|
187
|
+
|
|
188
|
+
判定结果:
|
|
189
|
+
|
|
190
|
+
- 满足 → 正常加载;
|
|
191
|
+
- 不满足 → **该插件行被禁用并打印原因**(`Plugin … is incompatible with dsh …`),其他插件不受影响;
|
|
174
192
|
- 需要冒险时可用**精确版本豁免**:`dsh plugin --profile <profile> allow-version <包>@<版本> --dsh-version <精确版本> --accept-risk`(只对该包与该精确版本生效);`dsh plugin --profile <profile> version-exemptions` 可列出当前 profile 的豁免。
|
|
175
193
|
|
|
176
|
-
`
|
|
194
|
+
**本插件声明了 `engines.dsh`,所以生效的门禁就是它**;peer 范围是给读者与包管理器的声明,只在 `engines.dsh` 被删除之后才会接管判定。上界写成 `<0.3.0-0` 而不是 `<0.3.0`,正是因为判定含预发布:`<0.3.0` 会把 `0.3.0-rc.1` 这类**预发布版判为兼容**并放行,`<0.3.0-0` 才真正封住整条 `0.3.0` 线。`@deepseek-ai/cordis` 单独声明为 `4.0.4 || 4.0.5-alpha.1 || 4.0.5-alpha.2 || 4.0.5`——依次是 DSH `0.2.0-rc.1`/`rc.2`、`0.2.1-alpha.1`/`alpha.2`、`0.2.1-alpha.3`–`alpha.5`、以及 `0.2.1-rc.2` 所用的运行时(`4.0.2`/`4.0.3` 随 `0.2.0` 之前的支持一并移除)。
|
|
177
195
|
|
|
178
|
-
**声明范围之外**:`0.2.0-rc.1`
|
|
196
|
+
**声明范围之外**:`0.2.0-rc.1` 之前的所有版本、以及整条 `0.3.0` 线(含预发布)都会被拒绝加载。如果 DSH 侧接口兼容,可自行放宽或删除该插件的 **`engines.dsh`** 后重新构建(改 peer 范围是无效的——它不参与判定);否则请等插件跟进发版。
|
|
179
197
|
|
|
180
198
|
### 版本绑定的注意事项
|
|
181
199
|
|
|
182
200
|
| 绑定层 | 声明 | 谁在把关 |
|
|
183
201
|
| --- | --- | --- |
|
|
184
|
-
| `
|
|
185
|
-
| `@deepseek-ai/
|
|
186
|
-
| `
|
|
202
|
+
| `engines.dsh` | `>=0.2.0-rc.1 <0.3.0-0` | **DSH 加载时的真门禁;声明了它,peer 判定被整体跳过** |
|
|
203
|
+
| `peerDependencies`(17 个 `@deepseek-ai/dsh-*`) | 均为 `>=0.2.0-rc.1 <0.3.0-0` | 仅当 `engines.dsh` 缺席时才接管;本插件声明了它,故当前不参与判定 |
|
|
204
|
+
| `@deepseek-ai/cordis` | `4.0.4 \|\| 4.0.5-alpha.1 \|\| 4.0.5-alpha.2 \|\| 4.0.5` | 同上(peer 通道) |
|
|
187
205
|
| `engines.node` | `^22.19.0 \|\| >=24.0.0` | 包管理器 |
|
|
188
206
|
| 已打包依赖 `@earendil-works/pi-ai` | `~1.3.0`(1.3.x 内),**打进产物**(devDependency) | 独立耦合:pi-ai 改请求构造即影响本插件的线上行为;其 opencode-go 内置目录同时是本插件的打底数据。打包后本插件**没有任何运行时依赖**,安装者不必再解析它 |
|
|
189
207
|
| 已打包依赖 `@deepseek-ai/schemastery` | `~3.18.5-alpha.2`,**打进产物**(devDependency) | 构建期依赖 |
|
|
190
208
|
|
|
191
209
|
维护时需要注意:
|
|
192
210
|
|
|
193
|
-
- **必选与可选之分**:18 个 peer(17 个 `dsh-*` 加框架 `cordis`)中 6 个标了 `optional`(`dsh-api-remotes`、`dsh-client-locale`、`dsh-client-store`、`dsh-client-ui-model-selection`、`dsh-client-ui-settings`、`dsh-client-ui-slots
|
|
194
|
-
- **名义范围 ≠ 实测范围**:声明范围是 `>=0.2.0-rc.1 <0.3.0`,本项目实际验证过的基线依次是 `0.2.0-rc.1`(开发依赖基线:门禁判定 + typecheck + 全量用例)、`0.2.0-rc.2`(同上 + `prepareCall` 代次冻结)、`0.2.1-alpha.3`(首个目标版本)、`0.2.1-alpha.4`(T-24)、`0.2.1-alpha.5`(T-26)与 `0.2.1-rc.2`(T-31,运行时升到 cordis `4.0.5`;上游契约层源码零变更)。范围内的其他版本没有逐个验证过。
|
|
195
|
-
- **上界就是跟版线**:`<0.3.0` 意味着 DSH 一旦发布 `0.3.0
|
|
211
|
+
- **必选与可选之分**:18 个 peer(17 个 `dsh-*` 加框架 `cordis`)中 6 个标了 `optional`(`dsh-api-remotes`、`dsh-client-locale`、`dsh-client-store`、`dsh-client-ui-model-selection`、`dsh-client-ui-settings`、`dsh-client-ui-slots`);其余 12 个是运行时必需的——`cordis`、`dsh-attachment`、`dsh-brand`、`dsh-client-ui-conversation`、`dsh-client-ui-renderer`、`dsh-credentials`、`dsh-fs`、`dsh-launch-environment`、`dsh-llm`、`dsh-timeout`、`dsh-typert-protocol`、`dsh-typert-registry`。这 18 个当前都**不参与加载判定**(见上:`engines.dsh` 单独决定),它们的价值在于声明依赖并供包管理器与读者使用。其中 `dsh-llm` 是最重的一处耦合(源码 import 二十余处)。
|
|
212
|
+
- **名义范围 ≠ 实测范围**:声明范围是 `>=0.2.0-rc.1 <0.3.0-0`,本项目实际验证过的基线依次是 `0.2.0-rc.1`(开发依赖基线:门禁判定 + typecheck + 全量用例)、`0.2.0-rc.2`(同上 + `prepareCall` 代次冻结)、`0.2.1-alpha.3`(首个目标版本)、`0.2.1-alpha.4`(T-24)、`0.2.1-alpha.5`(T-26)与 `0.2.1-rc.2`(T-31,运行时升到 cordis `4.0.5`;上游契约层源码零变更)。范围内的其他版本没有逐个验证过。
|
|
213
|
+
- **上界就是跟版线**:`<0.3.0-0` 意味着 DSH 一旦发布 `0.3.0-0`(`0.3.0` 线的第一个预发布版),本插件必须同批发新版,否则所有用户加载失败。`tests/peer-coverage.spec.ts` 会同时断言下界接受已安装版本、上界拒绝 `0.3.0-0`,所以这条线漂移会在本地测试里先失败,而不是等用户加载失败。调整范围后用 `npm test` 验证即可(本地 mock 网关,零网络零 token)——范围以接口声明为准,不要用线上探测来"测"出一个范围。
|
|
196
214
|
- **peer 覆盖已双向守住**:`dsh-typert-registry`、`dsh-client-ui-conversation`、`dsh-client-ui-renderer` 曾被源码 import 却未声明、门禁管不到,现已补进 `peerDependencies`;`tests/peer-coverage.spec.ts` 双向断言——源码用到的必须声明,声明了却没人用的必须删(`dsh-settings` 就这样被移除:设置表单实际由 `dsh-client-ui-settings` 提供)。
|
|
197
215
|
- **豁免是按 profile 记的**:只有显式执行过 `dsh plugin allow-version` 的 profile 才享有豁免;没有记录就代表必须落在声明范围内。用 `dsh plugin version-exemptions` 可查看当前 profile 的豁免。
|
|
198
216
|
|
|
@@ -217,11 +235,13 @@ API Key 通过 Harness 凭证库提供(引用名 `OPENCODE_GO_API_KEY`),
|
|
|
217
235
|
npm ci
|
|
218
236
|
npm run lint # oxlint:静态检查(发布流程也会跑,失败即阻断)
|
|
219
237
|
npm run typecheck # host 与 client 两半
|
|
238
|
+
npm run build # 先构建:产出 lib/index.js 与 lib/client.js
|
|
220
239
|
npm test # 契约测试:真实 HTTP mock 网关,断言请求头/体、目录阶梯、用量
|
|
221
|
-
npm run build # 产出 lib/index.js 与 lib/client.js
|
|
222
240
|
```
|
|
223
241
|
|
|
224
|
-
|
|
242
|
+
**顺序有讲究**:`npm run build` 要排在 `npm test` 之前——`tests/pack-contents.spec.ts` 会遍历 `exports` 的每个目标,`lib/` 存在与否决定这条检查是执行还是被跳过(不报错,但会少一层保护)。`package.json` 已移除 `prepare`,因此 `npm ci` 不会再替你隐式构建;三个 CI workflow 都显式跑了 `npm run build`。
|
|
243
|
+
|
|
244
|
+
测试覆盖配置校验、协议阶梯、网关与在线元数据、目录投影、适配器(三种协议的线上请求)、Remote 契约与服务、会话头不变量、用量窗口与计量、客户端控制器与用量组件、两半的挂载与登记、流空闲超时与中止的错误映射、验证脚本的退出码契约、以及几条**结构性不变量**(inject 声明与源码读取一致、peer 覆盖双向、README 与 manifest 一致)。共 **43 个测试文件**——这个数字由 `tests/readme-consistency.spec.ts` 对着 `tests/` 目录校验,不会漂移。
|
|
225
245
|
|
|
226
246
|
## 发布(维护者)
|
|
227
247
|
|
package/lib/client.js
CHANGED
|
@@ -228,13 +228,20 @@ function parseTotals(value) {
|
|
|
228
228
|
if (typeof entry !== "number" || !Number.isFinite(entry) || entry < 0) throw new Error(`invalid meter field "${key}"`);
|
|
229
229
|
return entry;
|
|
230
230
|
};
|
|
231
|
+
const optionalCount = (key) => row[key] === void 0 ? void 0 : count(key);
|
|
232
|
+
const reasoningTokens = optionalCount("reasoningTokens");
|
|
233
|
+
const estimatedUSD = optionalCount("estimatedUSD");
|
|
234
|
+
const unpricedTokens = optionalCount("unpricedTokens");
|
|
231
235
|
return {
|
|
232
236
|
calls: count("calls"),
|
|
233
237
|
inputTokens: count("inputTokens"),
|
|
234
238
|
outputTokens: count("outputTokens"),
|
|
235
239
|
cacheReadTokens: count("cacheReadTokens"),
|
|
236
240
|
cacheWriteTokens: count("cacheWriteTokens"),
|
|
237
|
-
totalTokens: count("totalTokens")
|
|
241
|
+
totalTokens: count("totalTokens"),
|
|
242
|
+
...reasoningTokens === void 0 ? {} : { reasoningTokens },
|
|
243
|
+
...estimatedUSD === void 0 ? {} : { estimatedUSD },
|
|
244
|
+
...unpricedTokens === void 0 ? {} : { unpricedTokens }
|
|
238
245
|
};
|
|
239
246
|
}
|
|
240
247
|
function parseGoMeter(value) {
|
|
@@ -350,6 +357,12 @@ var en = {
|
|
|
350
357
|
usageCallsUnit: "calls",
|
|
351
358
|
usageTokensUnit: "tokens",
|
|
352
359
|
usageCached: "cached read",
|
|
360
|
+
usageReasoning: "reasoning tokens",
|
|
361
|
+
usageSpend: "Estimated spend",
|
|
362
|
+
usageUnpriced: "Unpriced",
|
|
363
|
+
usageSpendPartial: "Unpriced tokens",
|
|
364
|
+
usageUnpricedModels: "No published rate:",
|
|
365
|
+
usageSpendHint: "models.dev rates \xD7 the tokens the provider reported \u2014 an estimate, not a bill. On the subscription (Go) endpoints it is indicative only; only the metered (Console) endpoints make it reconcilable.",
|
|
353
366
|
usageLocalHint: "The gateway publishes quota percentages only; token counts are what this process actually spent.",
|
|
354
367
|
usageLoading: "Loading\u2026",
|
|
355
368
|
updateTitle: "Plugin update",
|
|
@@ -440,6 +453,12 @@ var zh = {
|
|
|
440
453
|
usageCallsUnit: "\u6B21\u8C03\u7528",
|
|
441
454
|
usageTokensUnit: "tokens",
|
|
442
455
|
usageCached: "\u7F13\u5B58\u8BFB\u53D6",
|
|
456
|
+
usageReasoning: "\u63A8\u7406 token",
|
|
457
|
+
usageSpend: "\u4F30\u7B97\u82B1\u8D39",
|
|
458
|
+
usageUnpriced: "\u672A\u5B9A\u4EF7",
|
|
459
|
+
usageSpendPartial: "\u672A\u5B9A\u4EF7 token",
|
|
460
|
+
usageUnpricedModels: "\u65E0\u5355\u4EF7\u7684\u6A21\u578B\uFF1A",
|
|
461
|
+
usageSpendHint: "\u6309 models.dev \u5355\u4EF7 \xD7 provider \u56DE\u4F20 token \u4F30\u7B97\uFF0C\u4E0D\u662F\u8D26\u5355\u3002\u8BA2\u9605\u7AEF\u70B9\uFF08Go\uFF09\u4E0B\u4EC5\u4F9B\u53C2\u8003\uFF0C\u6309\u91CF\u7AEF\u70B9\uFF08Console\uFF09\u624D\u6709\u5BF9\u8D26\u610F\u4E49\u3002",
|
|
443
462
|
usageLocalHint: "\u7F51\u5173\u53EA\u516C\u5E03\u989D\u5EA6\u767E\u5206\u6BD4\uFF1Btoken \u6570\u4E3A\u672C\u8FDB\u7A0B\u5B9E\u9645\u6D88\u8017\u3002",
|
|
444
463
|
usageLoading: "\u52A0\u8F7D\u4E2D\u2026",
|
|
445
464
|
updateTitle: "\u63D2\u4EF6\u66F4\u65B0",
|
|
@@ -1353,6 +1372,12 @@ function failureOf(error) {
|
|
|
1353
1372
|
return { message: "", retainPrevious: false };
|
|
1354
1373
|
}
|
|
1355
1374
|
var POLL_MS = 6e4;
|
|
1375
|
+
var MAX_UNPRICED_IDS = 3;
|
|
1376
|
+
function formatUSD(value) {
|
|
1377
|
+
if (value === 0) return "$0.0000";
|
|
1378
|
+
if (value < 1e-4) return "<$0.0001";
|
|
1379
|
+
return value < 1 ? `$${value.toFixed(4)}` : `$${value.toFixed(2)}`;
|
|
1380
|
+
}
|
|
1356
1381
|
function UsagePill({ directory, ...rest }) {
|
|
1357
1382
|
const state = (0, import_react2.useSyncExternalStore)(directory.subscribe, directory.getSnapshot, directory.getSnapshot);
|
|
1358
1383
|
return state.current?.provider === "opencode-go" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ActiveUsage, { ...rest }) : null;
|
|
@@ -1420,6 +1445,8 @@ function ActiveUsage({
|
|
|
1420
1445
|
};
|
|
1421
1446
|
}, [open]);
|
|
1422
1447
|
const label = windows === void 0 ? `Go \xB7 ${failure === void 0 ? "\u2026" : t("usageUnavailable")}` : `Go \xB7 ${t("usageRolling")} ${windows.rolling.percent}% \xB7 ${t("usageWeekly")} ${windows.weekly.percent}%` + (failure === void 0 ? "" : ` \xB7 ${t("usageStale")}`);
|
|
1448
|
+
const unpriced = (meter?.models ?? []).filter((entry) => entry.estimatedUSD === void 0);
|
|
1449
|
+
const unpricedModels = unpriced.slice(0, MAX_UNPRICED_IDS).map((entry) => entry.model).join(", ") + (unpriced.length > MAX_UNPRICED_IDS ? ` +${unpriced.length - MAX_UNPRICED_IDS}` : "");
|
|
1423
1450
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: pill_default.root, ref: root, children: [
|
|
1424
1451
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1425
1452
|
"button",
|
|
@@ -1473,6 +1500,25 @@ function ActiveUsage({
|
|
|
1473
1500
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("usageCached") }),
|
|
1474
1501
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: meter.totals.cacheReadTokens.toLocaleString(getLocale?.()) })
|
|
1475
1502
|
] }),
|
|
1503
|
+
meter.totals.reasoningTokens !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: pill_default.row, children: [
|
|
1504
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("usageReasoning") }),
|
|
1505
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: meter.totals.reasoningTokens.toLocaleString(getLocale?.()) })
|
|
1506
|
+
] }),
|
|
1507
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: pill_default.row, children: [
|
|
1508
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("usageSpend") }),
|
|
1509
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: meter.totals.estimatedUSD === void 0 ? t("usageUnpriced") : formatUSD(meter.totals.estimatedUSD) })
|
|
1510
|
+
] }),
|
|
1511
|
+
meter.totals.unpricedTokens !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: pill_default.hint, children: [
|
|
1512
|
+
t("usageSpendPartial"),
|
|
1513
|
+
" ",
|
|
1514
|
+
meter.totals.unpricedTokens.toLocaleString(getLocale?.())
|
|
1515
|
+
] }),
|
|
1516
|
+
unpriced.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: pill_default.hint, children: [
|
|
1517
|
+
t("usageUnpricedModels"),
|
|
1518
|
+
" ",
|
|
1519
|
+
unpricedModels
|
|
1520
|
+
] }),
|
|
1521
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: pill_default.hint, children: t("usageSpendHint") }),
|
|
1476
1522
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: pill_default.hint, children: t("usageLocalHint") })
|
|
1477
1523
|
] }),
|
|
1478
1524
|
updatedAt !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: pill_default.hint, children: [
|