feat: separate frontend and backend services

This commit is contained in:
luckyyzh
2026-08-10 09:45:22 +08:00
parent cb9ec7a869
commit 7a9859e60d
132 changed files with 8393 additions and 6388 deletions
+94
View File
@@ -0,0 +1,94 @@
export interface ModelsData {
models: Record<string, string>;
modelList: {
id: string;
name: string;
provider: string;
serviceTier?: string;
}[];
defaultModel: { provider: string; modelId: string } | null;
thinkingLevels: Record<string, string[]>;
thinkingLevelMaps: Record<string, Record<string, string | null>>;
modelError?: string;
}
interface ModelsCacheState {
entries: Map<string, { data: ModelsData; expiresAt: number }>;
inFlight: Map<string, Promise<ModelsData>>;
generation: number;
}
declare global {
var __piModelsCacheState: ModelsCacheState | undefined;
}
const MODELS_CACHE_TTL_MS = 60_000;
const MAX_MODELS_CACHE_ENTRIES = 32;
function getModelsCacheState(): ModelsCacheState {
if (!globalThis.__piModelsCacheState) {
globalThis.__piModelsCacheState = {
entries: new Map(),
inFlight: new Map(),
generation: 0,
};
}
return globalThis.__piModelsCacheState;
}
export function invalidateModelsCache(): void {
const state = getModelsCacheState();
state.generation += 1;
state.entries.clear();
state.inFlight.clear();
}
export function withModelRuntimeError(
data: ModelsData,
modelError: string | undefined,
): ModelsData {
return modelError ? { ...data, modelError } : data;
}
export function loadModelsWithCache(
cwd: string,
loader: () => Promise<ModelsData>,
): Promise<ModelsData> {
const state = getModelsCacheState();
const cached = state.entries.get(cwd);
if (cached) {
if (cached.expiresAt > Date.now()) return Promise.resolve(cached.data);
state.entries.delete(cwd);
}
const existingLoad = state.inFlight.get(cwd);
if (existingLoad) return existingLoad;
const generation = state.generation;
const loadPromise: Promise<ModelsData> = Promise.resolve()
.then(loader)
.then((data) => {
if (
state.generation === generation &&
state.inFlight.get(cwd) === loadPromise
) {
const now = Date.now();
for (const [key, entry] of state.entries) {
if (entry.expiresAt <= now) state.entries.delete(key);
}
while (state.entries.size >= MAX_MODELS_CACHE_ENTRIES) {
const oldestKey = state.entries.keys().next().value;
if (oldestKey === undefined) break;
state.entries.delete(oldestKey);
}
state.entries.set(cwd, { data, expiresAt: now + MODELS_CACHE_TTL_MS });
}
return data;
})
.finally(() => {
if (state.inFlight.get(cwd) === loadPromise) state.inFlight.delete(cwd);
});
state.inFlight.set(cwd, loadPromise);
return loadPromise;
}