quickcommand.askAI支持流式响应,支持使用默认api配置

This commit is contained in:
fofolee 2025-02-21 11:31:45 +08:00
parent 0090442101
commit 1e5a4c1ff5
7 changed files with 143 additions and 265 deletions

View File

@ -72,7 +72,7 @@ function buildRequestConfig(apiConfig) {
} }
// 构建请求数据 // 构建请求数据
function buildRequestData(content, apiConfig, stream = false) { function buildRequestData(content, apiConfig) {
const { model } = apiConfig; const { model } = apiConfig;
const { prompt, role, context = [] } = content; const { prompt, role, context = [] } = content;
const rolePrompt = ROLE_PROMPTS[role] || role; const rolePrompt = ROLE_PROMPTS[role] || role;
@ -105,7 +105,7 @@ function buildRequestData(content, apiConfig, stream = false) {
return { return {
model, model,
messages, messages,
stream, stream: true,
}; };
} }
@ -222,7 +222,7 @@ async function handleStreamResponse(response, apiConfig, controller, onStream) {
reader.releaseLock(); reader.releaseLock();
} }
return { success: true, result: "流式请求完成" }; return { success: true };
} }
/** /**
@ -234,28 +234,24 @@ async function handleStreamResponse(response, apiConfig, controller, onStream) {
*/ */
async function chat(content, apiConfig, options = {}) { async function chat(content, apiConfig, options = {}) {
try { try {
const { showLoadingBar = true, stream = false, onStream } = options; const { showProcessBar = true, onStream = () => {} } = options;
// 验证必要参数 // 验证必要参数
if (!apiConfig.apiUrl || !content.prompt || !apiConfig.model) { if (!apiConfig.apiUrl || !content.prompt || !apiConfig.model) {
throw new Error("API地址、模型名称和提示词不能为空"); throw new Error("API地址、模型名称和提示词不能为空");
} }
if (stream && !onStream) {
throw new Error("使用流式请求时必须提供onStream回调函数");
}
// 构建请求URL和配置 // 构建请求URL和配置
const url = buildApiUrl( const url = buildApiUrl(
apiConfig.apiUrl, apiConfig.apiUrl,
API_ENDPOINTS[apiConfig.apiType].chat API_ENDPOINTS[apiConfig.apiType].chat
); );
const config = buildRequestConfig(apiConfig, stream); const config = buildRequestConfig(apiConfig);
const requestData = buildRequestData(content, apiConfig, stream); const requestData = buildRequestData(content, apiConfig);
// 显示加载 // 显示进度
const loadingBar = showLoadingBar const processBar = showProcessBar
? await quickcommand.showLoadingBar({ ? await quickcommand.showProcessBar({
text: "AI思考中...", text: "AI思考中...",
onClose: () => { onClose: () => {
if (controller) { if (controller) {
@ -265,6 +261,26 @@ async function chat(content, apiConfig, options = {}) {
}) })
: null; : null;
// 用于收集完整响应
let fullResponse = "";
// 包装 onStream 回调以收集完整响应并更新进度条
const streamHandler = (chunk, controller, isDone) => {
if (!isDone) {
fullResponse += chunk;
// 更新进度条显示最新的响应内容
if (processBar) {
quickcommand.updateProcessBar(
{
text: fullResponse, // 只显示最后100个字符避免内容过长
},
processBar
);
}
}
onStream(chunk, controller, isDone);
};
// 统一使用 fetch 处理请求 // 统一使用 fetch 处理请求
const controller = new AbortController(); const controller = new AbortController();
const response = await fetch(url, { const response = await fetch(url, {
@ -278,24 +294,35 @@ async function chat(content, apiConfig, options = {}) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
let result; const result = await handleStreamResponse(
if (stream) { response,
result = await handleStreamResponse( apiConfig,
response, controller,
apiConfig, streamHandler
controller, );
onStream
); // 如果请求被取消,返回取消状态
} else { if (!result.success) {
const responseData = await response.json(); processBar?.close();
result = { return result;
success: true,
result: parseResponse({ data: responseData }, apiConfig.apiType),
};
} }
loadingBar?.close(); // 完成时更新进度条并关闭
return result; if (processBar) {
quickcommand.updateProcessBar(
{
text: "AI响应完成",
complete: true,
},
processBar
);
}
// 返回完整的响应内容
return {
success: true,
result: fullResponse,
};
} catch (error) { } catch (error) {
if (error.name === "AbortError") { if (error.name === "AbortError") {
return { return {

View File

@ -305,7 +305,7 @@ let lastProcessBar = null;
* @param {object} options - 配置选项 * @param {object} options - 配置选项
* @param {string} [options.title="进度"] - 对话框标题 * @param {string} [options.title="进度"] - 对话框标题
* @param {string} [options.text="处理中..."] - 进度条上方的文本 * @param {string} [options.text="处理中..."] - 进度条上方的文本
* @param {number} [options.value=0] - 初始进度值(0-100) * @param {number} [options.value] - 初始进度值(0-100)不传则显示加载动画
* @param {string} [options.position="bottom-right"] - 进度条位置可选值top-left, top-right, bottom-left, bottom-right * @param {string} [options.position="bottom-right"] - 进度条位置可选值top-left, top-right, bottom-left, bottom-right
* @param {Function} [options.onClose] - 关闭按钮点击时的回调函数 * @param {Function} [options.onClose] - 关闭按钮点击时的回调函数
* @param {Function} [options.onPause] - 暂停按钮点击时的回调函数 * @param {Function} [options.onPause] - 暂停按钮点击时的回调函数
@ -316,7 +316,7 @@ let lastProcessBar = null;
const showProcessBar = async (options = {}) => { const showProcessBar = async (options = {}) => {
const { const {
text = "处理中...", text = "处理中...",
value = 0, value,
position = "bottom-right", position = "bottom-right",
onClose, onClose,
onPause, onPause,
@ -328,7 +328,7 @@ const showProcessBar = async (options = {}) => {
throw new Error("onPause 和 onResume 必须同时配置"); throw new Error("onPause 和 onResume 必须同时配置");
} }
const windowWidth = 350; const windowWidth = 250;
const windowHeight = 60; const windowHeight = 60;
// 计算窗口位置 // 计算窗口位置
@ -357,10 +357,11 @@ const showProcessBar = async (options = {}) => {
ipcRenderer.sendTo(windowId, "dialog-config", { ipcRenderer.sendTo(windowId, "dialog-config", {
type: "process", type: "process",
text, text,
value, value: value === undefined ? 0 : value,
isDark: utools.isDarkColors(), isDark: utools.isDarkColors(),
platform: process.platform, platform: process.platform,
showPause: Boolean(onPause && onResume), showPause: Boolean(onPause && onResume),
isLoading: value === undefined, // 当不传value时显示加载动画
}); });
// 监听窗口准备就绪 // 监听窗口准备就绪
@ -410,7 +411,7 @@ const showProcessBar = async (options = {}) => {
/** /**
* 更新进度条的进度 * 更新进度条的进度
* @param {object} options - 配置选项 * @param {object} options - 配置选项
* @param {number} options.value - 新的进度值(0-100) * @param {number} [options.value] - 新的进度值(0-100)不传则显示加载动画
* @param {string} [options.text] - 新的进度文本 * @param {string} [options.text] - 新的进度文本
* @param {boolean} [options.complete] - 是否完成并关闭进度条 * @param {boolean} [options.complete] - 是否完成并关闭进度条
* @param {{id: number, close: Function}|undefined} processBar - 进度条对象, 如果不传入则使用上一次创建的进度条 * @param {{id: number, close: Function}|undefined} processBar - 进度条对象, 如果不传入则使用上一次创建的进度条
@ -433,7 +434,11 @@ const updateProcessBar = (options = {}, processBar = null) => {
} }
const { value, text, complete } = options; const { value, text, complete } = options;
ipcRenderer.sendTo(processBar.id, "update-process", { value, text }); ipcRenderer.sendTo(processBar.id, "update-process", {
value,
text,
isLoading: value === undefined,
});
if (complete) { if (complete) {
setTimeout(() => { setTimeout(() => {
@ -442,97 +447,6 @@ const updateProcessBar = (options = {}, processBar = null) => {
} }
}; };
let lastLoadingBar = null;
/**
* 显示一个加载条对话框
* @param {object} options - 配置选项
* @param {string} [options.text="加载中..."] - 加载条上方的文本
* @param {string} [options.position="bottom-right"] - 加载条位置可选值top-left, top-right, bottom-left, bottom-right
* @param {Function} [options.onClose] - 关闭按钮点击时的回调函数
* @returns {Promise<{id: number, close: Function}>} 返回加载条窗口ID和关闭函数
*/
const showLoadingBar = async (options = {}) => {
const { text = "加载中...", position = "bottom-right", onClose } = options;
const windowWidth = 250;
const windowHeight = 60;
// 计算窗口位置
const { x, y } = calculateWindowPosition({
position,
width: windowWidth,
height: windowHeight,
});
return new Promise((resolve) => {
const UBrowser = createBrowserWindow(
dialogPath,
{
width: windowWidth,
height: windowHeight,
x,
y,
opacity: 0,
focusable: false,
...commonBrowserWindowOptions,
},
() => {
const windowId = UBrowser.webContents.id;
// 发送配置到子窗口
ipcRenderer.sendTo(windowId, "dialog-config", {
type: "process",
text,
value: 0,
isDark: utools.isDarkColors(),
platform: process.platform,
isLoading: true, // 标记为加载条模式
});
// 监听窗口准备就绪
ipcRenderer.once("dialog-ready", () => {
UBrowser.setOpacity(1);
});
// 监听对话框结果
ipcRenderer.once("dialog-result", (event, result) => {
if (result === "close" && typeof onClose === "function") {
onClose();
}
UBrowser.destroy();
});
const loadingBar = {
id: windowId,
close: () => {
if (typeof onClose === "function") {
onClose();
}
lastLoadingBar = null;
UBrowser.destroy();
},
};
lastLoadingBar = loadingBar;
// 返回窗口ID和关闭函数
resolve(loadingBar);
}
);
});
};
const closeLoadingBar = (loadingBar) => {
if (!loadingBar) {
if (!lastLoadingBar) {
throw new Error("没有找到已创建的加载条");
}
loadingBar = lastLoadingBar;
}
loadingBar.close();
};
module.exports = { module.exports = {
showSystemMessageBox, showSystemMessageBox,
showSystemInputBox, showSystemInputBox,
@ -543,6 +457,4 @@ module.exports = {
showSystemWaitButton, showSystemWaitButton,
showProcessBar, showProcessBar,
updateProcessBar, updateProcessBar,
showLoadingBar,
closeLoadingBar,
}; };

View File

@ -8,6 +8,8 @@ const axios = require("axios");
const marked = require("marked"); const marked = require("marked");
const { chat, getModels } = require("./ai"); const { chat, getModels } = require("./ai");
const { dbStorage } = utools;
window.getModelsFromAiApi = getModels; window.getModelsFromAiApi = getModels;
const systemDialog = require("./dialog/service"); const systemDialog = require("./dialog/service");
@ -197,6 +199,9 @@ const quickcommand = {
}, },
askAI: async function (content, apiConfig, options) { askAI: async function (content, apiConfig, options) {
if (window.lodashM.isEmpty(apiConfig)) {
apiConfig = dbStorage.getItem("cfg_aiConfigs")?.[0] || {};
}
return await chat(content, apiConfig, options); return await chat(content, apiConfig, options);
}, },

View File

@ -8,7 +8,7 @@ const stopMonitor = () => {
// 监控剪贴板变化 // 监控剪贴板变化
const watchClipboard = async function () { const watchClipboard = async function () {
const args = ["-type", "clipboard", "-once"]; const args = ["-type", "clipboard", "-once"];
const loadingBar = await quickcommand.showLoadingBar({ const loadingBar = await quickcommand.showProcessBar({
text: "等待剪贴板变化...", text: "等待剪贴板变化...",
onClose: () => { onClose: () => {
stopMonitor(); stopMonitor();
@ -44,7 +44,7 @@ const watchFileSystem = async function (watchPath, options = {}) {
args.push("-recursive", "false"); args.push("-recursive", "false");
} }
const loadingBar = await quickcommand.showLoadingBar({ const loadingBar = await quickcommand.showProcessBar({
text: "等待文件变化...", text: "等待文件变化...",
onClose: () => { onClose: () => {
stopMonitor(); stopMonitor();

View File

@ -91,7 +91,9 @@
:icon="streamingResponse ? 'stop' : 'send'" :icon="streamingResponse ? 'stop' : 'send'"
@click="handleSubmit" @click="handleSubmit"
> >
<q-tooltip> Enter 发送Shift+Enter 换行 </q-tooltip> <q-tooltip v-if="!streamingResponse">
Enter 发送Shift+Enter 换行
</q-tooltip>
</q-btn> </q-btn>
</div> </div>
</div> </div>
@ -183,8 +185,7 @@ export default defineComponent({
}, },
this.selectedApi, this.selectedApi,
{ {
showLoadingBar: false, showProcessBar: false,
stream: true,
onStream: (text, controller, done) => { onStream: (text, controller, done) => {
this.currentRequest = controller; this.currentRequest = controller;
if (text) { if (text) {
@ -251,7 +252,8 @@ export default defineComponent({
const languageMap = { const languageMap = {
quickcommand: "NodeJS", quickcommand: "NodeJS",
javascript: "NodeJS", javascript: "NodeJS",
cmd: "bat", cmd: "windows 批处理脚本",
shell: "liunx shell脚本",
}; };
const commonInstructions = `请根据我的需求编写${languageMap[language]}代码,并请遵循以下原则: const commonInstructions = `请根据我的需求编写${languageMap[language]}代码,并请遵循以下原则:
- 编写简洁可读性强的代码 - 编写简洁可读性强的代码
@ -275,7 +277,7 @@ export default defineComponent({
languageSpecific[language.toLowerCase()] || ""; languageSpecific[language.toLowerCase()] || "";
const lastInstructions = const lastInstructions =
"\n请直接给我代码,任何情况下都不需要做解释和说明"; "\n请直接给我MARKDOWN格式的代码,任何情况下都不需要做解释和说明";
return commonInstructions + specificInstructions + lastInstructions; return commonInstructions + specificInstructions + lastInstructions;
}, },
@ -306,13 +308,13 @@ export default defineComponent({
}, },
]; ];
if (this.submitDocs) { if (this.submitDocs && this.language === "quickcommand") {
const docs = this.getLanguageDocs(this.language); const docs = this.getLanguageDocs(this.language);
presetContext.push( presetContext.push(
{ {
role: "user", role: "user",
content: `你现在使用的是一种特殊的环境支持uTools和quickcommand两种特殊的接口请优先使用uTools和quickcommand接口解决需求`, content: `你现在使用的是一种特殊的环境支持uTools和quickcommand两种特殊的接口请优先使用uTools和quickcommand接口解决需求,然后再使用当前语言通用的解决方案`,
}, },
{ {
role: "assistant", role: "assistant",

View File

@ -446,8 +446,9 @@ export const uiCommands = {
width: 4, width: 4,
}, },
value: { value: {
label: "初始进度值", label: "初始进度值(0-100)",
component: "VariableInput", component: "VariableInput",
placeholder: "留空则显示加载动画",
disableToggleType: true, disableToggleType: true,
width: 4, width: 4,
}, },
@ -486,7 +487,7 @@ export const uiCommands = {
defaultValue: { defaultValue: {
title: newVarInputVal("str", "进度"), title: newVarInputVal("str", "进度"),
text: newVarInputVal("str", "处理中..."), text: newVarInputVal("str", "处理中..."),
value: newVarInputVal("var", "0"), value: newVarInputVal("var"),
position: "bottom-right", position: "bottom-right",
onClose: newVarInputVal("var"), onClose: newVarInputVal("var"),
onPause: newVarInputVal("var"), onPause: newVarInputVal("var"),
@ -504,8 +505,9 @@ export const uiCommands = {
component: "OptionEditor", component: "OptionEditor",
options: { options: {
value: { value: {
label: "进度值", label: "进度值(0-100)",
component: "VariableInput", component: "VariableInput",
placeholder: "留空则显示加载动画",
width: 4, width: 4,
disableToggleType: true, disableToggleType: true,
}, },
@ -522,7 +524,7 @@ export const uiCommands = {
}, },
}, },
defaultValue: { defaultValue: {
value: newVarInputVal("var", "0"), value: newVarInputVal("var", "100"),
text: newVarInputVal("str"), text: newVarInputVal("str"),
complete: false, complete: false,
}, },
@ -537,65 +539,6 @@ export const uiCommands = {
}, },
], ],
}, },
{
value: "quickcommand.showLoadingBar",
label: "显示载入界面",
neverHasOutput: true,
asyncMode: "await",
config: [
{
component: "OptionEditor",
options: {
text: {
label: "文本",
component: "VariableInput",
width: 4,
},
position: {
label: "位置",
component: "QSelect",
width: 4,
options: [
{ label: "屏幕左上角", value: "top-left" },
{ label: "屏幕右上角", value: "top-right" },
{ label: "屏幕左下角", value: "bottom-left" },
{ label: "屏幕右下角", value: "bottom-right" },
],
},
onClose: {
label: "关闭按钮回调函数",
component: "VariableInput",
disableToggleType: true,
width: 4,
},
},
defaultValue: {
text: newVarInputVal("str", "加载中..."),
position: "bottom-right",
onClose: newVarInputVal("var"),
},
},
],
outputs: {
label: "载入条对象",
suggestName: "loadingBar",
},
},
{
value: "quickcommand.closeLoadingBar",
label: "关闭载入界面",
neverHasOutput: true,
config: [
{
label: "载入条对象",
component: "VariableInput",
placeholder: "不传则关闭最近的载入条",
width: 12,
defaultValue: newVarInputVal("var"),
disableToggleType: true,
},
],
},
{ {
value: "utools.showOpenDialog", value: "utools.showOpenDialog",
label: "文件选择框", label: "文件选择框",

View File

@ -750,7 +750,7 @@ interface quickcommandApi {
* *
* @param {object} options - * @param {object} options -
* @param {string} [options.text="处理中..."] - * @param {string} [options.text="处理中..."] -
* @param {number} [options.value=0] - (0-100) * @param {number} [options.value] - (0-100)
* @param {string} [options.position="bottom-right"] - top-left, top-right, bottom-left, bottom-right * @param {string} [options.position="bottom-right"] - top-left, top-right, bottom-left, bottom-right
* @param {Function} [options.onClose] - * @param {Function} [options.onClose] -
* @param {Function} [options.onPause] - onResume一起配置 * @param {Function} [options.onPause] - onResume一起配置
@ -758,13 +758,19 @@ interface quickcommandApi {
* @returns {Promise<{id: number, close: Function}>} * @returns {Promise<{id: number, close: Function}>}
* *
* ```js * ```js
* // 基本使用 * // 显示进度条
* const processBar = await quickcommand.showProcessBar({ * const processBar = await quickcommand.showProcessBar({
* text: "正在下载文件...", * text: "正在下载文件...",
* value: 0, * value: 0,
* position: "bottom-right" * position: "bottom-right"
* }); * });
* *
* // 显示加载动画
* const processBar = await quickcommand.showProcessBar({
* text: "正在加载...",
* position: "bottom-right"
* });
*
* // 带暂停/恢复,关闭回调功能 * // 带暂停/恢复,关闭回调功能
* let isPaused = false; * let isPaused = false;
* const processBar = await quickcommand.showProcessBar({ * const processBar = await quickcommand.showProcessBar({
@ -824,18 +830,23 @@ interface quickcommandApi {
/** /**
* *
* @param {object} options - * @param {object} options -
* @param {number} options.value - (0-100) * @param {number} [options.value] - (0-100)
* @param {string} [options.text] - * @param {string} [options.text] -
* @param {boolean} [options.complete] - * @param {boolean} [options.complete] -
* @param {{id: number, close: Function}|undefined} processBar - 使 * @param {{id: number, close: Function}|undefined} processBar - 使
* *
* ```js * ```js
* // 使用最近创建的进度条 * // 更新进度
* quickcommand.updateProcessBar({ * quickcommand.updateProcessBar({
* value: 50, * value: 50,
* text: "已完成50%" * text: "已完成50%"
* }); * });
* *
* // 切换为加载动画
* quickcommand.updateProcessBar({
* text: "正在加载..."
* });
*
* // 使用指定的进度条 * // 使用指定的进度条
* quickcommand.updateProcessBar({ * quickcommand.updateProcessBar({
* value: 50, * value: 50,
@ -852,7 +863,7 @@ interface quickcommandApi {
*/ */
updateProcessBar( updateProcessBar(
options: { options: {
value: number; value?: number;
text?: string; text?: string;
complete?: boolean; complete?: boolean;
}, },
@ -862,71 +873,29 @@ interface quickcommandApi {
} }
): void; ): void;
/**
*
* @param {object} options -
* @param {string} [options.text="加载中..."] -
* @param {string} [options.position="bottom-right"] - top-left, top-right, bottom-left, bottom-right
* @param {Function} [options.onClose] -
* @returns {Promise<{id: number, close: Function}>}
*
* ```js
* // 基本使用
* const loadingBar = await quickcommand.showLoadingBar({
* text: "正在加载...",
* position: "bottom-right"
* });
*
* // 带关闭回调
* const loadingBar = await quickcommand.showLoadingBar({
* text: "正在加载...",
* onClose: () => {
* console.log("用户关闭了加载条");
* }
* });
*
* // 手动关闭
* loadingBar.close();
* // 或者
* quickcommand.closeLoadingBar();
* ```
*/
showLoadingBar(options?: {
text?: string;
position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
onClose?: () => void;
}): Promise<{
id: number;
close: () => void;
}>;
/**
*
* @param {{id: number, close: Function}|undefined} loadingBar -
*
* ```js
* // 关闭最近创建的加载条
* quickcommand.closeLoadingBar();
*
* // 关闭指定的加载条
* quickcommand.closeLoadingBar(loadingBar);
* ```
*/
closeLoadingBar(loadingBar?: { id: number; close: () => void }): void;
/** /**
* AI * AI
* @param content * @param content
* @param content.prompt * @param content.prompt
* @param content.role * @param content.role
* @param apiConfig API配置 * @param apiConfig API配置null则使用用户配置的第一个API配置
* @param apiConfig.apiType openai/ollama * @param apiConfig.apiType openai/ollama
* @param apiConfig.apiUrl API地址 * @param apiConfig.apiUrl API地址
* @param apiConfig.apiToken API令牌 OpenAI * @param apiConfig.apiToken API令牌 OpenAI
* @param apiConfig.model * @param apiConfig.model
* @param options * @param options
* @param options.showLoadingBar * @param options.showProcessBar
* @example * @param options.onStream
*
*
* ```js
* // 不传apiConfig时需在配置页面-右下角菜单-AI配置中进行配置
* const response = await quickcommand.askAI(
* {
* prompt: "你好",
* }
* );
*
* // OpenAI 示例 * // OpenAI 示例
* const response = await quickcommand.askAI( * const response = await quickcommand.askAI(
* { * {
@ -939,9 +908,10 @@ interface quickcommandApi {
* model: "gpt-3.5-turbo" * model: "gpt-3.5-turbo"
* } * }
* ); * );
* console.log(response);
* *
* // Ollama 示例 * // Ollama 示例 (流式回调)
* const response = await quickcommand.askAI( * await quickcommand.askAI(
* { * {
* prompt: "查找进程名为chrome的进程并关闭", * prompt: "查找进程名为chrome的进程并关闭",
* role: "shell" * role: "shell"
@ -950,8 +920,20 @@ interface quickcommandApi {
* apiType: "ollama", * apiType: "ollama",
* apiUrl: "http://localhost:11434", * apiUrl: "http://localhost:11434",
* model: "qwen2.5:32b" * model: "qwen2.5:32b"
* },
* {
* onStream: (chunk, controller, isDone) => {
* console.log(chunk);
* if () {
* controller.abort();
* }
* if (isDone) {
* console.log("流式请求完成");
* }
* }
* } * }
* ); * );
* ```
*/ */
askAI( askAI(
content: { content: {
@ -960,7 +942,8 @@ interface quickcommandApi {
/** 预设角色 */ /** 预设角色 */
role?: "translate" | "shell" | "summarize"; role?: "translate" | "shell" | "summarize";
}, },
apiConfig: { /** API配置不传或传入null则使用用户配置的第一个API配置 */
apiConfig?: {
/** 模型类型openai/ollama */ /** 模型类型openai/ollama */
apiType: "openai" | "ollama"; apiType: "openai" | "ollama";
/** API地址 */ /** API地址 */
@ -971,8 +954,14 @@ interface quickcommandApi {
model: string; model: string;
}, },
options?: { options?: {
/** 是否显示加载条, 默认 true */ /** 是否显示进度条, 默认 true */
showLoadingBar?: boolean; showProcessBar?: boolean;
/** 流式请求回调 */
onStream?: (
chunk: string,
controller: AbortController,
isDone: boolean
) => void;
} }
): Promise<{ ): Promise<{
/** 是否成功 */ /** 是否成功 */