refactor: add execution backend selection
This commit is contained in:
97
src/lib/execution/backend-selector.ts
Normal file
97
src/lib/execution/backend-selector.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import type { AiAccountStatus, AiProvider } from "@/lib/boss-data";
|
||||
import {
|
||||
ALIYUN_QWEN_BACKEND,
|
||||
isReadyAliyunQwenBackend,
|
||||
} from "@/lib/execution/backends/aliyun-qwen-backend";
|
||||
import {
|
||||
MASTER_CODEX_NODE_BACKEND,
|
||||
isReadyMasterCodexNodeBackend,
|
||||
} from "@/lib/execution/backends/master-codex-node-backend";
|
||||
import { OPENAI_BACKEND, isReadyOpenAiBackend } from "@/lib/execution/backends/openai-backend";
|
||||
|
||||
export interface ExecutionBackendSelectionInput {
|
||||
primary: {
|
||||
provider: AiProvider;
|
||||
status: AiAccountStatus;
|
||||
};
|
||||
backups: Array<{
|
||||
provider: AiProvider;
|
||||
status: AiAccountStatus;
|
||||
}>;
|
||||
}
|
||||
|
||||
export type ExecutionBackendChoice =
|
||||
| typeof MASTER_CODEX_NODE_BACKEND
|
||||
| typeof OPENAI_BACKEND
|
||||
| typeof ALIYUN_QWEN_BACKEND;
|
||||
|
||||
function resolveBackendByProvider(provider: AiProvider): ExecutionBackendChoice {
|
||||
switch (provider) {
|
||||
case "master_codex_node":
|
||||
return MASTER_CODEX_NODE_BACKEND;
|
||||
case "openai_api":
|
||||
return OPENAI_BACKEND;
|
||||
case "aliyun_qwen_api":
|
||||
return ALIYUN_QWEN_BACKEND;
|
||||
default:
|
||||
return MASTER_CODEX_NODE_BACKEND;
|
||||
}
|
||||
}
|
||||
|
||||
function isReadyBackend(choice: ExecutionBackendChoice, input: ExecutionBackendSelectionInput) {
|
||||
const candidates = [
|
||||
...(input.primary.provider === choice.provider ? [input.primary] : []),
|
||||
...input.backups.filter((item) => item.provider === choice.provider),
|
||||
];
|
||||
|
||||
return candidates.some((candidate) => {
|
||||
switch (choice.backendId) {
|
||||
case "aliyun-qwen":
|
||||
return isReadyAliyunQwenBackend(candidate);
|
||||
case "openai-api":
|
||||
return isReadyOpenAiBackend(candidate);
|
||||
case "master-codex-node":
|
||||
return isReadyMasterCodexNodeBackend(candidate);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function selectExecutionBackend(
|
||||
input: ExecutionBackendSelectionInput,
|
||||
): Promise<ExecutionBackendChoice> {
|
||||
return listExecutionBackendChoices(input)[0] ?? resolveBackendByProvider(input.primary.provider);
|
||||
}
|
||||
|
||||
export function listExecutionBackendChoices(
|
||||
input: ExecutionBackendSelectionInput,
|
||||
): ExecutionBackendChoice[] {
|
||||
const primaryBackend = resolveBackendByProvider(input.primary.provider);
|
||||
const ordered: ExecutionBackendChoice[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
const pushBackend = (backend: ExecutionBackendChoice) => {
|
||||
if (seen.has(backend.backendId)) {
|
||||
return;
|
||||
}
|
||||
ordered.push(backend);
|
||||
seen.add(backend.backendId);
|
||||
};
|
||||
|
||||
if (input.primary.status === "ready") {
|
||||
pushBackend(primaryBackend);
|
||||
}
|
||||
|
||||
const orderedBackends: ExecutionBackendChoice[] = [ALIYUN_QWEN_BACKEND, OPENAI_BACKEND, MASTER_CODEX_NODE_BACKEND];
|
||||
for (const backend of orderedBackends) {
|
||||
if (isReadyBackend(backend, input)) {
|
||||
pushBackend(backend);
|
||||
}
|
||||
}
|
||||
|
||||
pushBackend(primaryBackend);
|
||||
return ordered;
|
||||
}
|
||||
|
||||
export const selectExecutionBackendForTesting = selectExecutionBackend;
|
||||
Reference in New Issue
Block a user