Skip to content

Commit 7dee489

Browse files
committed
feat: support stream output
1 parent a747e94 commit 7dee489

File tree

4 files changed

+84
-35
lines changed

4 files changed

+84
-35
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ ChatGPT 向我们展示了 GPT 模型的伟大之处,所以我使用 OpenAI
4646

4747
## 使用方法
4848

49-
1. 安装 [Bob](https://bobtranslate.com/guide/#%E5%AE%89%E8%A3%85) (版本 >= 0.50),一款 macOS 平台的翻译和 OCR 软件
49+
1. 安装 [Bob](https://bobtranslate.com/guide/#%E5%AE%89%E8%A3%85),一款 macOS 平台的翻译和 OCR 软件;[openai-translator.bobplugin](https://github.com/yetone/bob-plugin-openai-translator/releases/latest) >= 0.50 以后默认开启流式输出,需要 Bob 版本 >= 1.8.0
5050

5151
2. 下载此插件: [openai-translator.bobplugin](https://github.com/yetone/bob-plugin-openai-translator/releases/latest)
5252

docs/README_EN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ To use the ChatGPT API, go to Bob's settings page and change the plugin model to
4848

4949
## Usage
5050

51-
1. Install [Bob](https://bobtranslate.com/guide/#%E5%AE%89%E8%A3%85) (version >= 0.50), a macOS translation and OCR software
51+
1. Install [Bob](https://bobtranslate.com/guide/#%E5%AE%89%E8%A3%85), a translation and OCR software for macOS platform; [openai-translator.bobplugin](https://github.com/yetone/bob-plugin-openai-translator/releases/latest) >= 0.50 enables streaming output by default after installation, requiring Bob version >= 1.8.0.
5252

5353
2. Download this plugin: [openai-translator.bobplugin](https://github.com/yetone/bob-plugin-openai-translator/releases/latest)
5454

global.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ declare namespace Bob {
263263
body?: any;
264264
files?: HttpRequestFiles;
265265
handler?: (resp: HttpResponse) => void;
266+
cancelSignal?: Signal;
266267
streamHandler?: (stream: { text: string, rawData: Data }) => void
267268
timeout?: number;
268269
}

src/main.js

Lines changed: 81 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ function buildRequestBody(model, isChatGPTModel, query) {
127127

128128
const standardBody = {
129129
model,
130+
stream: true,
130131
temperature: 0.2,
131132
max_tokens: 1000,
132133
top_p: 1,
@@ -170,40 +171,57 @@ function handleError(query, result) {
170171
error: {
171172
type: reason,
172173
message: `接口响应错误 - ${result.data.error.message}`,
173-
addtion: JSON.stringify(result),
174+
addtion: `${statusCode}: ${JSON.stringify(result)}`,
174175
},
175176
});
176177
}
177178

179+
178180
/**
179-
* @param {boolean} isChatGPTModel
180181
* @param {Bob.TranslateQuery} query
181-
* @param {Bob.HttpResponse} result
182-
* @returns {void}
182+
* @param {boolean} isChatGPTModel
183+
* @param {string} targetText
184+
* @param {string} textFromResponse
185+
* @returns {string}
183186
*/
184-
function handleResponse(isChatGPTModel, query, result) {
185-
const { choices } = result.data;
187+
function handleResponse(query, isChatGPTModel, targetText, textFromResponse) {
188+
if (textFromResponse !== '[DONE]') {
189+
try {
190+
const dataObj = JSON.parse(textFromResponse);
191+
const { choices } = dataObj;
192+
if (!choices || choices.length === 0) {
193+
query.onCompletion({
194+
error: {
195+
type: "api",
196+
message: "接口未返回结果",
197+
addtion: textFromResponse,
198+
},
199+
});
200+
return targetText;
201+
}
186202

187-
if (!choices || choices.length === 0) {
188-
query.onCompletion({
189-
error: {
190-
type: "api",
191-
message: "接口未返回结果",
192-
addtion: JSON.stringify(result),
193-
},
194-
});
195-
return;
203+
const content = isChatGPTModel ? choices[0].delta.content : choices[0].text;
204+
if (content !== undefined) {
205+
targetText += content;
206+
query.onStream({
207+
result: {
208+
from: query.detectFrom,
209+
to: query.detectTo,
210+
toParagraphs: [targetText],
211+
},
212+
});
213+
}
214+
} catch (err) {
215+
query.onCompletion({
216+
error: {
217+
type: err._type || "param",
218+
message: err._message || "Failed to parse JSON",
219+
addtion: err._addition,
220+
},
221+
});
222+
}
196223
}
197-
198-
let targetText = (isChatGPTModel ? choices[0].message.content : choices[0].text).trim();
199-
200-
query.onCompletion({
201-
result: {
202-
from: query.detectFrom,
203-
to: query.detectTo,
204-
toParagraphs: targetText.split("\n"),
205-
},
206-
});
224+
return targetText;
207225
}
208226

209227
/**
@@ -258,20 +276,50 @@ function translate(query) {
258276

259277
const header = buildHeader(isAzureServiceProvider, apiKey);
260278
const body = buildRequestBody(model, isChatGPTModel, query);
261-
279+
280+
// 初始化拼接结果变量
281+
let targetText = "";
262282
(async () => {
263-
const result = await $http.request({
283+
await $http.streamRequest({
264284
method: "POST",
265285
url: modifiedApiUrl + apiUrlPath,
266286
header,
267287
body,
288+
cancelSignal: query.cancelSignal,
289+
streamHandler: (streamData) => {
290+
if (streamData.text.includes("Invalid token")) {
291+
query.onCompletion({
292+
error: {
293+
type: "secretKey",
294+
message: "配置错误 - 请确保您在插件配置中填入了正确的 API Keys",
295+
addtion: "请在插件配置中填写正确的 API Keys",
296+
},
297+
});
298+
} else {
299+
const lines = streamData.text.split('\n').filter(line => line);
300+
lines.forEach(line => {
301+
const match = line.match(/^data: (.*)/);
302+
if (match) {
303+
const textFromResponse = match[1].trim();
304+
targetText = handleResponse(query, isChatGPTModel, targetText, textFromResponse);
305+
}
306+
});
307+
}
308+
},
309+
handler: (result) => {
310+
if (result.response.statusCode >= 400) {
311+
handleError(query, result);
312+
} else {
313+
query.onCompletion({
314+
result: {
315+
from: query.detectFrom,
316+
to: query.detectTo,
317+
toParagraphs: [targetText],
318+
},
319+
});
320+
}
321+
}
268322
});
269-
270-
if (result.error) {
271-
handleError(query, result);
272-
} else {
273-
handleResponse(isChatGPTModel, query, result);
274-
}
275323
})().catch((err) => {
276324
query.onCompletion({
277325
error: {

0 commit comments

Comments
 (0)