dsh-grok-subscription 1.0.6 → 1.0.7
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 +133 -23
- 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.7`.
|
|
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.7
|
|
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.7`。
|
|
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.7
|
|
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.7",
|
|
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.7",
|
|
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;
|
|
@@ -1180,6 +1264,7 @@ function createDuckAdapter(session) {
|
|
|
1180
1264
|
yield { type: "finish", reason: { kind: "error", failure: { message: "Unknown provider", code: "NO_ADAPTER" } } };
|
|
1181
1265
|
return;
|
|
1182
1266
|
}
|
|
1267
|
+
const callOptions = typeof adapterOptions.resolveAttachments === "function" ? { ...options, resolveAttachments: adapterOptions.resolveAttachments } : options;
|
|
1183
1268
|
let token = await session.currentToken();
|
|
1184
1269
|
if (!token) {
|
|
1185
1270
|
yield { type: "finish", reason: { kind: "error", failure: { message: "Grok subscription is not signed in", code: "MISSING_CREDENTIAL" } } };
|
|
@@ -1188,7 +1273,7 @@ function createDuckAdapter(session) {
|
|
|
1188
1273
|
for (let attempt = 0; ; attempt++) {
|
|
1189
1274
|
let emitted = false;
|
|
1190
1275
|
try {
|
|
1191
|
-
for await (const chunk of streamResponses(
|
|
1276
|
+
for await (const chunk of streamResponses(callOptions, token, callOptions.resolveAttachments)) {
|
|
1192
1277
|
emitted = true;
|
|
1193
1278
|
yield chunk;
|
|
1194
1279
|
}
|
|
@@ -1287,7 +1372,7 @@ function wrapAsHostAdapter(candidate, dshLlm) {
|
|
|
1287
1372
|
return new GrokBuildAdapter();
|
|
1288
1373
|
}
|
|
1289
1374
|
function createGrokBuildAdapterSync(session, options = {}) {
|
|
1290
|
-
const duck = createDuckAdapter(session);
|
|
1375
|
+
const duck = createDuckAdapter(session, { resolveAttachments: options.resolveAttachments });
|
|
1291
1376
|
const dshLlm = options.dshLlm;
|
|
1292
1377
|
return {
|
|
1293
1378
|
adapter: wrapAsHostAdapter(duck, dshLlm),
|
|
@@ -1302,7 +1387,7 @@ async function createGrokBuildAdapter(session, options = {}) {
|
|
|
1302
1387
|
optionalImport("@deepseek-ai/dsh-llm-pi-ai", importOpts),
|
|
1303
1388
|
optionalImport("@deepseek-ai/dsh-llm", importOpts)
|
|
1304
1389
|
]);
|
|
1305
|
-
const duck = createDuckAdapter(session);
|
|
1390
|
+
const duck = createDuckAdapter(session, { resolveAttachments: options.resolveAttachments });
|
|
1306
1391
|
const asHostAdapter = (candidate) => wrapAsHostAdapter(candidate, dshLlm);
|
|
1307
1392
|
if (!piAi?.createProvider || !dshPi?.PiAiAdapter) {
|
|
1308
1393
|
return {
|
|
@@ -1878,8 +1963,17 @@ function sanitizeUsage(usage) {
|
|
|
1878
1963
|
}
|
|
1879
1964
|
return out;
|
|
1880
1965
|
}
|
|
1881
|
-
async function dispatch(session, endpoint) {
|
|
1882
|
-
if (endpoint === "status")
|
|
1966
|
+
async function dispatch(session, endpoint, diagnostics) {
|
|
1967
|
+
if (endpoint === "status") {
|
|
1968
|
+
const status = await session.status();
|
|
1969
|
+
return publicResult(stripSecrets({
|
|
1970
|
+
...status,
|
|
1971
|
+
// Which adapter is actually serving this route: the host's official
|
|
1972
|
+
// pi-ai implementation or the bundled fallback. Diagnosing "model does
|
|
1973
|
+
// not support images" starts here.
|
|
1974
|
+
diagnostics: typeof diagnostics === "function" ? diagnostics() : diagnostics
|
|
1975
|
+
}));
|
|
1976
|
+
}
|
|
1883
1977
|
if (endpoint === "pull") return publicResult(stripSecrets(await session.pull()));
|
|
1884
1978
|
if (endpoint === "logout") return publicResult(stripSecrets(await session.logout()));
|
|
1885
1979
|
if (endpoint === "catalog/refresh") {
|
|
@@ -1902,7 +1996,7 @@ function createRpcHandler(session, options = {}) {
|
|
|
1902
1996
|
return async function handle(endpoint, _payload, _signal) {
|
|
1903
1997
|
try {
|
|
1904
1998
|
return await withTimeout(
|
|
1905
|
-
dispatch(session, endpoint),
|
|
1999
|
+
dispatch(session, endpoint, options.diagnostics),
|
|
1906
2000
|
timeoutMs,
|
|
1907
2001
|
`Grok subscription RPC "${endpoint}" timed out after ${timeoutMs}ms`,
|
|
1908
2002
|
{ unref: false }
|
|
@@ -2011,6 +2105,15 @@ function softService(ctx, key) {
|
|
|
2011
2105
|
}
|
|
2012
2106
|
return void 0;
|
|
2013
2107
|
}
|
|
2108
|
+
function attachmentResolver(ctx) {
|
|
2109
|
+
return () => {
|
|
2110
|
+
try {
|
|
2111
|
+
return typeof ctx.get === "function" ? ctx.get("attachments") : void 0;
|
|
2112
|
+
} catch {
|
|
2113
|
+
return void 0;
|
|
2114
|
+
}
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2014
2117
|
function apply(ctx, options = {}) {
|
|
2015
2118
|
let active = true;
|
|
2016
2119
|
if (typeof ctx.effect === "function") {
|
|
@@ -2038,11 +2141,21 @@ function apply(ctx, options = {}) {
|
|
|
2038
2141
|
logger: ctx.logger,
|
|
2039
2142
|
onCatalogChange: notifyCatalogChange
|
|
2040
2143
|
});
|
|
2041
|
-
const
|
|
2144
|
+
const adapterState = { kind: "custom-mvp", upgraded: false };
|
|
2145
|
+
const handler = createRpcHandler(session, {
|
|
2146
|
+
diagnostics: () => ({
|
|
2147
|
+
adapter: adapterState.upgraded ? "pi-ai" : "fallback",
|
|
2148
|
+
adapterKind: adapterState.kind,
|
|
2149
|
+
imageInput: typeof resolveAttachments()?.readImageRequest === "function",
|
|
2150
|
+
hostPeersResolved: adapterState.upgraded
|
|
2151
|
+
})
|
|
2152
|
+
});
|
|
2153
|
+
const resolveAttachments = attachmentResolver(ctx);
|
|
2042
2154
|
const createSync = options.createSync ?? createGrokBuildAdapterSync;
|
|
2043
2155
|
try {
|
|
2044
|
-
const created = createSync(session);
|
|
2156
|
+
const created = createSync(session, { resolveAttachments });
|
|
2045
2157
|
if (created.note) ctx.logger?.debug?.(created.note);
|
|
2158
|
+
adapterState.kind = created.kind;
|
|
2046
2159
|
ctx.llm.registerAdapter([PROVIDER_ID], created.adapter);
|
|
2047
2160
|
notifyCatalogChange();
|
|
2048
2161
|
} catch (error) {
|
|
@@ -2053,14 +2166,14 @@ function apply(ctx, options = {}) {
|
|
|
2053
2166
|
}
|
|
2054
2167
|
const defer = options.defer ?? scheduleDeferred2;
|
|
2055
2168
|
defer(() => {
|
|
2056
|
-
void deferredBoot(ctx, session, notifyCatalogChange, options);
|
|
2169
|
+
void deferredBoot(ctx, session, notifyCatalogChange, options, adapterState);
|
|
2057
2170
|
});
|
|
2058
2171
|
ctx.inject(["connection"], (connectionContext) => connectionContext.effect(
|
|
2059
2172
|
() => registerSubscriptionTransport(connectionContext.connection, handler),
|
|
2060
2173
|
"grok-subscription: host-only account RPC"
|
|
2061
2174
|
));
|
|
2062
2175
|
}
|
|
2063
|
-
async function deferredBoot(ctx, session, notifyCatalogChange, options = {}) {
|
|
2176
|
+
async function deferredBoot(ctx, session, notifyCatalogChange, options = {}, adapterState) {
|
|
2064
2177
|
if (!options.skipSettings) {
|
|
2065
2178
|
await tryRegisterSettings(ctx);
|
|
2066
2179
|
}
|
|
@@ -2087,18 +2200,15 @@ async function deferredBoot(ctx, session, notifyCatalogChange, options = {}) {
|
|
|
2087
2200
|
}
|
|
2088
2201
|
if (options.skipUpgrade) return;
|
|
2089
2202
|
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
2203
|
try {
|
|
2098
|
-
const created = await createAsync(session, { ...options.adapterOptions, resolveAttachments });
|
|
2204
|
+
const created = await createAsync(session, { ...options.adapterOptions, resolveAttachments: attachmentResolver(ctx) });
|
|
2099
2205
|
if (created.kind === "pi-ai") {
|
|
2100
2206
|
if (created.note) ctx.logger?.warn?.(created.note);
|
|
2101
2207
|
ctx.llm.registerAdapter([PROVIDER_ID], created.adapter);
|
|
2208
|
+
if (adapterState) {
|
|
2209
|
+
adapterState.kind = created.kind;
|
|
2210
|
+
adapterState.upgraded = true;
|
|
2211
|
+
}
|
|
2102
2212
|
notifyCatalogChange?.();
|
|
2103
2213
|
} else if (created.note) {
|
|
2104
2214
|
ctx.logger?.debug?.(created.note);
|