dsh-remote-bridge 2.2.3 → 2.2.4
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/docs/api/asyncapi.yaml +1 -1
- package/docs/api/openapi.yaml +1 -1
- package/lib/index.d.ts +967 -941
- package/lib/index.js +30 -16
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -389,7 +389,7 @@ var DshChatAdapter = class {
|
|
|
389
389
|
payload
|
|
390
390
|
}));
|
|
391
391
|
}
|
|
392
|
-
async prompt(principal, sessionId, parts, mode, clientTimeZone) {
|
|
392
|
+
async prompt(principal, sessionId, parts, mode, clientTimeZone, locale) {
|
|
393
393
|
await this.requireSession(principal, sessionId);
|
|
394
394
|
const hasText = parts.some((part) => part.type === "text" && part.text.trim() !== "");
|
|
395
395
|
const hasAttachment = parts.some((part) => part.type !== "text");
|
|
@@ -400,7 +400,8 @@ var DshChatAdapter = class {
|
|
|
400
400
|
sessionId,
|
|
401
401
|
mode,
|
|
402
402
|
content: parts,
|
|
403
|
-
...clientTimeZone === void 0 ? {} : { clientTimeZone }
|
|
403
|
+
...clientTimeZone === void 0 ? {} : { clientTimeZone },
|
|
404
|
+
...locale === void 0 ? {} : { locale }
|
|
404
405
|
}
|
|
405
406
|
}));
|
|
406
407
|
}
|
|
@@ -1956,18 +1957,29 @@ async function modelIsConfirmedTextOnly(host, sessionId) {
|
|
|
1956
1957
|
if (!Array.isArray(modalities)) return false;
|
|
1957
1958
|
return !modalities.includes("image");
|
|
1958
1959
|
}
|
|
1960
|
+
/** 从 `Accept-Language` 里挑一个我们支持的语种;认不出来就是中文。 */
|
|
1961
|
+
function handoffLocaleOf(acceptLanguage) {
|
|
1962
|
+
if (acceptLanguage === void 0) return "zh";
|
|
1963
|
+
for (const tag of acceptLanguage.split(",")) {
|
|
1964
|
+
const language = (tag.split(";")[0] ?? "").trim().toLowerCase();
|
|
1965
|
+
if (language.startsWith("zh")) return "zh";
|
|
1966
|
+
if (language.startsWith("en")) return "en";
|
|
1967
|
+
}
|
|
1968
|
+
return "zh";
|
|
1969
|
+
}
|
|
1959
1970
|
/**
|
|
1960
|
-
*
|
|
1961
|
-
*
|
|
1962
|
-
* 同一行路径要同时伺候两类读者:
|
|
1963
|
-
* - **给人看**(第一行):图片序号、原始文件名、工作目录里的短路径;
|
|
1964
|
-
* - **给机器看**(第二行 `[Image: source: <绝对路径>]`):这是读图技能明确
|
|
1965
|
-
* 识别的占位形状之一(带图片扩展名的绝对路径同样触发),任何按路径读图的
|
|
1966
|
-
* 工具都能接手,不出现任何具体插件名。
|
|
1971
|
+
* 交接说明的第一行(给人看的那行)。
|
|
1967
1972
|
*
|
|
1968
|
-
*
|
|
1973
|
+
* 写的是**人话**,不是挂在用户眼前的祈使句 —— 早期版本写「请先用读图工具打开下面那行的
|
|
1974
|
+
* 绝对路径」,那是写给机器看的措辞,用户看了的反馈是看不懂。现在一句话说清两件事:
|
|
1975
|
+
* 模型看不到图、图存在哪;要看图该怎么办。语言跟随界面(见 [handoffLocaleOf])。
|
|
1976
|
+
* 第二行的 `[Image: source: …]` 是机器契约,跨语言不变。
|
|
1969
1977
|
*/
|
|
1970
|
-
|
|
1978
|
+
function handoffLine(index, name, relative, locale) {
|
|
1979
|
+
if (locale === "en") return `🖼️ Image ${index} "${name}" (this model can't see images, saved to ${relative}; to view it, open the path below with an image-reading tool)`;
|
|
1980
|
+
return `🖼️ 图片 ${index}「${name}」(当前模型看不到图,已存到 ${relative};要看图,请用识图工具打开下面那行的路径)`;
|
|
1981
|
+
}
|
|
1982
|
+
async function handoffImagesForTextOnlyModel(host, roots, fallbackDir, locale, sessionId, content) {
|
|
1971
1983
|
if (!content.some((part) => part.type === "image")) return content;
|
|
1972
1984
|
try {
|
|
1973
1985
|
if (!await modelIsConfirmedTextOnly(host, sessionId)) return content;
|
|
@@ -1978,10 +1990,10 @@ async function handoffImagesForTextOnlyModel(host, roots, fallbackDir, sessionId
|
|
|
1978
1990
|
if (part.type !== "image") return part;
|
|
1979
1991
|
index += 1;
|
|
1980
1992
|
const { absolute, relative } = writeHandoffFile(dir, workspace, part.mediaType, part.data);
|
|
1981
|
-
const name = typeof part.name === "string" && part.name.trim() !== "" ? part.name.trim() : `图片 ${index}`;
|
|
1993
|
+
const name = typeof part.name === "string" && part.name.trim() !== "" ? part.name.trim() : locale === "en" ? `image ${index}` : `图片 ${index}`;
|
|
1982
1994
|
return {
|
|
1983
1995
|
type: "text",
|
|
1984
|
-
text: [
|
|
1996
|
+
text: [handoffLine(index, name, relative, locale), `[Image: source: ${absolute}]`].join("\n")
|
|
1985
1997
|
};
|
|
1986
1998
|
});
|
|
1987
1999
|
} catch {
|
|
@@ -2106,15 +2118,16 @@ function createDsh02ApiProxy(host, options = {}) {
|
|
|
2106
2118
|
}),
|
|
2107
2119
|
prompt: (request) => call(async () => {
|
|
2108
2120
|
const requestId = randomUUID();
|
|
2121
|
+
const { locale, ...kernelPayload } = request.payload;
|
|
2109
2122
|
const receiptIds = request.payload.content.filter((part) => part.type === "file").map((part) => part.receiptId);
|
|
2110
|
-
const content = imageHandoff ? await handoffImagesForTextOnlyModel(host, handoffRoots, imageHandoffDir, request.payload.sessionId, request.payload.content) : request.payload.content;
|
|
2123
|
+
const content = imageHandoff ? await handoffImagesForTextOnlyModel(host, handoffRoots, imageHandoffDir, locale ?? "zh", request.payload.sessionId, request.payload.content) : request.payload.content;
|
|
2111
2124
|
const binding = receiptIds.length === 0 ? void 0 : await (async () => {
|
|
2112
2125
|
if (fileUploadService == null) throw new Error("FILE_UPLOADS_UNAVAILABLE: this DSH build provides no ctx.fileUploads service, so file attachments cannot be sent");
|
|
2113
2126
|
return fileUploadService.bindPrompt(await agentFor(request.payload.sessionId), receiptIds, requestId);
|
|
2114
2127
|
})();
|
|
2115
2128
|
await host.sessionController.prompt({
|
|
2116
2129
|
requestId,
|
|
2117
|
-
...
|
|
2130
|
+
...kernelPayload,
|
|
2118
2131
|
content
|
|
2119
2132
|
}, new AbortController().signal);
|
|
2120
2133
|
binding?.commit();
|
|
@@ -3356,7 +3369,8 @@ var ApiRouter = class {
|
|
|
3356
3369
|
}
|
|
3357
3370
|
const parts = readPromptParts(body);
|
|
3358
3371
|
const mode = body.mode === "steer" ? "steer" : "queue";
|
|
3359
|
-
const
|
|
3372
|
+
const acceptLanguage = req.headers["accept-language"];
|
|
3373
|
+
const response = { message: await this.options.chat.prompt(principal, sessionId, parts, mode, typeof body.clientTimeZone === "string" ? body.clientTimeZone : void 0, handoffLocaleOf(Array.isArray(acceptLanguage) ? acceptLanguage[0] : acceptLanguage)) };
|
|
3360
3374
|
this.saveIdempotent(principal, body.clientRequestId, 202, response);
|
|
3361
3375
|
this.audit(principal, "chat.message.submit", void 0, void 0, requestId, {
|
|
3362
3376
|
sessionId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-remote-bridge",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.4",
|
|
4
4
|
"description": "Versioned REST bridge for DeepSeek Harness: remote access for trusted clients (chat, files, devices) plus a Phone-settings panel inside the DSH WebUI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@11.19.0",
|