refactor(*): 稳定期治理 - 收敛核心架构、错误语义并修复跨项目数据竞态

This commit is contained in:
ddCat
2026-09-09 22:02:10 +08:00
parent bacd502f5c
commit b5f3413ded
298 changed files with 12681 additions and 26428 deletions

View File

@@ -1,11 +1,10 @@
import axios from "axios";
import { sanitizeChannelModelCatalogItem, type ChannelModelCatalogItem } from "@/lib/channel-model-catalog";
import { projectDesktopLocalChannelRuntime } from "@/lib/desktop-local-channel";
import { channelRequest } from "@/services/api/custom-channel-relay";
import { createChannelTransport } from "@/services/api/channel-transport";
import { readAxiosError, validateGeminiPayload } from "@/services/api/image-response";
import { geminiApiUrl, geminiHeaders } from "@/services/api/image-transport";
import { buildApiUrl, resolveBackendApiUrl, type AiConfig, type ModelChannel } from "@/stores/use-config-store";
import { http } from "@/services/api/request";
import { buildApiUrl, type AiConfig, type ModelChannel } from "@/stores/use-config-store";
const defaultGeminiConfig: Pick<AiConfig, "baseUrl" | "apiKey" | "apiFormat" | "model" | "systemPrompt"> = {
baseUrl: "https://generativelanguage.googleapis.com",
@@ -22,17 +21,15 @@ export async function fetchImageModels(config: Pick<AiConfig, "baseUrl" | "apiKe
try {
if (config.apiFormat === "gemini") {
const requestConfig = { ...defaultGeminiConfig, ...config };
const request = channelRequest(requestConfig, geminiApiUrl(requestConfig), geminiHeaders(requestConfig));
const response = await axios.get<GeminiModelPayload>(request.url, { headers: request.headers, withCredentials: request.credentials === "include" });
validateGeminiPayload(response.data);
return (response.data.models || [])
const payload = await createChannelTransport(requestConfig, "image").get<GeminiModelPayload>(geminiApiUrl(requestConfig), { headers: geminiHeaders(requestConfig) });
validateGeminiPayload(payload);
return (payload.models || [])
.map((model) => model.name?.replace(/^models\//, ""))
.filter((id): id is string => Boolean(id))
.sort((a, b) => a.localeCompare(b));
}
const request = channelRequest(config, buildApiUrl(config.baseUrl, "/models"), { Authorization: `Bearer ${config.apiKey}` });
const response = await axios.get<OpenAIModelPayload>(request.url, { headers: request.headers, withCredentials: request.credentials === "include" });
return (response.data.data || [])
const payload = await createChannelTransport(config, "image").get<OpenAIModelPayload>(buildApiUrl(config.baseUrl, "/models"));
return (payload.data || [])
.map((model) => model.id)
.filter((id): id is string => Boolean(id))
.sort((a, b) => a.localeCompare(b));
@@ -51,22 +48,15 @@ export async function fetchChannelModels(channel: ModelChannel, viaBackend = fal
}
try {
// 登录态由同源后端代取模型目录,避免每个 OpenAI 兼容服务分别维护浏览器 CORS 白名单。
const response = await axios.post<{ code?: number; data?: { models?: Array<string | ChannelModelCatalogItem> }; msg?: string }>(
resolveBackendApiUrl("/api/ai/models"),
{
baseUrl: runtimeChannel.baseUrl,
allowLocalChannel: runtimeChannel.allowLocalChannel === true,
apiKey: runtimeChannel.apiKey,
apiFormat: runtimeChannel.apiFormat,
headers: runtimeChannel.headers,
},
{ withCredentials: true },
);
if (typeof response.data.code === "number" && response.data.code !== 0) {
throw new Error(response.data.msg || "读取模型失败");
}
const result = await http.post<{ models?: Array<string | ChannelModelCatalogItem> }>("/ai/models", {
baseUrl: runtimeChannel.baseUrl,
allowLocalChannel: runtimeChannel.allowLocalChannel === true,
apiKey: runtimeChannel.apiKey,
apiFormat: runtimeChannel.apiFormat,
headers: runtimeChannel.headers,
});
const catalog = new Map<string, ChannelModelCatalogItem>();
for (const item of response.data.data?.models || []) {
for (const item of result.models || []) {
const entry = typeof item === "string" ? sanitizeChannelModelCatalogItem({ id: item }) : sanitizeChannelModelCatalogItem(item);
if (!entry) continue;
const existing = catalog.get(entry.id);