refactor: add execution backend selection

This commit is contained in:
kris
2026-04-03 00:21:19 +08:00
parent a3a4f3e980
commit 8a62e72fd5
11 changed files with 1067 additions and 318 deletions

View 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;