mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-26 09:03:54 +08:00
fix: harden agent snapshot restore flow
This commit is contained in:
@@ -1,6 +1,41 @@
|
||||
import { getServiceUrl } from '../api';
|
||||
import RequestService from '../httpRequest';
|
||||
|
||||
const CALLBACK_RETRY_LIMIT = 10;
|
||||
const CALLBACK_RETRY_DELAY_MS = 2000;
|
||||
const CALLBACK_RETRY_WINDOW_MS = CALLBACK_RETRY_LIMIT * CALLBACK_RETRY_DELAY_MS;
|
||||
|
||||
function retryCallbackRequest(retry, retryCount, onTerminalFailure, error, retryStartedAt) {
|
||||
if (!onTerminalFailure) {
|
||||
RequestService.reAjaxFun(() => retry(retryCount + 1))
|
||||
return
|
||||
}
|
||||
const startedAt = retryStartedAt || Date.now()
|
||||
if (retryCount >= CALLBACK_RETRY_LIMIT || Date.now() - startedAt >= CALLBACK_RETRY_WINDOW_MS) {
|
||||
RequestService.clearRequestTime()
|
||||
onTerminalFailure(error)
|
||||
return
|
||||
}
|
||||
setTimeout(() => retry(retryCount + 1, startedAt), CALLBACK_RETRY_DELAY_MS)
|
||||
}
|
||||
|
||||
function attachTerminalFailure(request, onTerminalFailure) {
|
||||
if (onTerminalFailure) {
|
||||
request.fail((error) => {
|
||||
RequestService.clearRequestTime()
|
||||
onTerminalFailure(error)
|
||||
})
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
function terminateCallbackRequest(onTerminalFailure, error) {
|
||||
RequestService.clearRequestTime()
|
||||
if (onTerminalFailure) {
|
||||
onTerminalFailure(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
// 获取智能体列表
|
||||
@@ -50,8 +85,9 @@ export default {
|
||||
}).send();
|
||||
},
|
||||
// 获取智能体配置
|
||||
getDeviceConfig(agentId, callback) {
|
||||
RequestService.sendRequest()
|
||||
getDeviceConfig(agentId, callback, onTerminalFailure, retryCount = 0, retryStartedAt = 0) {
|
||||
const retryWindowStartedAt = retryStartedAt || Date.now()
|
||||
const request = RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/agent/${agentId}`)
|
||||
.method('GET')
|
||||
.success((res) => {
|
||||
@@ -60,10 +96,21 @@ export default {
|
||||
})
|
||||
.networkFail((err) => {
|
||||
console.error('获取配置失败:', err);
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.getDeviceConfig(agentId, callback);
|
||||
});
|
||||
}).send();
|
||||
retryCallbackRequest(
|
||||
(nextRetryCount, nextRetryStartedAt) => this.getDeviceConfig(
|
||||
agentId,
|
||||
callback,
|
||||
onTerminalFailure,
|
||||
nextRetryCount,
|
||||
nextRetryStartedAt
|
||||
),
|
||||
retryCount,
|
||||
onTerminalFailure,
|
||||
err,
|
||||
retryWindowStartedAt
|
||||
)
|
||||
})
|
||||
attachTerminalFailure(request, onTerminalFailure).send();
|
||||
},
|
||||
// 配置智能体
|
||||
updateAgentConfig(agentId, configData, callback) {
|
||||
@@ -82,8 +129,9 @@ export default {
|
||||
}).send();
|
||||
},
|
||||
// 获取智能体配置快照列表
|
||||
getAgentSnapshots(agentId, params, callback) {
|
||||
RequestService.sendRequest()
|
||||
getAgentSnapshots(agentId, params, callback, onTerminalFailure, retryCount = 0, retryStartedAt = 0) {
|
||||
const retryWindowStartedAt = retryStartedAt || Date.now()
|
||||
const request = RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/agent/${agentId}/snapshots`)
|
||||
.method('GET')
|
||||
.data(params)
|
||||
@@ -91,56 +139,80 @@ export default {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.networkFail(() => {
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.getAgentSnapshots(agentId, params, callback);
|
||||
});
|
||||
}).send();
|
||||
.networkFail((error) => {
|
||||
retryCallbackRequest(
|
||||
(nextRetryCount, nextRetryStartedAt) => this.getAgentSnapshots(
|
||||
agentId,
|
||||
params,
|
||||
callback,
|
||||
onTerminalFailure,
|
||||
nextRetryCount,
|
||||
nextRetryStartedAt
|
||||
),
|
||||
retryCount,
|
||||
onTerminalFailure,
|
||||
error,
|
||||
retryWindowStartedAt
|
||||
)
|
||||
})
|
||||
attachTerminalFailure(request, onTerminalFailure).send();
|
||||
},
|
||||
// 获取智能体配置快照详情
|
||||
getAgentSnapshot(agentId, snapshotId, callback) {
|
||||
RequestService.sendRequest()
|
||||
getAgentSnapshot(agentId, snapshotId, callback, onTerminalFailure, retryCount = 0, retryStartedAt = 0) {
|
||||
const retryWindowStartedAt = retryStartedAt || Date.now()
|
||||
const request = RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/agent/${agentId}/snapshots/${snapshotId}`)
|
||||
.method('GET')
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.networkFail(() => {
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.getAgentSnapshot(agentId, snapshotId, callback);
|
||||
});
|
||||
}).send();
|
||||
.networkFail((error) => {
|
||||
retryCallbackRequest(
|
||||
(nextRetryCount, nextRetryStartedAt) => this.getAgentSnapshot(
|
||||
agentId,
|
||||
snapshotId,
|
||||
callback,
|
||||
onTerminalFailure,
|
||||
nextRetryCount,
|
||||
nextRetryStartedAt
|
||||
),
|
||||
retryCount,
|
||||
onTerminalFailure,
|
||||
error,
|
||||
retryWindowStartedAt
|
||||
)
|
||||
})
|
||||
attachTerminalFailure(request, onTerminalFailure).send();
|
||||
},
|
||||
// 恢复智能体配置快照
|
||||
restoreAgentSnapshot(agentId, snapshotId, callback) {
|
||||
RequestService.sendRequest()
|
||||
restoreAgentSnapshot(agentId, snapshotId, currentStateToken, callback, onTerminalFailure) {
|
||||
const request = RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/agent/${agentId}/snapshots/${snapshotId}/restore`)
|
||||
.method('POST')
|
||||
.data({ currentStateToken })
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.networkFail(() => {
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.restoreAgentSnapshot(agentId, snapshotId, callback);
|
||||
});
|
||||
}).send();
|
||||
.networkFail((error) => {
|
||||
terminateCallbackRequest(onTerminalFailure, error)
|
||||
})
|
||||
attachTerminalFailure(request, onTerminalFailure).send();
|
||||
},
|
||||
// 删除智能体配置快照
|
||||
deleteAgentSnapshot(agentId, snapshotId, callback) {
|
||||
RequestService.sendRequest()
|
||||
deleteAgentSnapshot(agentId, snapshotId, callback, onTerminalFailure) {
|
||||
const request = RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/agent/${agentId}/snapshots/${snapshotId}`)
|
||||
.method('DELETE')
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.networkFail(() => {
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.deleteAgentSnapshot(agentId, snapshotId, callback);
|
||||
});
|
||||
}).send();
|
||||
.networkFail((error) => {
|
||||
terminateCallbackRequest(onTerminalFailure, error)
|
||||
})
|
||||
attachTerminalFailure(request, onTerminalFailure).send();
|
||||
},
|
||||
// 新增方法:获取智能体模板
|
||||
getAgentTemplate(callback) { // 移除templateName参数
|
||||
@@ -463,19 +535,31 @@ export default {
|
||||
}).send();
|
||||
},
|
||||
// 获取智能体标签
|
||||
getAgentTags(agentId, callback) {
|
||||
RequestService.sendRequest()
|
||||
getAgentTags(agentId, callback, onTerminalFailure, retryCount = 0, retryStartedAt = 0) {
|
||||
const retryWindowStartedAt = retryStartedAt || Date.now()
|
||||
const request = RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/agent/${agentId}/tags`)
|
||||
.method('GET')
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.networkFail(() => {
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.getAgentTags(agentId, callback);
|
||||
});
|
||||
}).send();
|
||||
.networkFail((error) => {
|
||||
retryCallbackRequest(
|
||||
(nextRetryCount, nextRetryStartedAt) => this.getAgentTags(
|
||||
agentId,
|
||||
callback,
|
||||
onTerminalFailure,
|
||||
nextRetryCount,
|
||||
nextRetryStartedAt
|
||||
),
|
||||
retryCount,
|
||||
onTerminalFailure,
|
||||
error,
|
||||
retryWindowStartedAt
|
||||
)
|
||||
})
|
||||
attachTerminalFailure(request, onTerminalFailure).send();
|
||||
},
|
||||
// 保存智能体标签
|
||||
saveAgentTags(agentId, tags, callback) {
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/* eslint-disable test/no-import-node-test -- zero-dependency API regression gate */
|
||||
import assert from "node:assert/strict";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import test from "node:test";
|
||||
|
||||
const agentApiSource = await readFile(new URL("./agent.js", import.meta.url), "utf8");
|
||||
const snapshotDialogSource = await readFile(
|
||||
new URL("../../components/AgentSnapshotDialog.vue", import.meta.url),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
function sourceBetween(source, startMarker, endMarker) {
|
||||
const start = source.indexOf(startMarker);
|
||||
const end = source.indexOf(endMarker, start);
|
||||
assert.notEqual(start, -1, `missing source marker: ${startMarker}`);
|
||||
assert.notEqual(end, -1, `missing source marker: ${endMarker}`);
|
||||
return source.slice(start, end);
|
||||
}
|
||||
|
||||
test("snapshot restore sends the preview token once without an automatic replay", () => {
|
||||
const source = sourceBetween(
|
||||
agentApiSource,
|
||||
"restoreAgentSnapshot(agentId",
|
||||
"// 删除智能体配置快照"
|
||||
);
|
||||
|
||||
assert.match(source, /restoreAgentSnapshot\(agentId, snapshotId, currentStateToken, callback, onTerminalFailure\)/);
|
||||
assert.match(source, /\.method\('POST'\)/);
|
||||
assert.match(source, /\.data\(\{ currentStateToken \}\)/);
|
||||
assert.match(source, /terminateCallbackRequest\(onTerminalFailure, error\)/);
|
||||
assert.doesNotMatch(source, /retryCallbackRequest|RequestService\.reAjaxFun|this\.restoreAgentSnapshot/);
|
||||
});
|
||||
|
||||
test("snapshot deletion terminates on network failure without an automatic replay", () => {
|
||||
const source = sourceBetween(
|
||||
agentApiSource,
|
||||
"deleteAgentSnapshot(agentId",
|
||||
"// 新增方法:获取智能体模板"
|
||||
);
|
||||
|
||||
assert.match(source, /deleteAgentSnapshot\(agentId, snapshotId, callback, onTerminalFailure\)/);
|
||||
assert.match(source, /\.method\('DELETE'\)/);
|
||||
assert.match(source, /terminateCallbackRequest\(onTerminalFailure, error\)/);
|
||||
assert.doesNotMatch(source, /retryCallbackRequest|RequestService\.reAjaxFun|this\.deleteAgentSnapshot/);
|
||||
});
|
||||
|
||||
test("restore preview uses the state data and token from one snapshot-detail response", () => {
|
||||
const source = sourceBetween(
|
||||
snapshotDialogSource,
|
||||
"restoreSnapshot(row)",
|
||||
"decorateSnapshotRows(rows)"
|
||||
);
|
||||
|
||||
assert.match(source, /this\.fetchSnapshotDetail\(row\.id\)\.then\(\(targetSnapshot\)/);
|
||||
assert.match(source, /beforeSnapshotData: targetSnapshot\.currentSnapshotData/);
|
||||
assert.match(source, /hasValidCurrentStateToken\(targetSnapshot\.currentStateToken\)/);
|
||||
assert.doesNotMatch(source, /fetchCurrentAgentData|fetchCurrentAgentTags|Promise\.all/);
|
||||
});
|
||||
|
||||
test("snapshot dialogs cannot close while a restore request is in flight", () => {
|
||||
const guardedDialogs = snapshotDialogSource.match(/:before-close="guardRestoreInFlightClose"/g) || [];
|
||||
const hiddenCloseButtons = snapshotDialogSource.match(/:show-close="!restoring"/g) || [];
|
||||
assert.equal(guardedDialogs.length, 2);
|
||||
assert.equal(hiddenCloseButtons.length, 2);
|
||||
assert.match(
|
||||
snapshotDialogSource,
|
||||
/class="snapshot-footer-button snapshot-footer-cancel"[\s\S]*?:disabled="restoring"[\s\S]*?@click="closeRestorePreview"/
|
||||
);
|
||||
|
||||
const closeMethods = sourceBetween(
|
||||
snapshotDialogSource,
|
||||
" close() {",
|
||||
" open() {"
|
||||
);
|
||||
assert.match(closeMethods, /close\(\) \{\s+if \(this\.restoring\) \{\s+return;/);
|
||||
assert.match(closeMethods, /guardRestoreInFlightClose\(done\) \{\s+if \(this\.restoring\) \{\s+return;[\s\S]*?done\(\);/);
|
||||
assert.match(closeMethods, /closeRestorePreview\(\) \{\s+if \(this\.restoring\) \{\s+return;[\s\S]*?this\.restorePreviewVisible = false;/);
|
||||
});
|
||||
|
||||
test("destructive restore requires a second explicit warning before the POST", () => {
|
||||
const source = sourceBetween(
|
||||
snapshotDialogSource,
|
||||
" confirmRestoreSnapshot() {",
|
||||
" deleteSnapshot(row) {"
|
||||
);
|
||||
|
||||
assert.match(
|
||||
source,
|
||||
/if \(!this\.restoreWillClearChatHistory\) \{\s+this\.submitRestoreSnapshot\(snapshotId, currentStateToken\);\s+return;\s+\}/
|
||||
);
|
||||
assert.match(
|
||||
source,
|
||||
/this\.\$confirm\(\s+this\.\$t\("agentSnapshot\.restoreMemoryDestructiveWarning"\),[\s\S]*?type: "error"[\s\S]*?\)\.then\(\(\) => \{[\s\S]*?this\.submitRestoreSnapshot\(snapshotId, currentStateToken, requestSeq\);/
|
||||
);
|
||||
assert.match(source, /if \(this\.restoring \|\| !this\.restorePreviewRow\) \{\s+return;/);
|
||||
});
|
||||
|
||||
test("a terminal restore failure invalidates the atomic preview instead of replaying it", () => {
|
||||
const source = sourceBetween(
|
||||
snapshotDialogSource,
|
||||
" submitRestoreSnapshot(snapshotId",
|
||||
" deleteSnapshot(row) {"
|
||||
);
|
||||
|
||||
assert.match(source, /else \{\s+this\.invalidateRestorePreview\(\);\s+this\.\$message\.error\(this\.restoreFailedMessage\(data\)\);/);
|
||||
assert.match(source, /\}, \(\) => \{[\s\S]*?this\.invalidateRestorePreview\(\);\s+this\.\$message\.error\(this\.\$t\("agentSnapshot\.restoreFailed"\)\);/);
|
||||
assert.match(source, /invalidateRestorePreview\(\) \{[\s\S]*?this\.restorePreviewSnapshot = null;[\s\S]*?this\.restorePreviewRow = null;/);
|
||||
});
|
||||
|
||||
test("the latest snapshot does not expose a restore action", () => {
|
||||
const source = sourceBetween(
|
||||
snapshotDialogSource,
|
||||
" canRestoreSnapshot(row) {",
|
||||
" canDeleteSnapshot(row) {"
|
||||
);
|
||||
|
||||
assert.match(source, /!!row\?\.id && !row\.isLatestSnapshot/);
|
||||
assert.match(snapshotDialogSource, /v-if="canRestoreSnapshot\(scope\.row\)"/);
|
||||
assert.match(
|
||||
snapshotDialogSource,
|
||||
/restoreSnapshot\(row\) \{\s+if \(!this\.canRestoreSnapshot\(row\)\) \{\s+return;/
|
||||
);
|
||||
});
|
||||
@@ -1,6 +1,26 @@
|
||||
import { getServiceUrl } from '../api';
|
||||
import RequestService from '../httpRequest';
|
||||
|
||||
const CALLBACK_RETRY_LIMIT = 10;
|
||||
const CALLBACK_RETRY_DELAY_MS = 2000;
|
||||
const CALLBACK_RETRY_WINDOW_MS = CALLBACK_RETRY_LIMIT * CALLBACK_RETRY_DELAY_MS;
|
||||
|
||||
function retryCallbackRequest(retry, retryCount, onTerminalFailure, error, retryStartedAt) {
|
||||
if (!onTerminalFailure) {
|
||||
RequestService.reAjaxFun(() => retry(retryCount + 1))
|
||||
return
|
||||
}
|
||||
const startedAt = retryStartedAt || Date.now()
|
||||
if (retryCount >= CALLBACK_RETRY_LIMIT || Date.now() - startedAt >= CALLBACK_RETRY_WINDOW_MS) {
|
||||
RequestService.clearRequestTime()
|
||||
if (onTerminalFailure) {
|
||||
onTerminalFailure(error)
|
||||
}
|
||||
return
|
||||
}
|
||||
setTimeout(() => retry(retryCount + 1, startedAt), CALLBACK_RETRY_DELAY_MS)
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
// 获取替换词文件列表
|
||||
@@ -26,8 +46,9 @@ export default {
|
||||
},
|
||||
|
||||
// 获取所有替换词文件(不分页)
|
||||
selectAll(callback) {
|
||||
RequestService.sendRequest()
|
||||
selectAll(callback, onTerminalFailure, retryCount = 0, retryStartedAt = 0) {
|
||||
const retryWindowStartedAt = retryStartedAt || Date.now()
|
||||
const request = RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/correct-word/file/select`)
|
||||
.method('GET')
|
||||
.success((res) => {
|
||||
@@ -36,10 +57,26 @@ export default {
|
||||
})
|
||||
.networkFail((err) => {
|
||||
console.error('获取所有替换词文件失败:', err)
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.selectAll(callback)
|
||||
})
|
||||
}).send()
|
||||
retryCallbackRequest(
|
||||
(nextRetryCount, nextRetryStartedAt) => this.selectAll(
|
||||
callback,
|
||||
onTerminalFailure,
|
||||
nextRetryCount,
|
||||
nextRetryStartedAt
|
||||
),
|
||||
retryCount,
|
||||
onTerminalFailure,
|
||||
err,
|
||||
retryWindowStartedAt
|
||||
)
|
||||
})
|
||||
if (onTerminalFailure) {
|
||||
request.fail((error) => {
|
||||
RequestService.clearRequestTime()
|
||||
onTerminalFailure(error)
|
||||
})
|
||||
}
|
||||
request.send()
|
||||
},
|
||||
|
||||
// 下载替换词文件
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
import { getServiceUrl } from '../api';
|
||||
import RequestService from '../httpRequest';
|
||||
|
||||
const CALLBACK_RETRY_LIMIT = 10;
|
||||
const CALLBACK_RETRY_DELAY_MS = 2000;
|
||||
const CALLBACK_RETRY_WINDOW_MS = CALLBACK_RETRY_LIMIT * CALLBACK_RETRY_DELAY_MS;
|
||||
|
||||
function retryCallbackRequest(retry, retryCount, onTerminalFailure, error, retryStartedAt) {
|
||||
if (!onTerminalFailure) {
|
||||
RequestService.reAjaxFun(() => retry(retryCount + 1));
|
||||
return;
|
||||
}
|
||||
const startedAt = retryStartedAt || Date.now();
|
||||
if (retryCount >= CALLBACK_RETRY_LIMIT || Date.now() - startedAt >= CALLBACK_RETRY_WINDOW_MS) {
|
||||
RequestService.clearRequestTime();
|
||||
if (onTerminalFailure) {
|
||||
onTerminalFailure(error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
setTimeout(() => retry(retryCount + 1, startedAt), CALLBACK_RETRY_DELAY_MS);
|
||||
}
|
||||
|
||||
export default {
|
||||
// 获取模型配置列表
|
||||
getModelList(params, callback) {
|
||||
@@ -92,8 +112,9 @@ export default {
|
||||
}).send()
|
||||
},
|
||||
// 获取模型名称列表
|
||||
getModelNames(modelType, modelName, callback) {
|
||||
RequestService.sendRequest()
|
||||
getModelNames(modelType, modelName, callback, onTerminalFailure, retryCount = 0, retryStartedAt = 0) {
|
||||
const retryWindowStartedAt = retryStartedAt || Date.now();
|
||||
const request = RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/models/names`)
|
||||
.method('GET')
|
||||
.data({ modelType, modelName })
|
||||
@@ -101,15 +122,34 @@ export default {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.networkFail(() => {
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.getModelNames(modelType, modelName, callback);
|
||||
});
|
||||
}).send();
|
||||
.networkFail((error) => {
|
||||
retryCallbackRequest(
|
||||
(nextRetryCount, nextRetryStartedAt) => this.getModelNames(
|
||||
modelType,
|
||||
modelName,
|
||||
callback,
|
||||
onTerminalFailure,
|
||||
nextRetryCount,
|
||||
nextRetryStartedAt
|
||||
),
|
||||
retryCount,
|
||||
onTerminalFailure,
|
||||
error,
|
||||
retryWindowStartedAt
|
||||
);
|
||||
});
|
||||
if (onTerminalFailure) {
|
||||
request.fail((error) => {
|
||||
RequestService.clearRequestTime();
|
||||
onTerminalFailure(error);
|
||||
});
|
||||
}
|
||||
request.send();
|
||||
},
|
||||
// 获取LLM模型名称列表
|
||||
getLlmModelCodeList(modelName, callback) {
|
||||
RequestService.sendRequest()
|
||||
getLlmModelCodeList(modelName, callback, onTerminalFailure, retryCount = 0, retryStartedAt = 0) {
|
||||
const retryWindowStartedAt = retryStartedAt || Date.now();
|
||||
const request = RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/models/llm/names`)
|
||||
.method('GET')
|
||||
.data({ modelName })
|
||||
@@ -117,29 +157,65 @@ export default {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.networkFail(() => {
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.getLlmModelCodeList(modelName, callback);
|
||||
});
|
||||
}).send();
|
||||
.networkFail((error) => {
|
||||
retryCallbackRequest(
|
||||
(nextRetryCount, nextRetryStartedAt) => this.getLlmModelCodeList(
|
||||
modelName,
|
||||
callback,
|
||||
onTerminalFailure,
|
||||
nextRetryCount,
|
||||
nextRetryStartedAt
|
||||
),
|
||||
retryCount,
|
||||
onTerminalFailure,
|
||||
error,
|
||||
retryWindowStartedAt
|
||||
);
|
||||
});
|
||||
if (onTerminalFailure) {
|
||||
request.fail((error) => {
|
||||
RequestService.clearRequestTime();
|
||||
onTerminalFailure(error);
|
||||
});
|
||||
}
|
||||
request.send();
|
||||
},
|
||||
// 获取模型音色列表
|
||||
getModelVoices(modelId, voiceName, callback) {
|
||||
getModelVoices(modelId, voiceName, callback, onTerminalFailure, retryCount = 0, retryStartedAt = 0) {
|
||||
const retryWindowStartedAt = retryStartedAt || Date.now();
|
||||
const queryParams = new URLSearchParams({
|
||||
voiceName: voiceName || ''
|
||||
}).toString();
|
||||
RequestService.sendRequest()
|
||||
const request = RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/models/${modelId}/voices?${queryParams}`)
|
||||
.method('GET')
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.networkFail(() => {
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.getModelVoices(modelId, voiceName, callback);
|
||||
});
|
||||
}).send();
|
||||
.networkFail((error) => {
|
||||
retryCallbackRequest(
|
||||
(nextRetryCount, nextRetryStartedAt) => this.getModelVoices(
|
||||
modelId,
|
||||
voiceName,
|
||||
callback,
|
||||
onTerminalFailure,
|
||||
nextRetryCount,
|
||||
nextRetryStartedAt
|
||||
),
|
||||
retryCount,
|
||||
onTerminalFailure,
|
||||
error,
|
||||
retryWindowStartedAt
|
||||
);
|
||||
});
|
||||
if (onTerminalFailure) {
|
||||
request.fail((error) => {
|
||||
RequestService.clearRequestTime();
|
||||
onTerminalFailure(error);
|
||||
});
|
||||
}
|
||||
request.send();
|
||||
},
|
||||
// 获取单个模型配置
|
||||
getModelConfig(id, callback) {
|
||||
@@ -323,8 +399,9 @@ export default {
|
||||
}).send()
|
||||
},
|
||||
// 获取插件列表
|
||||
getPluginFunctionList(params, callback) {
|
||||
RequestService.sendRequest()
|
||||
getPluginFunctionList(params, callback, onTerminalFailure, retryCount = 0, retryStartedAt = 0) {
|
||||
const retryWindowStartedAt = retryStartedAt || Date.now();
|
||||
const request = RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/models/provider/plugin/names`)
|
||||
.method('GET')
|
||||
.success((res) => {
|
||||
@@ -332,11 +409,30 @@ export default {
|
||||
callback(res)
|
||||
})
|
||||
.networkFail((err) => {
|
||||
this.$message.error(err.msg || '获取插件列表失败')
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.getPluginFunctionList(params, callback)
|
||||
})
|
||||
}).send()
|
||||
if (!onTerminalFailure && this.$message) {
|
||||
this.$message.error(err.msg || '获取插件列表失败');
|
||||
}
|
||||
retryCallbackRequest(
|
||||
(nextRetryCount, nextRetryStartedAt) => this.getPluginFunctionList(
|
||||
params,
|
||||
callback,
|
||||
onTerminalFailure,
|
||||
nextRetryCount,
|
||||
nextRetryStartedAt
|
||||
),
|
||||
retryCount,
|
||||
onTerminalFailure,
|
||||
err,
|
||||
retryWindowStartedAt
|
||||
);
|
||||
});
|
||||
if (onTerminalFailure) {
|
||||
request.fail((error) => {
|
||||
RequestService.clearRequestTime();
|
||||
onTerminalFailure(error);
|
||||
});
|
||||
}
|
||||
request.send()
|
||||
},
|
||||
|
||||
// 获取RAG模型列表
|
||||
|
||||
Reference in New Issue
Block a user