dsh-grok-subscription 1.0.6 → 1.0.8
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 +6 -2
- package/README.md +6 -2
- package/lib/client.js +32 -2
- package/lib/index.js +136 -24
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -25,7 +25,7 @@ Models, reasoning effort, and weekly quota all stay inside DSH.
|
|
|
25
25
|
|
|
26
26
|
## Three-step start
|
|
27
27
|
|
|
28
|
-
1. **Install the plugin.** Run the command below; to select a candidate version, enter the full `package@version`, for example `dsh-grok-subscription@1.0.
|
|
28
|
+
1. **Install the plugin.** Run the command below; to select a candidate version, enter the full `package@version`, for example `dsh-grok-subscription@1.0.8`.
|
|
29
29
|
|
|
30
30
|
```sh
|
|
31
31
|
dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
|
|
@@ -91,7 +91,7 @@ dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
|
|
|
91
91
|
You can also install the version published on npm:
|
|
92
92
|
|
|
93
93
|
```sh
|
|
94
|
-
dsh plugin --profile web add dsh-grok-subscription@1.0.
|
|
94
|
+
dsh plugin --profile web add dsh-grok-subscription@1.0.8
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
DSH handles target selection, the profile lock, dependency resolution, and bundle activation.
|
|
@@ -160,6 +160,10 @@ The badge appears only when the current session's provider is `grok-build` and t
|
|
|
160
160
|
|
|
161
161
|
`grok-4.7`, `grok-4.7-fast` and `grok-4.6` accept image input: paste or attach an image in the composer and ask about it. Images are normalized to the host's pixel and byte budget by its attachment service before they are sent, and an over-budget image degrades to a text description rather than being dropped silently.
|
|
162
162
|
|
|
163
|
+
**Both paths support it**: when the host's official `pi-ai` adapter is available it handles images, and otherwise the bundled fallback adapter reads the attachment bytes itself and inlines them as `input_image`. Image support therefore does not depend on resolving the host's packages. The **Active path** row in Settings shows which one is live and whether image input is available — check it first when a model rejects an image.
|
|
164
|
+
|
|
165
|
+
> If it says "bundled fallback" and images are rejected, `dsh web` most likely was not restarted after updating: the client UI refreshes from disk, but the host adapter is loaded at process start.
|
|
166
|
+
|
|
163
167
|
`grok-4.5` is **deliberately excluded**. Given a solid red image and asked its colour it answered "green", and it described a yellow square as a "circle with a black outline" — it confabulates instead of looking. The plugin therefore does not advertise image input for it, so it cannot return confident nonsense.
|
|
164
168
|
|
|
165
169
|
### Reasoning effort
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
## 三步开始
|
|
27
27
|
|
|
28
|
-
1. **安装插件**:在终端运行下面的命令;指定候选版本时填写完整的 `包名@版本`,例如 `dsh-grok-subscription@1.0.
|
|
28
|
+
1. **安装插件**:在终端运行下面的命令;指定候选版本时填写完整的 `包名@版本`,例如 `dsh-grok-subscription@1.0.8`。
|
|
29
29
|
|
|
30
30
|
```sh
|
|
31
31
|
dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
|
|
@@ -91,7 +91,7 @@ dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
|
|
|
91
91
|
也可以按 npm 上的已发布版本安装:
|
|
92
92
|
|
|
93
93
|
```sh
|
|
94
|
-
dsh plugin --profile web add dsh-grok-subscription@1.0.
|
|
94
|
+
dsh plugin --profile web add dsh-grok-subscription@1.0.8
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
目标选择、profile 锁、依赖解析和 bundle 激活均由 DSH 负责。
|
|
@@ -160,6 +160,10 @@ dsh --profile web --dump-config
|
|
|
160
160
|
|
|
161
161
|
`grok-4.7`、`grok-4.7-fast` 与 `grok-4.6` 支持图片输入:在输入框粘贴或附加图片即可提问。图片由宿主的附件服务按像素与体积预算归一化后发送,超预算时降级为文本说明,而不是静默丢弃。
|
|
162
162
|
|
|
163
|
+
**两条通路都支持**:宿主官方 `pi-ai` 适配器可用时走官方实现,否则内置回退适配器自己读取附件字节并内联成 `input_image`。因此贴图能力不依赖宿主包的解析结果。设置页的「运行通路」会显示当前生效的是哪一条,以及图片输入是否可用 —— 遇到「当前模型不支持图片」时先看这一行。
|
|
164
|
+
|
|
165
|
+
> 若显示「内置回退」且贴图被拒,通常是 `dsh web` 更新后没有重启:客户端界面会随刷新更新,但主机端适配器是进程启动时加载的。
|
|
166
|
+
|
|
163
167
|
`grok-4.5` **不在支持列表内**。实测给它一张纯红图片并询问颜色,它回答 "green",还把黄色方块描述成「带黑边的圆」——它在编造而不是看图。插件因此不为它声明图片能力,避免输出看起来自信却完全错误的答案。
|
|
164
168
|
|
|
165
169
|
### 推理档位
|
package/lib/client.js
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/client.jsx
|
|
21
21
|
var client_exports = {};
|
|
22
22
|
__export(client_exports, {
|
|
23
|
+
DiagnosticsRows: () => DiagnosticsRows,
|
|
23
24
|
GrokSubscriptionPanel: () => GrokSubscriptionPanel,
|
|
24
25
|
GrokSubscriptionSection: () => GrokSubscriptionSection,
|
|
25
26
|
RPC_CALL_TIMEOUT_MS: () => RPC_CALL_TIMEOUT_MS,
|
|
@@ -37,7 +38,7 @@ var import_react2 = require("react");
|
|
|
37
38
|
var zh = {
|
|
38
39
|
nav: "Grok \u8BA2\u9605",
|
|
39
40
|
title: "Grok \u8BA2\u9605",
|
|
40
|
-
subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 1.0.
|
|
41
|
+
subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 1.0.8",
|
|
41
42
|
signedIn: "\u5DF2\u767B\u5F55",
|
|
42
43
|
signedOut: "\u672A\u767B\u5F55",
|
|
43
44
|
account: "\u8D26\u6237",
|
|
@@ -58,6 +59,13 @@ var zh = {
|
|
|
58
59
|
loginHelp: "\u767B\u5F55\u65B9\u5F0F\u8BF4\u660E",
|
|
59
60
|
refreshCatalog: "\u5237\u65B0\u6A21\u578B\u76EE\u5F55",
|
|
60
61
|
renderError: "\u754C\u9762\u6E32\u67D3\u5931\u8D25",
|
|
62
|
+
diagnostics: "\u8FD0\u884C\u901A\u8DEF",
|
|
63
|
+
adapterPiAi: "\u5B98\u65B9 pi-ai",
|
|
64
|
+
adapterFallback: "\u5185\u7F6E\u56DE\u9000",
|
|
65
|
+
imageInput: "\u56FE\u7247\u8F93\u5165",
|
|
66
|
+
imageInputOn: "\u53EF\u7528",
|
|
67
|
+
imageInputOff: "\u4E0D\u53EF\u7528\uFF08\u672A\u89E3\u6790\u5230\u5BBF\u4E3B\u9644\u4EF6\u670D\u52A1\uFF09",
|
|
68
|
+
adapterHint: "\u82E5\u663E\u793A\u300C\u5185\u7F6E\u56DE\u9000\u300D\u4E14\u8D34\u56FE\u88AB\u62D2\uFF0C\u8BF7\u91CD\u542F dsh web \u8BA9\u65B0\u7248\u4E3B\u673A\u7AEF\u751F\u6548\u3002",
|
|
61
69
|
busy: "\u5904\u7406\u4E2D\u2026",
|
|
62
70
|
busyCatalog: "\u6B63\u5728\u5237\u65B0\u6A21\u578B\u76EE\u5F55\u2026",
|
|
63
71
|
busyUsage: "\u6B63\u5728\u5237\u65B0\u7528\u91CF\u2026",
|
|
@@ -87,7 +95,7 @@ var zh = {
|
|
|
87
95
|
var en = {
|
|
88
96
|
nav: "Grok Subscription",
|
|
89
97
|
title: "Grok Subscription",
|
|
90
|
-
subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 1.0.
|
|
98
|
+
subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 1.0.8",
|
|
91
99
|
signedIn: "Signed in",
|
|
92
100
|
signedOut: "Signed out",
|
|
93
101
|
account: "Account",
|
|
@@ -108,6 +116,13 @@ var en = {
|
|
|
108
116
|
loginHelp: "How signing in works",
|
|
109
117
|
refreshCatalog: "Refresh model catalog",
|
|
110
118
|
renderError: "Failed to render this panel",
|
|
119
|
+
diagnostics: "Active path",
|
|
120
|
+
adapterPiAi: "official pi-ai",
|
|
121
|
+
adapterFallback: "bundled fallback",
|
|
122
|
+
imageInput: "Image input",
|
|
123
|
+
imageInputOn: "available",
|
|
124
|
+
imageInputOff: "unavailable (host attachment service not resolved)",
|
|
125
|
+
adapterHint: 'If this says "bundled fallback" and images are rejected, restart dsh web so the new host bundle loads.',
|
|
111
126
|
busy: "Working\u2026",
|
|
112
127
|
busyCatalog: "Refreshing model catalog\u2026",
|
|
113
128
|
busyUsage: "Refreshing usage\u2026",
|
|
@@ -659,6 +674,19 @@ function Chip({ tone = "off", children }) {
|
|
|
659
674
|
children
|
|
660
675
|
] });
|
|
661
676
|
}
|
|
677
|
+
function DiagnosticsRows({ diagnostics, t }) {
|
|
678
|
+
if (!diagnostics) return null;
|
|
679
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsRows", children: [
|
|
680
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsRow", children: [
|
|
681
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("diagnostics") }),
|
|
682
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gsRowValue", children: diagnostics.adapter === "pi-ai" ? t("adapterPiAi") : t("adapterFallback") })
|
|
683
|
+
] }),
|
|
684
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsRow", children: [
|
|
685
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("imageInput") }),
|
|
686
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gsRowValue", children: diagnostics.imageInput ? t("imageInputOn") : t("imageInputOff") })
|
|
687
|
+
] })
|
|
688
|
+
] });
|
|
689
|
+
}
|
|
662
690
|
function UsagePanel({ usage, t, usageBusy, onRefresh, signedIn }) {
|
|
663
691
|
const ok = usage?.status === "ok";
|
|
664
692
|
const usedNumber = ok && Number.isFinite(usage.usedPercent) ? usage.usedPercent : void 0;
|
|
@@ -835,6 +863,8 @@ function GrokSubscriptionSection({ rpc, t }) {
|
|
|
835
863
|
error
|
|
836
864
|
] }) : null,
|
|
837
865
|
status?.cliAvailable === false ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--warn", children: t("cliMissing") }) : null,
|
|
866
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DiagnosticsRows, { diagnostics: status?.diagnostics, t }),
|
|
867
|
+
status?.diagnostics && !status.diagnostics.imageInput ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsHint", children: t("adapterHint") }) : null,
|
|
838
868
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("details", { className: "gsDisclosure", children: [
|
|
839
869
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("summary", { children: [
|
|
840
870
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Chevron, {}),
|
package/lib/index.js
CHANGED
|
@@ -389,7 +389,9 @@ function toLlmModels(models) {
|
|
|
389
389
|
provider: PROVIDER_ID,
|
|
390
390
|
id: model.id,
|
|
391
391
|
name: model.name,
|
|
392
|
-
|
|
392
|
+
// The bundled adapter inlines images through the host attachment store,
|
|
393
|
+
// so it advertises the same modality map as the official pi-ai path.
|
|
394
|
+
inputModalities: supportsImageInput(model.id) ? ["text", "image"] : ["text"]
|
|
393
395
|
};
|
|
394
396
|
const reasoning = reasoningInfoOf(model);
|
|
395
397
|
if (reasoning) info.reasoning = reasoning;
|
|
@@ -751,6 +753,17 @@ function hostModuleRoots(env = process.env, argv1 = process.argv[1]) {
|
|
|
751
753
|
}
|
|
752
754
|
const prefix = env.NPM_CONFIG_PREFIX;
|
|
753
755
|
if (typeof prefix === "string" && prefix.trim()) push(join2(prefix.trim(), "lib", "node_modules"));
|
|
756
|
+
if (typeof process.execPath === "string" && process.execPath) {
|
|
757
|
+
push(join2(dirname(process.execPath), "..", "lib", "node_modules"));
|
|
758
|
+
}
|
|
759
|
+
push(join2("/opt/homebrew", "lib", "node_modules"));
|
|
760
|
+
push(join2("/usr/local", "lib", "node_modules"));
|
|
761
|
+
for (const segment of ["Library/pnpm", ".local/share/pnpm", ".pnpm-global"]) {
|
|
762
|
+
push(join2(homedir2(), segment, "node_modules"));
|
|
763
|
+
}
|
|
764
|
+
if (typeof env.DSH_PORTABLE_HOME === "string" && env.DSH_PORTABLE_HOME.trim()) {
|
|
765
|
+
push(join2(env.DSH_PORTABLE_HOME.trim(), "node_modules"));
|
|
766
|
+
}
|
|
754
767
|
push(join2(homedir2(), ".local", "lib", "node_modules"));
|
|
755
768
|
const dshHome = typeof env.DSH_HOME === "string" && env.DSH_HOME.trim() ? env.DSH_HOME.trim() : join2(homedir2(), ".dsh");
|
|
756
769
|
push(join2(dshHome, "profiles", "node_modules"));
|
|
@@ -796,7 +809,13 @@ function resolveExportsTarget(exportsField, subpath) {
|
|
|
796
809
|
}
|
|
797
810
|
function resolveHostSpecifierManually(root, specifier) {
|
|
798
811
|
const { name: name2, subpath } = splitSpecifier(specifier);
|
|
799
|
-
|
|
812
|
+
const candidates = [
|
|
813
|
+
join2(root, name2),
|
|
814
|
+
join2(root, "node_modules", name2),
|
|
815
|
+
join2(root, "@deepseek-ai", "dsh", "node_modules", name2),
|
|
816
|
+
join2(root, "node_modules", "@deepseek-ai", "dsh", "node_modules", name2)
|
|
817
|
+
];
|
|
818
|
+
for (const pkgDir of candidates) {
|
|
800
819
|
const manifest = join2(pkgDir, "package.json");
|
|
801
820
|
if (!existsSync2(manifest)) continue;
|
|
802
821
|
let pkg;
|
|
@@ -858,7 +877,66 @@ function textOf(content) {
|
|
|
858
877
|
return "";
|
|
859
878
|
}).join("");
|
|
860
879
|
}
|
|
861
|
-
function
|
|
880
|
+
function imagePlaceholderText(ref) {
|
|
881
|
+
const name2 = typeof ref?.name === "string" && ref.name.trim() ? ` ${JSON.stringify(ref.name.trim())}` : "";
|
|
882
|
+
return `[attached image${name2} could not be included in this request]`;
|
|
883
|
+
}
|
|
884
|
+
async function collectImageParts(options, resolveAttachments, policy = {}) {
|
|
885
|
+
const parts = /* @__PURE__ */ new Map();
|
|
886
|
+
const refs = [];
|
|
887
|
+
for (const message of options.messages ?? []) {
|
|
888
|
+
for (const block of message?.content ?? []) {
|
|
889
|
+
if (block?.type !== "image" || !block.attachment) continue;
|
|
890
|
+
const id = block.attachment.attachmentId;
|
|
891
|
+
if (typeof id !== "string" || !id || parts.has(id)) continue;
|
|
892
|
+
parts.set(id, void 0);
|
|
893
|
+
refs.push(block.attachment);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (refs.length === 0) return parts;
|
|
897
|
+
let store;
|
|
898
|
+
try {
|
|
899
|
+
store = resolveAttachments?.();
|
|
900
|
+
} catch {
|
|
901
|
+
store = void 0;
|
|
902
|
+
}
|
|
903
|
+
if (!store?.readImageRequest) return parts;
|
|
904
|
+
const request = {
|
|
905
|
+
maxPixels: policy.maxPixels ?? REQUEST_IMAGE_PIXEL_BUDGET,
|
|
906
|
+
maxBytes: policy.maxBytes ?? REQUEST_IMAGE_MAX_BYTES
|
|
907
|
+
};
|
|
908
|
+
await Promise.all(refs.map(async (ref) => {
|
|
909
|
+
try {
|
|
910
|
+
const version = await store.readImageRequest(ref, request);
|
|
911
|
+
if (!version?.data || typeof version.mediaType !== "string" || !version.mediaType) return;
|
|
912
|
+
parts.set(ref.attachmentId, {
|
|
913
|
+
mediaType: version.mediaType,
|
|
914
|
+
base64: Buffer.from(version.data).toString("base64")
|
|
915
|
+
});
|
|
916
|
+
} catch {
|
|
917
|
+
}
|
|
918
|
+
}));
|
|
919
|
+
return parts;
|
|
920
|
+
}
|
|
921
|
+
function imageContentParts(blocks, imageParts) {
|
|
922
|
+
const parts = [];
|
|
923
|
+
const text = textOf(blocks);
|
|
924
|
+
if (text) parts.push({ type: "input_text", text });
|
|
925
|
+
for (const block of blocks ?? []) {
|
|
926
|
+
if (block?.type !== "image" || !block.attachment) continue;
|
|
927
|
+
const resolved = imageParts?.get(block.attachment.attachmentId);
|
|
928
|
+
if (resolved) {
|
|
929
|
+
parts.push({
|
|
930
|
+
type: "input_image",
|
|
931
|
+
image_url: `data:${resolved.mediaType};base64,${resolved.base64}`
|
|
932
|
+
});
|
|
933
|
+
} else {
|
|
934
|
+
parts.push({ type: "input_text", text: imagePlaceholderText(block.attachment) });
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
return parts;
|
|
938
|
+
}
|
|
939
|
+
function responsesInput(options, imageParts) {
|
|
862
940
|
const input = [];
|
|
863
941
|
const system = typeof options.system === "string" && options.system ? options.system : void 0;
|
|
864
942
|
if (system) input.push({ role: "system", content: system });
|
|
@@ -889,6 +967,12 @@ function responsesInput(options) {
|
|
|
889
967
|
if (text2) input.push({ role: "assistant", content: text2 });
|
|
890
968
|
continue;
|
|
891
969
|
}
|
|
970
|
+
const hasImage = (message.content ?? []).some((block) => block?.type === "image");
|
|
971
|
+
if (role === "user" && hasImage) {
|
|
972
|
+
const parts = imageContentParts(message.content, imageParts);
|
|
973
|
+
if (parts.length > 0) input.push({ role, content: parts });
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
892
976
|
const text = textOf(message.content);
|
|
893
977
|
if (!text) continue;
|
|
894
978
|
input.push({ role, content: text });
|
|
@@ -937,7 +1021,7 @@ async function* streamResponsesUnsafe(options, token) {
|
|
|
937
1021
|
}
|
|
938
1022
|
const body = {
|
|
939
1023
|
model: options.model,
|
|
940
|
-
input: responsesInput(options),
|
|
1024
|
+
input: responsesInput(options, await collectImageParts(options, options.resolveAttachments)),
|
|
941
1025
|
stream: true
|
|
942
1026
|
};
|
|
943
1027
|
if (typeof options.maxTokens === "number") body.max_output_tokens = options.maxTokens;
|
|
@@ -1138,7 +1222,7 @@ function visiblePiModels(session) {
|
|
|
1138
1222
|
if (session.publicAccount()?.signedIn !== true) return [];
|
|
1139
1223
|
return toPiModels(session.models()).map((model) => model.provider === PROVIDER_ID ? model : { ...model, provider: PROVIDER_ID });
|
|
1140
1224
|
}
|
|
1141
|
-
function createDuckAdapter(session) {
|
|
1225
|
+
function createDuckAdapter(session, adapterOptions = {}) {
|
|
1142
1226
|
const providerInfo = () => ({ id: PROVIDER_ID, name: DISPLAY_NAME });
|
|
1143
1227
|
const list = () => {
|
|
1144
1228
|
const signedIn = session.publicAccount()?.signedIn === true;
|
|
@@ -1160,7 +1244,9 @@ function createDuckAdapter(session) {
|
|
|
1160
1244
|
provider,
|
|
1161
1245
|
id: model,
|
|
1162
1246
|
name: found?.name ?? model,
|
|
1163
|
-
|
|
1247
|
+
// The host gates prompt attachment admission on resolveModelInfo, NOT on
|
|
1248
|
+
// listModels, so this must match the catalog's modality map exactly.
|
|
1249
|
+
inputModalities: supportsImageInput(model) ? ["text", "image"] : ["text"],
|
|
1164
1250
|
context: { contextWindow: raw?.contextWindow ?? 5e5 }
|
|
1165
1251
|
};
|
|
1166
1252
|
const reasoning = reasoningInfoOf(raw);
|
|
@@ -1180,6 +1266,7 @@ function createDuckAdapter(session) {
|
|
|
1180
1266
|
yield { type: "finish", reason: { kind: "error", failure: { message: "Unknown provider", code: "NO_ADAPTER" } } };
|
|
1181
1267
|
return;
|
|
1182
1268
|
}
|
|
1269
|
+
const callOptions = typeof adapterOptions.resolveAttachments === "function" ? { ...options, resolveAttachments: adapterOptions.resolveAttachments } : options;
|
|
1183
1270
|
let token = await session.currentToken();
|
|
1184
1271
|
if (!token) {
|
|
1185
1272
|
yield { type: "finish", reason: { kind: "error", failure: { message: "Grok subscription is not signed in", code: "MISSING_CREDENTIAL" } } };
|
|
@@ -1188,7 +1275,7 @@ function createDuckAdapter(session) {
|
|
|
1188
1275
|
for (let attempt = 0; ; attempt++) {
|
|
1189
1276
|
let emitted = false;
|
|
1190
1277
|
try {
|
|
1191
|
-
for await (const chunk of streamResponses(
|
|
1278
|
+
for await (const chunk of streamResponses(callOptions, token, callOptions.resolveAttachments)) {
|
|
1192
1279
|
emitted = true;
|
|
1193
1280
|
yield chunk;
|
|
1194
1281
|
}
|
|
@@ -1287,7 +1374,7 @@ function wrapAsHostAdapter(candidate, dshLlm) {
|
|
|
1287
1374
|
return new GrokBuildAdapter();
|
|
1288
1375
|
}
|
|
1289
1376
|
function createGrokBuildAdapterSync(session, options = {}) {
|
|
1290
|
-
const duck = createDuckAdapter(session);
|
|
1377
|
+
const duck = createDuckAdapter(session, { resolveAttachments: options.resolveAttachments });
|
|
1291
1378
|
const dshLlm = options.dshLlm;
|
|
1292
1379
|
return {
|
|
1293
1380
|
adapter: wrapAsHostAdapter(duck, dshLlm),
|
|
@@ -1302,7 +1389,7 @@ async function createGrokBuildAdapter(session, options = {}) {
|
|
|
1302
1389
|
optionalImport("@deepseek-ai/dsh-llm-pi-ai", importOpts),
|
|
1303
1390
|
optionalImport("@deepseek-ai/dsh-llm", importOpts)
|
|
1304
1391
|
]);
|
|
1305
|
-
const duck = createDuckAdapter(session);
|
|
1392
|
+
const duck = createDuckAdapter(session, { resolveAttachments: options.resolveAttachments });
|
|
1306
1393
|
const asHostAdapter = (candidate) => wrapAsHostAdapter(candidate, dshLlm);
|
|
1307
1394
|
if (!piAi?.createProvider || !dshPi?.PiAiAdapter) {
|
|
1308
1395
|
return {
|
|
@@ -1878,8 +1965,17 @@ function sanitizeUsage(usage) {
|
|
|
1878
1965
|
}
|
|
1879
1966
|
return out;
|
|
1880
1967
|
}
|
|
1881
|
-
async function dispatch(session, endpoint) {
|
|
1882
|
-
if (endpoint === "status")
|
|
1968
|
+
async function dispatch(session, endpoint, diagnostics) {
|
|
1969
|
+
if (endpoint === "status") {
|
|
1970
|
+
const status = await session.status();
|
|
1971
|
+
return publicResult(stripSecrets({
|
|
1972
|
+
...status,
|
|
1973
|
+
// Which adapter is actually serving this route: the host's official
|
|
1974
|
+
// pi-ai implementation or the bundled fallback. Diagnosing "model does
|
|
1975
|
+
// not support images" starts here.
|
|
1976
|
+
diagnostics: typeof diagnostics === "function" ? diagnostics() : diagnostics
|
|
1977
|
+
}));
|
|
1978
|
+
}
|
|
1883
1979
|
if (endpoint === "pull") return publicResult(stripSecrets(await session.pull()));
|
|
1884
1980
|
if (endpoint === "logout") return publicResult(stripSecrets(await session.logout()));
|
|
1885
1981
|
if (endpoint === "catalog/refresh") {
|
|
@@ -1902,7 +1998,7 @@ function createRpcHandler(session, options = {}) {
|
|
|
1902
1998
|
return async function handle(endpoint, _payload, _signal) {
|
|
1903
1999
|
try {
|
|
1904
2000
|
return await withTimeout(
|
|
1905
|
-
dispatch(session, endpoint),
|
|
2001
|
+
dispatch(session, endpoint, options.diagnostics),
|
|
1906
2002
|
timeoutMs,
|
|
1907
2003
|
`Grok subscription RPC "${endpoint}" timed out after ${timeoutMs}ms`,
|
|
1908
2004
|
{ unref: false }
|
|
@@ -2011,6 +2107,15 @@ function softService(ctx, key) {
|
|
|
2011
2107
|
}
|
|
2012
2108
|
return void 0;
|
|
2013
2109
|
}
|
|
2110
|
+
function attachmentResolver(ctx) {
|
|
2111
|
+
return () => {
|
|
2112
|
+
try {
|
|
2113
|
+
return typeof ctx.get === "function" ? ctx.get("attachments") : void 0;
|
|
2114
|
+
} catch {
|
|
2115
|
+
return void 0;
|
|
2116
|
+
}
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2014
2119
|
function apply(ctx, options = {}) {
|
|
2015
2120
|
let active = true;
|
|
2016
2121
|
if (typeof ctx.effect === "function") {
|
|
@@ -2038,11 +2143,21 @@ function apply(ctx, options = {}) {
|
|
|
2038
2143
|
logger: ctx.logger,
|
|
2039
2144
|
onCatalogChange: notifyCatalogChange
|
|
2040
2145
|
});
|
|
2041
|
-
const
|
|
2146
|
+
const adapterState = { kind: "custom-mvp", upgraded: false };
|
|
2147
|
+
const handler = createRpcHandler(session, {
|
|
2148
|
+
diagnostics: () => ({
|
|
2149
|
+
adapter: adapterState.upgraded ? "pi-ai" : "fallback",
|
|
2150
|
+
adapterKind: adapterState.kind,
|
|
2151
|
+
imageInput: typeof resolveAttachments()?.readImageRequest === "function",
|
|
2152
|
+
hostPeersResolved: adapterState.upgraded
|
|
2153
|
+
})
|
|
2154
|
+
});
|
|
2155
|
+
const resolveAttachments = attachmentResolver(ctx);
|
|
2042
2156
|
const createSync = options.createSync ?? createGrokBuildAdapterSync;
|
|
2043
2157
|
try {
|
|
2044
|
-
const created = createSync(session);
|
|
2158
|
+
const created = createSync(session, { resolveAttachments });
|
|
2045
2159
|
if (created.note) ctx.logger?.debug?.(created.note);
|
|
2160
|
+
adapterState.kind = created.kind;
|
|
2046
2161
|
ctx.llm.registerAdapter([PROVIDER_ID], created.adapter);
|
|
2047
2162
|
notifyCatalogChange();
|
|
2048
2163
|
} catch (error) {
|
|
@@ -2053,14 +2168,14 @@ function apply(ctx, options = {}) {
|
|
|
2053
2168
|
}
|
|
2054
2169
|
const defer = options.defer ?? scheduleDeferred2;
|
|
2055
2170
|
defer(() => {
|
|
2056
|
-
void deferredBoot(ctx, session, notifyCatalogChange, options);
|
|
2171
|
+
void deferredBoot(ctx, session, notifyCatalogChange, options, adapterState);
|
|
2057
2172
|
});
|
|
2058
2173
|
ctx.inject(["connection"], (connectionContext) => connectionContext.effect(
|
|
2059
2174
|
() => registerSubscriptionTransport(connectionContext.connection, handler),
|
|
2060
2175
|
"grok-subscription: host-only account RPC"
|
|
2061
2176
|
));
|
|
2062
2177
|
}
|
|
2063
|
-
async function deferredBoot(ctx, session, notifyCatalogChange, options = {}) {
|
|
2178
|
+
async function deferredBoot(ctx, session, notifyCatalogChange, options = {}, adapterState) {
|
|
2064
2179
|
if (!options.skipSettings) {
|
|
2065
2180
|
await tryRegisterSettings(ctx);
|
|
2066
2181
|
}
|
|
@@ -2087,18 +2202,15 @@ async function deferredBoot(ctx, session, notifyCatalogChange, options = {}) {
|
|
|
2087
2202
|
}
|
|
2088
2203
|
if (options.skipUpgrade) return;
|
|
2089
2204
|
const createAsync = options.createAsync ?? createGrokBuildAdapter;
|
|
2090
|
-
const resolveAttachments = () => {
|
|
2091
|
-
try {
|
|
2092
|
-
return typeof ctx.get === "function" ? ctx.get("attachments") : void 0;
|
|
2093
|
-
} catch {
|
|
2094
|
-
return void 0;
|
|
2095
|
-
}
|
|
2096
|
-
};
|
|
2097
2205
|
try {
|
|
2098
|
-
const created = await createAsync(session, { ...options.adapterOptions, resolveAttachments });
|
|
2206
|
+
const created = await createAsync(session, { ...options.adapterOptions, resolveAttachments: attachmentResolver(ctx) });
|
|
2099
2207
|
if (created.kind === "pi-ai") {
|
|
2100
2208
|
if (created.note) ctx.logger?.warn?.(created.note);
|
|
2101
2209
|
ctx.llm.registerAdapter([PROVIDER_ID], created.adapter);
|
|
2210
|
+
if (adapterState) {
|
|
2211
|
+
adapterState.kind = created.kind;
|
|
2212
|
+
adapterState.upgraded = true;
|
|
2213
|
+
}
|
|
2102
2214
|
notifyCatalogChange?.();
|
|
2103
2215
|
} else if (created.note) {
|
|
2104
2216
|
ctx.logger?.debug?.(created.note);
|