mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-18 10:40:50 +08:00
refactor(providers): modularize AiProvidersPage into separate provider components
This commit is contained in:
132
src/components/providers/utils.ts
Normal file
132
src/components/providers/utils.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import type { AmpcodeConfig, AmpcodeModelMapping, ApiKeyEntry } from '@/types';
|
||||
import type { KeyStatBucket, KeyStats } from '@/utils/usage';
|
||||
import type { AmpcodeFormState, ModelEntry } from './types';
|
||||
|
||||
export const DISABLE_ALL_MODELS_RULE = '*';
|
||||
|
||||
export const hasDisableAllModelsRule = (models?: string[]) =>
|
||||
Array.isArray(models) &&
|
||||
models.some((model) => String(model ?? '').trim() === DISABLE_ALL_MODELS_RULE);
|
||||
|
||||
export const stripDisableAllModelsRule = (models?: string[]) =>
|
||||
Array.isArray(models)
|
||||
? models.filter((model) => String(model ?? '').trim() !== DISABLE_ALL_MODELS_RULE)
|
||||
: [];
|
||||
|
||||
export const withDisableAllModelsRule = (models?: string[]) => {
|
||||
const base = stripDisableAllModelsRule(models);
|
||||
return [...base, DISABLE_ALL_MODELS_RULE];
|
||||
};
|
||||
|
||||
export const withoutDisableAllModelsRule = (models?: string[]) => {
|
||||
const base = stripDisableAllModelsRule(models);
|
||||
return base;
|
||||
};
|
||||
|
||||
export const parseExcludedModels = (text: string): string[] =>
|
||||
text
|
||||
.split(/[\n,]+/)
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
export const excludedModelsToText = (models?: string[]) =>
|
||||
Array.isArray(models) ? models.join('\n') : '';
|
||||
|
||||
export const normalizeOpenAIBaseUrl = (baseUrl: string): string => {
|
||||
let trimmed = String(baseUrl || '').trim();
|
||||
if (!trimmed) return '';
|
||||
trimmed = trimmed.replace(/\/?v0\/management\/?$/i, '');
|
||||
trimmed = trimmed.replace(/\/+$/g, '');
|
||||
if (!/^https?:\/\//i.test(trimmed)) {
|
||||
trimmed = `http://${trimmed}`;
|
||||
}
|
||||
return trimmed;
|
||||
};
|
||||
|
||||
export const buildOpenAIModelsEndpoint = (baseUrl: string): string => {
|
||||
const trimmed = normalizeOpenAIBaseUrl(baseUrl);
|
||||
if (!trimmed) return '';
|
||||
return trimmed.endsWith('/v1') ? `${trimmed}/models` : `${trimmed}/v1/models`;
|
||||
};
|
||||
|
||||
export const buildOpenAIChatCompletionsEndpoint = (baseUrl: string): string => {
|
||||
const trimmed = normalizeOpenAIBaseUrl(baseUrl);
|
||||
if (!trimmed) return '';
|
||||
if (trimmed.endsWith('/chat/completions')) {
|
||||
return trimmed;
|
||||
}
|
||||
return trimmed.endsWith('/v1') ? `${trimmed}/chat/completions` : `${trimmed}/v1/chat/completions`;
|
||||
};
|
||||
|
||||
// 根据 source (apiKey) 获取统计数据 - 与旧版逻辑一致
|
||||
export const getStatsBySource = (
|
||||
apiKey: string,
|
||||
keyStats: KeyStats,
|
||||
maskFn: (key: string) => string
|
||||
): KeyStatBucket => {
|
||||
const bySource = keyStats.bySource ?? {};
|
||||
const masked = maskFn(apiKey);
|
||||
return bySource[apiKey] || bySource[masked] || { success: 0, failure: 0 };
|
||||
};
|
||||
|
||||
// 对于 OpenAI 提供商,汇总所有 apiKeyEntries 的统计 - 与旧版逻辑一致
|
||||
export const getOpenAIProviderStats = (
|
||||
apiKeyEntries: ApiKeyEntry[] | undefined,
|
||||
keyStats: KeyStats,
|
||||
maskFn: (key: string) => string
|
||||
): KeyStatBucket => {
|
||||
const bySource = keyStats.bySource ?? {};
|
||||
let totalSuccess = 0;
|
||||
let totalFailure = 0;
|
||||
|
||||
(apiKeyEntries || []).forEach((entry) => {
|
||||
const key = entry?.apiKey || '';
|
||||
if (!key) return;
|
||||
const masked = maskFn(key);
|
||||
const stats = bySource[key] || bySource[masked] || { success: 0, failure: 0 };
|
||||
totalSuccess += stats.success;
|
||||
totalFailure += stats.failure;
|
||||
});
|
||||
|
||||
return { success: totalSuccess, failure: totalFailure };
|
||||
};
|
||||
|
||||
export const buildApiKeyEntry = (input?: Partial<ApiKeyEntry>): ApiKeyEntry => ({
|
||||
apiKey: input?.apiKey ?? '',
|
||||
proxyUrl: input?.proxyUrl ?? '',
|
||||
headers: input?.headers ?? {},
|
||||
});
|
||||
|
||||
export const ampcodeMappingsToEntries = (mappings?: AmpcodeModelMapping[]): ModelEntry[] => {
|
||||
if (!Array.isArray(mappings) || mappings.length === 0) {
|
||||
return [{ name: '', alias: '' }];
|
||||
}
|
||||
return mappings.map((mapping) => ({
|
||||
name: mapping.from ?? '',
|
||||
alias: mapping.to ?? '',
|
||||
}));
|
||||
};
|
||||
|
||||
export const entriesToAmpcodeMappings = (entries: ModelEntry[]): AmpcodeModelMapping[] => {
|
||||
const seen = new Set<string>();
|
||||
const mappings: AmpcodeModelMapping[] = [];
|
||||
|
||||
entries.forEach((entry) => {
|
||||
const from = entry.name.trim();
|
||||
const to = entry.alias.trim();
|
||||
if (!from || !to) return;
|
||||
const key = from.toLowerCase();
|
||||
if (seen.has(key)) return;
|
||||
seen.add(key);
|
||||
mappings.push({ from, to });
|
||||
});
|
||||
|
||||
return mappings;
|
||||
};
|
||||
|
||||
export const buildAmpcodeFormState = (ampcode?: AmpcodeConfig | null): AmpcodeFormState => ({
|
||||
upstreamUrl: ampcode?.upstreamUrl ?? '',
|
||||
upstreamApiKey: '',
|
||||
forceModelMappings: ampcode?.forceModelMappings ?? false,
|
||||
mappingEntries: ampcodeMappingsToEntries(ampcode?.modelMappings),
|
||||
});
|
||||
Reference in New Issue
Block a user