@dan-ai-studio/dshopencodego 0.1.12 → 0.1.13
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 +3 -2
- package/README.md +3 -2
- package/lib/client.js +44 -95
- package/lib/index.js +16 -0
- package/lib/types/catalog/contract.js +9 -0
- package/lib/types/catalog/index.js +1 -0
- package/lib/types/catalog/metadata.d.ts +12 -0
- package/lib/types/catalog/metadata.js +18 -0
- package/lib/types/catalog/reading.js +1 -0
- package/lib/types/models.d.ts +6 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -124,8 +124,9 @@ The "OpenCode Go" section shows the live gateway catalog (42+ models) with, per
|
|
|
124
124
|
|
|
125
125
|
- **context / max input / max output**, **release date**, **price per 1M tokens** (from models.dev), and **Go allowance**;
|
|
126
126
|
- **Go allowance** comes from OpenCode's own documentation (the "usage limits / estimated requests" tables) — **no API exposes it**. It is a transcribed table (source URL and date in the `src/go-limits.ts` header); pricing or promotion changes require updating it and shipping a release;
|
|
127
|
-
- **Capability marks**:
|
|
128
|
-
-
|
|
127
|
+
- **Capability marks**: badges on each row label **structured output / temperature control / open weights** exactly as models.dev declares them. Only a declared "yes" is marked — an unstated or declared "no" stays unmarked, the same rule the reasoning levels follow.
|
|
128
|
+
- **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.
|
|
129
|
+
- 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.
|
|
129
130
|
|
|
130
131
|
**Default switches**: with no explicit `modelVisibility` entries at all, the **top five models by published monthly request estimate** are enabled (deprecated, unconfigurable, and training-"contributor" models never qualify). The first explicit entry switches the whole list to explicit values. The Host picker and the settings page compute this from one rule, so they never disagree.
|
|
131
132
|
|
package/README.md
CHANGED
|
@@ -124,8 +124,9 @@ API Key 通过 Harness 凭证库提供(引用名 `OPENCODE_GO_API_KEY`),
|
|
|
124
124
|
|
|
125
125
|
- **上下文 / 输入 / 输出**(最大输入只有部分模型有官方数据)、**发布时间**、**单价 /1M**(来自 models.dev)、**Go 额度**;
|
|
126
126
|
- **Go 额度**来自 OpenCode 官方文档的「使用限制 / 预估请求数」表,**没有接口提供**,是本插件转写的数据表(见 `src/go-limits.ts` 的注释:来源 URL 与转写日期);文档调整价格或促销时需要更新该表并随发版发布。
|
|
127
|
-
-
|
|
128
|
-
-
|
|
127
|
+
- **能力标记**:按 models.dev 的声明给每个模型加**结构化输出 / 温度控制 / 开源权重**徽标。只标注声明为"支持"的项——**未声明或声明不支持都不加标记**,与推理档位同一口径:不替文档下结论。
|
|
128
|
+
- **输入模态**:单独一行列出 models.dev 声明的输入方式(文本 / 图像 / 音频 / 视频 / PDF)。这是**模型自身的元数据**,不等于 DSH 能直传:DSH 的模型输入只原生支持文本与图像,音视频/PDF 走附件与工具读取路径;文档未声明时该行不显示。
|
|
129
|
+
- 筛选(按名称/ID、仅已启用、显示已弃用——**默认隐藏已弃用**)、排序(新发布优先 / 每月预估次数 / 输入单价 / 上下文 / 名称 / 已启用优先);列表是唯一视图,逐模型纵向排列。
|
|
129
130
|
|
|
130
131
|
**默认开关规则**:没人显式配置过 `modelVisibility` 时,默认启用**每月预估次数最高的 5 个**(跳过已弃用、无法配置、以及"贡献者版"这类以数据换折扣的模型);**一旦有任一显式条目,全部按显式值走**。同一规则同时作用于模型选择器与设置页,两处不会出现不同答案。
|
|
131
132
|
|
package/lib/client.js
CHANGED
|
@@ -26,6 +26,13 @@ __export(index_exports, {
|
|
|
26
26
|
});
|
|
27
27
|
module.exports = __toCommonJS(index_exports);
|
|
28
28
|
|
|
29
|
+
// src/catalog/constants.ts
|
|
30
|
+
var MODEL_LISTING_MAX_BYTES = 1024 * 1024;
|
|
31
|
+
var MODEL_METADATA_MAX_BYTES = 16 * 1024 * 1024;
|
|
32
|
+
|
|
33
|
+
// src/catalog/metadata.ts
|
|
34
|
+
var INPUT_MODALITIES = ["text", "image", "audio", "video", "pdf"];
|
|
35
|
+
|
|
29
36
|
// src/catalog/contract.ts
|
|
30
37
|
function parseGoQuota(value) {
|
|
31
38
|
if (value === null || typeof value !== "object") throw new Error("invalid go quota");
|
|
@@ -58,6 +65,11 @@ function parseCost(value) {
|
|
|
58
65
|
...typeof row["cacheWrite"] === "number" ? { cacheWrite: rate("cacheWrite") } : {}
|
|
59
66
|
};
|
|
60
67
|
}
|
|
68
|
+
function parseModalities(value) {
|
|
69
|
+
if (!Array.isArray(value)) return {};
|
|
70
|
+
const kept = INPUT_MODALITIES.filter((modality) => value.includes(modality));
|
|
71
|
+
return kept.length === 0 ? {} : { inputModalities: kept };
|
|
72
|
+
}
|
|
61
73
|
function parseModel(value) {
|
|
62
74
|
if (value === null || typeof value !== "object") throw new Error("invalid catalog model");
|
|
63
75
|
const row = value;
|
|
@@ -74,6 +86,7 @@ function parseModel(value) {
|
|
|
74
86
|
...row["cost"] === void 0 ? {} : { cost: parseCost(row["cost"]) },
|
|
75
87
|
...row["protocolSource"] === "builtin" || row["protocolSource"] === "online" || row["protocolSource"] === "inferred" || row["protocolSource"] === "override" ? { protocolSource: row["protocolSource"] } : {},
|
|
76
88
|
...typeof row["assumedLimits"] === "boolean" ? { assumedLimits: row["assumedLimits"] } : {},
|
|
89
|
+
...parseModalities(row["inputModalities"]),
|
|
77
90
|
...typeof row["structuredOutput"] === "boolean" ? { structuredOutput: row["structuredOutput"] } : {},
|
|
78
91
|
...typeof row["temperature"] === "boolean" ? { temperature: row["temperature"] } : {},
|
|
79
92
|
...typeof row["openWeights"] === "boolean" ? { openWeights: row["openWeights"] } : {},
|
|
@@ -259,17 +272,12 @@ var en = {
|
|
|
259
272
|
sortEnabled: "Enabled first",
|
|
260
273
|
sortQuota: "Monthly requests",
|
|
261
274
|
sortPrice: "Input price",
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
headerIo: "In / Out",
|
|
269
|
-
headerPrice: "Price /1M",
|
|
270
|
-
headerQuota: "Go quota",
|
|
271
|
-
headerCapabilities: "Capabilities",
|
|
272
|
-
headerNotes: "Notes",
|
|
275
|
+
inputLabel: "Input",
|
|
276
|
+
modalityText: "Text",
|
|
277
|
+
modalityImage: "Image",
|
|
278
|
+
modalityAudio: "Audio",
|
|
279
|
+
modalityVideo: "Video",
|
|
280
|
+
modalityPdf: "PDF",
|
|
273
281
|
capStructured: "Structured",
|
|
274
282
|
capTemperature: "Temp",
|
|
275
283
|
capOpenWeights: "Open",
|
|
@@ -333,17 +341,12 @@ var zh = {
|
|
|
333
341
|
sortEnabled: "\u5DF2\u542F\u7528\u4F18\u5148",
|
|
334
342
|
sortQuota: "\u6BCF\u6708\u9884\u4F30\u6B21\u6570",
|
|
335
343
|
sortPrice: "\u8F93\u5165\u5355\u4EF7",
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
headerIo: "\u8F93\u5165 / \u8F93\u51FA",
|
|
343
|
-
headerPrice: "\u5355\u4EF7 /1M",
|
|
344
|
-
headerQuota: "Go \u989D\u5EA6",
|
|
345
|
-
headerCapabilities: "\u80FD\u529B",
|
|
346
|
-
headerNotes: "\u5907\u6CE8",
|
|
344
|
+
inputLabel: "\u8F93\u5165",
|
|
345
|
+
modalityText: "\u6587\u672C",
|
|
346
|
+
modalityImage: "\u56FE\u50CF",
|
|
347
|
+
modalityAudio: "\u97F3\u9891",
|
|
348
|
+
modalityVideo: "\u89C6\u9891",
|
|
349
|
+
modalityPdf: "PDF",
|
|
347
350
|
capStructured: "\u7ED3\u6784\u5316",
|
|
348
351
|
capTemperature: "\u6E29\u5EA6",
|
|
349
352
|
capOpenWeights: "\u5F00\u6E90",
|
|
@@ -396,8 +399,7 @@ var INITIAL_FILTER = {
|
|
|
396
399
|
query: "",
|
|
397
400
|
showDeprecated: false,
|
|
398
401
|
onlyEnabled: false,
|
|
399
|
-
sort: "quota"
|
|
400
|
-
view: "list"
|
|
402
|
+
sort: "quota"
|
|
401
403
|
};
|
|
402
404
|
function compactCount(value) {
|
|
403
405
|
if (value >= 1e6) return `${(value / 1e6).toFixed(value >= 1e7 ? 0 : 1)}M`;
|
|
@@ -448,6 +450,16 @@ function capabilityLabels(model, t) {
|
|
|
448
450
|
if (model.openWeights === true) labels.push(t("capOpenWeights"));
|
|
449
451
|
return labels;
|
|
450
452
|
}
|
|
453
|
+
var MODALITY_KEYS = {
|
|
454
|
+
text: "modalityText",
|
|
455
|
+
image: "modalityImage",
|
|
456
|
+
audio: "modalityAudio",
|
|
457
|
+
video: "modalityVideo",
|
|
458
|
+
pdf: "modalityPdf"
|
|
459
|
+
};
|
|
460
|
+
function inputModalityLabels(model, t) {
|
|
461
|
+
return (model.inputModalities ?? []).map((modality) => t(MODALITY_KEYS[modality]));
|
|
462
|
+
}
|
|
451
463
|
|
|
452
464
|
// plugin-css:/home/runner/work/dshopencodego/dshopencodego/src/client/section.module.css
|
|
453
465
|
var id = "@dan-ai-studio/dshopencodego/section.module.css";
|
|
@@ -455,10 +467,10 @@ if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id) + "]")
|
|
|
455
467
|
const style = document.createElement("style");
|
|
456
468
|
style.dataset.plugin = "@dan-ai-studio/dshopencodego";
|
|
457
469
|
style.dataset.pluginCss = id;
|
|
458
|
-
style.textContent = ".d7lz0W_root{color:var(--dsw-alias-label-primary);flex-direction:column;gap:1.25rem;display:flex}.d7lz0W_title{margin:0;font-size:1.05rem}.d7lz0W_hint{color:var(--dsw-alias-label-secondary);margin:0;font-size:.8rem}.d7lz0W_warn{color:var(--dsw-alias-state-warn-primary);margin:0;font-size:.8rem}.d7lz0W_ok{color:var(--dsw-alias-state-success-primary);font-size:.8rem}.d7lz0W_block{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:.6rem;flex-direction:column;gap:.5rem;padding:.85rem;display:flex}.d7lz0W_row{flex-wrap:wrap;align-items:center;gap:.5rem;display:flex}.d7lz0W_row>strong{margin-right:auto}.d7lz0W_input{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);min-width:10rem;color:var(--dsw-alias-label-primary);font:inherit;border-radius:.35rem;flex:14rem;padding:.35rem .5rem;font-size:.85rem}.d7lz0W_button{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:.35rem;padding:.35rem .7rem;font-size:.8rem}.d7lz0W_button:disabled{color:var(--dsw-alias-state-idle-primary);cursor:default}.d7lz0W_number{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:5rem;color:var(--dsw-alias-label-primary);font:inherit;border-radius:.35rem;padding:.25rem .4rem;font-size:.8rem}.d7lz0W_select{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:auto;max-width:100%;color:var(--dsw-alias-label-primary);font:inherit;border-radius:.35rem;padding:.25rem .4rem;font-size:.8rem}.
|
|
470
|
+
style.textContent = ".d7lz0W_root{color:var(--dsw-alias-label-primary);flex-direction:column;gap:1.25rem;display:flex}.d7lz0W_title{margin:0;font-size:1.05rem}.d7lz0W_hint{color:var(--dsw-alias-label-secondary);margin:0;font-size:.8rem}.d7lz0W_warn{color:var(--dsw-alias-state-warn-primary);margin:0;font-size:.8rem}.d7lz0W_ok{color:var(--dsw-alias-state-success-primary);font-size:.8rem}.d7lz0W_block{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:.6rem;flex-direction:column;gap:.5rem;padding:.85rem;display:flex}.d7lz0W_row{flex-wrap:wrap;align-items:center;gap:.5rem;display:flex}.d7lz0W_row>strong{margin-right:auto}.d7lz0W_input{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);min-width:10rem;color:var(--dsw-alias-label-primary);font:inherit;border-radius:.35rem;flex:14rem;padding:.35rem .5rem;font-size:.85rem}.d7lz0W_button{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:.35rem;padding:.35rem .7rem;font-size:.8rem}.d7lz0W_button:disabled{color:var(--dsw-alias-state-idle-primary);cursor:default}.d7lz0W_number{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:5rem;color:var(--dsw-alias-label-primary);font:inherit;border-radius:.35rem;padding:.25rem .4rem;font-size:.8rem}.d7lz0W_select{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:auto;max-width:100%;color:var(--dsw-alias-label-primary);font:inherit;border-radius:.35rem;padding:.25rem .4rem;font-size:.8rem}.d7lz0W_list{flex-direction:column;display:flex}.d7lz0W_item{border-top:1px solid var(--dsw-alias-border-l1);align-items:flex-start;gap:.5rem;padding:.45rem .15rem;display:flex}.d7lz0W_item input[type=checkbox]{accent-color:var(--dsw-alias-brand-primary);margin-top:.15rem}.d7lz0W_itemBody{flex-direction:column;gap:.15rem;min-width:0;display:flex}.d7lz0W_itemMain{flex-wrap:wrap;align-items:baseline;gap:.6rem;display:flex}.d7lz0W_itemMeta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:.75rem;font-size:.75rem;display:flex}.d7lz0W_itemModalities{color:var(--dsw-alias-label-secondary);font-size:.75rem}.d7lz0W_name{color:var(--dsw-alias-label-primary)}.d7lz0W_mono{color:var(--dsw-alias-label-secondary);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.d7lz0W_badge{color:var(--dsw-alias-brand-primary)}";
|
|
459
471
|
document.head.appendChild(style);
|
|
460
472
|
}
|
|
461
|
-
var section_default = { "title": "d7lz0W_title", "
|
|
473
|
+
var section_default = { "title": "d7lz0W_title", "root": "d7lz0W_root", "block": "d7lz0W_block", "name": "d7lz0W_name", "number": "d7lz0W_number", "select": "d7lz0W_select", "badge": "d7lz0W_badge", "itemModalities": "d7lz0W_itemModalities", "hint": "d7lz0W_hint", "mono": "d7lz0W_mono", "warn": "d7lz0W_warn", "row": "d7lz0W_row", "ok": "d7lz0W_ok", "input": "d7lz0W_input", "list": "d7lz0W_list", "button": "d7lz0W_button", "itemMain": "d7lz0W_itemMain", "itemBody": "d7lz0W_itemBody", "itemMeta": "d7lz0W_itemMeta", "item": "d7lz0W_item" };
|
|
462
474
|
|
|
463
475
|
// src/client/Section.tsx
|
|
464
476
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -898,80 +910,16 @@ function Section({ controller, t, getLocale }) {
|
|
|
898
910
|
}
|
|
899
911
|
)
|
|
900
912
|
] }),
|
|
901
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: section_default.segmented, children: [
|
|
902
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
903
|
-
"button",
|
|
904
|
-
{
|
|
905
|
-
type: "button",
|
|
906
|
-
className: state.filter.view === "list" ? section_default.segmentActive : section_default.segment,
|
|
907
|
-
onClick: () => {
|
|
908
|
-
controller.setFilter({ view: "list" });
|
|
909
|
-
},
|
|
910
|
-
children: t("viewList")
|
|
911
|
-
}
|
|
912
|
-
),
|
|
913
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
914
|
-
"button",
|
|
915
|
-
{
|
|
916
|
-
type: "button",
|
|
917
|
-
className: state.filter.view === "table" ? section_default.segmentActive : section_default.segment,
|
|
918
|
-
onClick: () => {
|
|
919
|
-
controller.setFilter({ view: "table" });
|
|
920
|
-
},
|
|
921
|
-
children: t("viewTable")
|
|
922
|
-
}
|
|
923
|
-
)
|
|
924
|
-
] }),
|
|
925
913
|
hidden > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: section_default.hint, children: [
|
|
926
914
|
hidden,
|
|
927
915
|
" ",
|
|
928
916
|
t("filterHidden")
|
|
929
917
|
] })
|
|
930
918
|
] }),
|
|
931
|
-
reading !== void 0 &&
|
|
932
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
933
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {}),
|
|
934
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerModel") }),
|
|
935
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerId") }),
|
|
936
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerReleased") }),
|
|
937
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerCapacity") }),
|
|
938
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerIo") }),
|
|
939
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerPrice") }),
|
|
940
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerQuota") }),
|
|
941
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerCapabilities") }),
|
|
942
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerNotes") })
|
|
943
|
-
] }) }),
|
|
944
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: rows.map((model) => {
|
|
945
|
-
const badge = badgeFor(model, t);
|
|
946
|
-
const capabilities = capabilityLabels(model, t).join(" \xB7 ");
|
|
947
|
-
const offered = isOffered(model, visibility);
|
|
948
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
949
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.toggle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
950
|
-
"input",
|
|
951
|
-
{
|
|
952
|
-
type: "checkbox",
|
|
953
|
-
"aria-label": `${t("catalogTitle")}: ${model.name}`,
|
|
954
|
-
checked: offered,
|
|
955
|
-
disabled: !editable || model.configurationMissing !== void 0,
|
|
956
|
-
onChange: (event) => {
|
|
957
|
-
controller.setVisibility(model.id, event.target.checked);
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
) }),
|
|
961
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.name, children: model.name }),
|
|
962
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.mono, children: model.id }),
|
|
963
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.hint, children: model.releaseDate ?? "\u2014" }),
|
|
964
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.hint, children: model.contextWindow === void 0 ? "\u2014" : model.contextWindow.toLocaleString(getLocale?.()) }),
|
|
965
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.hint, children: ioLabel(model, t) }),
|
|
966
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.hint, children: priceLabel(model) }),
|
|
967
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.hint, children: quotaLabel(model, t) }),
|
|
968
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.hint, children: capabilities.length === 0 ? "\u2014" : capabilities }),
|
|
969
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: badge.length === 0 ? section_default.hint : section_default.badge, children: badge })
|
|
970
|
-
] }, model.id);
|
|
971
|
-
}) })
|
|
972
|
-
] }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: section_default.list, children: rows.map((model) => {
|
|
919
|
+
reading !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: section_default.list, children: rows.map((model) => {
|
|
973
920
|
const badge = badgeFor(model, t);
|
|
974
921
|
const capabilities = capabilityLabels(model, t).join(" \xB7 ");
|
|
922
|
+
const modalities = inputModalityLabels(model, t).join(" \xB7 ");
|
|
975
923
|
const offered = isOffered(model, visibility);
|
|
976
924
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: section_default.item, children: [
|
|
977
925
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -999,10 +947,11 @@ function Section({ controller, t, getLocale }) {
|
|
|
999
947
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: ioLabel(model, t) }),
|
|
1000
948
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: priceLabel(model) }),
|
|
1001
949
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: quotaLabel(model, t) })
|
|
1002
|
-
] })
|
|
950
|
+
] }),
|
|
951
|
+
modalities.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: section_default.itemModalities, children: `${t("inputLabel")} ${modalities}` })
|
|
1003
952
|
] })
|
|
1004
953
|
] }, model.id);
|
|
1005
|
-
}) })
|
|
954
|
+
}) })
|
|
1006
955
|
] })
|
|
1007
956
|
] });
|
|
1008
957
|
}
|
|
@@ -1019,7 +968,7 @@ if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id2) + "]"
|
|
|
1019
968
|
style.textContent = ".mMNkkW_root{align-items:center;display:inline-flex;position:relative}.mMNkkW_trigger{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;border-radius:999px;padding:.35rem .6rem;font-size:.8rem;line-height:1}.mMNkkW_trigger:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l2)}.mMNkkW_panel{z-index:20;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-overlay);width:18rem;color:var(--dsw-alias-label-primary);border-radius:.6rem;flex-direction:column;gap:.6rem;padding:.75rem .85rem;display:flex;position:absolute;bottom:calc(100% + .5rem);right:0;box-shadow:0 8px 24px #0000002e}.mMNkkW_window{flex-direction:column;gap:.25rem;display:flex}.mMNkkW_row{justify-content:space-between;gap:.5rem;font-size:.85rem;display:flex}.mMNkkW_hint{color:var(--dsw-alias-label-secondary);margin:0;font-size:.75rem}.mMNkkW_progress{width:100%;height:.35rem;accent-color:var(--dsw-alias-brand-primary)}.mMNkkW_limited{color:var(--dsw-alias-state-warn-primary);font-size:.75rem}.mMNkkW_warning{border:1px solid var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-label-primary);border-radius:.4rem;padding:.4rem .5rem;font-size:.75rem}.mMNkkW_retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:.35rem;align-self:flex-start;padding:.25rem .5rem;font-size:.75rem}.mMNkkW_local{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:.2rem;padding-top:.5rem;display:flex}";
|
|
1020
969
|
document.head.appendChild(style);
|
|
1021
970
|
}
|
|
1022
|
-
var pill_default = { "
|
|
971
|
+
var pill_default = { "progress": "mMNkkW_progress", "root": "mMNkkW_root", "limited": "mMNkkW_limited", "local": "mMNkkW_local", "panel": "mMNkkW_panel", "row": "mMNkkW_row", "retry": "mMNkkW_retry", "trigger": "mMNkkW_trigger", "warning": "mMNkkW_warning", "hint": "mMNkkW_hint", "window": "mMNkkW_window" };
|
|
1023
972
|
|
|
1024
973
|
// src/client/UsagePill.tsx
|
|
1025
974
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
package/lib/index.js
CHANGED
|
@@ -202,6 +202,7 @@ function decideProtocol(id, evidence) {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
// src/catalog/metadata.ts
|
|
205
|
+
var INPUT_MODALITIES = ["text", "image", "audio", "video", "pdf"];
|
|
205
206
|
var LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
206
207
|
function record(value) {
|
|
207
208
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
@@ -215,6 +216,12 @@ function nonEmptyString(value) {
|
|
|
215
216
|
function declaredBoolean(value) {
|
|
216
217
|
return typeof value === "boolean" ? value : void 0;
|
|
217
218
|
}
|
|
219
|
+
function readInputModalities(metadata) {
|
|
220
|
+
const declared = record(metadata["modalities"])["input"];
|
|
221
|
+
if (!Array.isArray(declared)) return void 0;
|
|
222
|
+
const kept = INPUT_MODALITIES.filter((modality) => declared.includes(modality));
|
|
223
|
+
return kept.length === 0 ? void 0 : kept;
|
|
224
|
+
}
|
|
218
225
|
function validReleaseDate(value) {
|
|
219
226
|
if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return void 0;
|
|
220
227
|
return Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value ? value : void 0;
|
|
@@ -344,6 +351,7 @@ function readOnlineMetadata(body, sources) {
|
|
|
344
351
|
maxTokens,
|
|
345
352
|
assumedLimits: onlineContext === void 0 && builtinContext === void 0 || onlineOutput === void 0 && builtinOutput === void 0,
|
|
346
353
|
input,
|
|
354
|
+
inputModalities: readInputModalities(metadata),
|
|
347
355
|
reasoning,
|
|
348
356
|
thinkingLevelMap: reasoning ? thinkingLevels(metadata, exact?.api === api ? exact : sibling) : void 0,
|
|
349
357
|
structuredOutput: declaredBoolean(metadata["structured_output"]),
|
|
@@ -540,6 +548,7 @@ var OpencodeGoCatalog = class {
|
|
|
540
548
|
maxTokens,
|
|
541
549
|
assumedLimits: exact === void 0,
|
|
542
550
|
input: exact?.input ?? this.options.defaults.input,
|
|
551
|
+
inputModalities: void 0,
|
|
543
552
|
reasoning: exact?.reasoning ?? false,
|
|
544
553
|
thinkingLevelMap: exact?.thinkingLevelMap,
|
|
545
554
|
// No source describes these, so the page says nothing instead of
|
|
@@ -1508,6 +1517,11 @@ function parseCost(value) {
|
|
|
1508
1517
|
...typeof row["cacheWrite"] === "number" ? { cacheWrite: rate("cacheWrite") } : {}
|
|
1509
1518
|
};
|
|
1510
1519
|
}
|
|
1520
|
+
function parseModalities(value) {
|
|
1521
|
+
if (!Array.isArray(value)) return {};
|
|
1522
|
+
const kept = INPUT_MODALITIES.filter((modality) => value.includes(modality));
|
|
1523
|
+
return kept.length === 0 ? {} : { inputModalities: kept };
|
|
1524
|
+
}
|
|
1511
1525
|
function parseModel(value) {
|
|
1512
1526
|
if (value === null || typeof value !== "object") throw new Error("invalid catalog model");
|
|
1513
1527
|
const row = value;
|
|
@@ -1524,6 +1538,7 @@ function parseModel(value) {
|
|
|
1524
1538
|
...row["cost"] === void 0 ? {} : { cost: parseCost(row["cost"]) },
|
|
1525
1539
|
...row["protocolSource"] === "builtin" || row["protocolSource"] === "online" || row["protocolSource"] === "inferred" || row["protocolSource"] === "override" ? { protocolSource: row["protocolSource"] } : {},
|
|
1526
1540
|
...typeof row["assumedLimits"] === "boolean" ? { assumedLimits: row["assumedLimits"] } : {},
|
|
1541
|
+
...parseModalities(row["inputModalities"]),
|
|
1527
1542
|
...typeof row["structuredOutput"] === "boolean" ? { structuredOutput: row["structuredOutput"] } : {},
|
|
1528
1543
|
...typeof row["temperature"] === "boolean" ? { temperature: row["temperature"] } : {},
|
|
1529
1544
|
...typeof row["openWeights"] === "boolean" ? { openWeights: row["openWeights"] } : {},
|
|
@@ -1718,6 +1733,7 @@ function catalogReading(snapshot, visibility, listingFailure) {
|
|
|
1718
1733
|
protocolSource: fact.protocolSource,
|
|
1719
1734
|
assumedLimits: fact.assumedLimits,
|
|
1720
1735
|
...fact.structuredOutput === void 0 ? {} : { structuredOutput: fact.structuredOutput },
|
|
1736
|
+
...fact.inputModalities === void 0 ? {} : { inputModalities: fact.inputModalities },
|
|
1721
1737
|
...fact.temperature === void 0 ? {} : { temperature: fact.temperature },
|
|
1722
1738
|
...fact.openWeights === void 0 ? {} : { openWeights: fact.openWeights }
|
|
1723
1739
|
};
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module @dan-ai-studio/dshopencodego/catalog/contract
|
|
11
11
|
*/
|
|
12
|
+
import { INPUT_MODALITIES } from "./metadata.js";
|
|
12
13
|
function parseGoQuota(value) {
|
|
13
14
|
if (value === null || typeof value !== 'object')
|
|
14
15
|
throw new Error('invalid go quota');
|
|
@@ -44,6 +45,13 @@ function parseCost(value) {
|
|
|
44
45
|
...typeof row['cacheWrite'] === 'number' ? { cacheWrite: rate('cacheWrite') } : {},
|
|
45
46
|
};
|
|
46
47
|
}
|
|
48
|
+
/** Recognised input modalities out of one wire value; absent when none survive. */
|
|
49
|
+
function parseModalities(value) {
|
|
50
|
+
if (!Array.isArray(value))
|
|
51
|
+
return {};
|
|
52
|
+
const kept = INPUT_MODALITIES.filter(modality => value.includes(modality));
|
|
53
|
+
return kept.length === 0 ? {} : { inputModalities: kept };
|
|
54
|
+
}
|
|
47
55
|
function parseModel(value) {
|
|
48
56
|
if (value === null || typeof value !== 'object')
|
|
49
57
|
throw new Error('invalid catalog model');
|
|
@@ -64,6 +72,7 @@ function parseModel(value) {
|
|
|
64
72
|
|| row['protocolSource'] === 'inferred' || row['protocolSource'] === 'override'
|
|
65
73
|
? { protocolSource: row['protocolSource'] } : {},
|
|
66
74
|
...typeof row['assumedLimits'] === 'boolean' ? { assumedLimits: row['assumedLimits'] } : {},
|
|
75
|
+
...parseModalities(row['inputModalities']),
|
|
67
76
|
...typeof row['structuredOutput'] === 'boolean' ? { structuredOutput: row['structuredOutput'] } : {},
|
|
68
77
|
...typeof row['temperature'] === 'boolean' ? { temperature: row['temperature'] } : {},
|
|
69
78
|
...typeof row['openWeights'] === 'boolean' ? { openWeights: row['openWeights'] } : {},
|
|
@@ -206,6 +206,7 @@ export class OpencodeGoCatalog {
|
|
|
206
206
|
maxTokens,
|
|
207
207
|
assumedLimits: exact === undefined,
|
|
208
208
|
input: exact?.input ?? this.options.defaults.input,
|
|
209
|
+
inputModalities: undefined,
|
|
209
210
|
reasoning: exact?.reasoning ?? false,
|
|
210
211
|
thinkingLevelMap: exact?.thinkingLevelMap,
|
|
211
212
|
// No source describes these, so the page says nothing instead of
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import type { Api, Model, ModelCost, ThinkingLevelMap } from '@earendil-works/pi-ai';
|
|
18
18
|
import type { ProtocolSource, WireProtocol } from './protocol.ts';
|
|
19
|
+
/** Input modalities this build can name, in display order. */
|
|
20
|
+
export declare const INPUT_MODALITIES: readonly ["text", "image", "audio", "video", "pdf"];
|
|
21
|
+
/** One input modality a model document may declare. */
|
|
22
|
+
export type InputModality = (typeof INPUT_MODALITIES)[number];
|
|
19
23
|
/** Everything this plugin knows about one advertised model. */
|
|
20
24
|
export interface ModelFacts {
|
|
21
25
|
readonly id: string;
|
|
@@ -30,6 +34,14 @@ export interface ModelFacts {
|
|
|
30
34
|
/** True when a capacity came from the route default rather than a source. */
|
|
31
35
|
readonly assumedLimits: boolean;
|
|
32
36
|
readonly input: readonly ('text' | 'image')[];
|
|
37
|
+
/**
|
|
38
|
+
* Every input modality the document declares, in display order. `undefined`
|
|
39
|
+
* means it declares none this build can name — silence, not "text only".
|
|
40
|
+
* Kept beside {@link input} because the two answer different questions:
|
|
41
|
+
* `input` gates what this route may send, this one reports what the model
|
|
42
|
+
* itself accepts.
|
|
43
|
+
*/
|
|
44
|
+
readonly inputModalities: readonly InputModality[] | undefined;
|
|
33
45
|
readonly reasoning: boolean;
|
|
34
46
|
readonly thinkingLevelMap: ThinkingLevelMap | undefined;
|
|
35
47
|
/**
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { asWireProtocol, decideProtocol, protocolOfNpm } from "./protocol.js";
|
|
18
18
|
import { MODEL_METADATA_PROVIDER } from "./constants.js";
|
|
19
|
+
/** Input modalities this build can name, in display order. */
|
|
20
|
+
export const INPUT_MODALITIES = ['text', 'image', 'audio', 'video', 'pdf'];
|
|
19
21
|
const LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
20
22
|
function record(value) {
|
|
21
23
|
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
@@ -31,6 +33,21 @@ function nonEmptyString(value) {
|
|
|
31
33
|
function declaredBoolean(value) {
|
|
32
34
|
return typeof value === 'boolean' ? value : undefined;
|
|
33
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Input modalities exactly as the document declares them, in display order.
|
|
38
|
+
*
|
|
39
|
+
* A modality this build cannot name is dropped rather than passed through: the
|
|
40
|
+
* settings page labels every value it renders. An absent list, or one naming
|
|
41
|
+
* nothing recognisable, stays `undefined` — the document is silent, which is a
|
|
42
|
+
* different fact from "text only".
|
|
43
|
+
*/
|
|
44
|
+
function readInputModalities(metadata) {
|
|
45
|
+
const declared = record(metadata['modalities'])['input'];
|
|
46
|
+
if (!Array.isArray(declared))
|
|
47
|
+
return undefined;
|
|
48
|
+
const kept = INPUT_MODALITIES.filter(modality => declared.includes(modality));
|
|
49
|
+
return kept.length === 0 ? undefined : kept;
|
|
50
|
+
}
|
|
34
51
|
/** A calendar date models.dev states without a timezone. */
|
|
35
52
|
function validReleaseDate(value) {
|
|
36
53
|
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value))
|
|
@@ -225,6 +242,7 @@ export function readOnlineMetadata(body, sources) {
|
|
|
225
242
|
assumedLimits: onlineContext === undefined && builtinContext === undefined
|
|
226
243
|
|| onlineOutput === undefined && builtinOutput === undefined,
|
|
227
244
|
input,
|
|
245
|
+
inputModalities: readInputModalities(metadata),
|
|
228
246
|
reasoning,
|
|
229
247
|
thinkingLevelMap: reasoning ? thinkingLevels(metadata, exact?.api === api ? exact : sibling) : undefined,
|
|
230
248
|
structuredOutput: declaredBoolean(metadata['structured_output']),
|
|
@@ -44,6 +44,7 @@ export function catalogReading(snapshot, visibility, listingFailure) {
|
|
|
44
44
|
protocolSource: fact.protocolSource,
|
|
45
45
|
assumedLimits: fact.assumedLimits,
|
|
46
46
|
...fact.structuredOutput === undefined ? {} : { structuredOutput: fact.structuredOutput },
|
|
47
|
+
...fact.inputModalities === undefined ? {} : { inputModalities: fact.inputModalities },
|
|
47
48
|
...fact.temperature === undefined ? {} : { temperature: fact.temperature },
|
|
48
49
|
...fact.openWeights === undefined ? {} : { openWeights: fact.openWeights },
|
|
49
50
|
};
|
package/lib/types/models.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* @module @dan-ai-studio/dshopencodego/models
|
|
10
10
|
*/
|
|
11
11
|
import type { ProtocolSource } from './catalog/protocol.ts';
|
|
12
|
+
import type { InputModality } from './catalog/metadata.ts';
|
|
12
13
|
import type { GoQuota } from './go-limits.ts';
|
|
13
14
|
/** One model as the settings page and the picker describe it. */
|
|
14
15
|
export interface ModelSummary {
|
|
@@ -36,6 +37,11 @@ export interface ModelSummary {
|
|
|
36
37
|
};
|
|
37
38
|
/** Which ladder level decided this model's protocol. */
|
|
38
39
|
readonly protocolSource?: ProtocolSource;
|
|
40
|
+
/**
|
|
41
|
+
* Every input modality models.dev declares, in display order. Absent when the
|
|
42
|
+
* document declares none this build can name.
|
|
43
|
+
*/
|
|
44
|
+
readonly inputModalities?: readonly InputModality[];
|
|
39
45
|
/**
|
|
40
46
|
* Capabilities models.dev declares, in its own words. Each is absent when the
|
|
41
47
|
* document is silent about it — absence is "unstated", never "unsupported",
|
package/package.json
CHANGED