Files
wehub-resource-sync c48612c494
CI / E2E Tests (push) Has been cancelled
CI / Lint, Typecheck & Unit Tests (push) Has been cancelled
Docs Build / Build docs site (push) Has been cancelled
Publish @openmaic packages / Build, validate & publish (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:03:23 +08:00

106 lines
3.3 KiB
TypeScript

import type { ProviderId, ProviderType, ModelInfo } from '@/lib/types/provider';
import type { ProviderSettings } from '@/lib/types/settings';
import { getCatalogThinkingCapability } from '@/lib/ai/model-metadata';
/** Heuristic: model ids matching this are treated as vision-capable. */
const VISION_MODEL_PATTERN = /vision|vl|omni|4o|gpt-5|gemini|claude/i;
/**
* Builds a default ModelInfo from a probed model id. Vision capability is
* inferred from the id via {@link VISION_MODEL_PATTERN}. Shared by the provider
* panel and the token-plan apply flow so the heuristic stays in one place.
*
* When `providerId` is given, the built-in thinking capability for that
* (provider, model) pair is overlaid — so a model that supports configurable
* thinking keeps its `capabilities.thinking` instead of silently losing it
* (which would hide InlineThinkingControl). Unknown pairs are unaffected.
*/
export function modelInfoFromId(id: string, providerId?: string): ModelInfo {
const thinking = providerId ? getCatalogThinkingCapability(providerId, id) : undefined;
return {
id,
name: id,
capabilities: {
streaming: true,
tools: true,
vision: VISION_MODEL_PATTERN.test(id),
...(thinking ? { thinking } : {}),
},
};
}
interface NewCustomProviderConfig {
name: string;
type: ProviderType;
baseUrl: string;
icon: string;
requiresApiKey: boolean;
/** Optional explicit /models URL override (from a preset). */
modelsUrl?: string;
}
export function formatContextWindow(size?: number): string {
if (!size) return '-';
// For M: prefer decimal (use decimal for exact thousands)
if (size >= 1000000) {
if (size % 1000000 === 0) {
return `${size / 1000000}M`;
}
return `${(size / 1000000).toFixed(1)}M`;
}
// For K: prefer decimal if divisible by 1000, otherwise use binary
if (size >= 1000) {
if (size % 1000 === 0) {
return `${size / 1000}K`;
}
return `${Math.floor(size / 1024)}K`;
}
return size.toString();
}
export function getProviderTypeLabel(type: string, t: (key: string) => string): string {
const translationKey = `settings.providerTypes.${type}`;
const translated = t(translationKey);
// If translation exists (not equal to key), use it; otherwise fallback to type
return translated !== translationKey ? translated : type;
}
export function createCustomProviderSettings(
providerData: NewCustomProviderConfig,
): ProviderSettings {
return {
apiKey: '',
baseUrl: providerData.baseUrl || '',
models: [],
name: providerData.name,
type: providerData.type,
defaultBaseUrl: providerData.baseUrl || undefined,
icon: providerData.icon || undefined,
requiresApiKey: providerData.requiresApiKey,
isBuiltIn: false,
modelsUrl: providerData.modelsUrl || undefined,
};
}
interface VerifyModelRequestConfig {
providerId: ProviderId;
modelId: string;
apiKey?: string;
baseUrl?: string;
providerType?: ProviderType | string;
requiresApiKey?: boolean;
}
export function createVerifyModelRequest(config: VerifyModelRequestConfig) {
return {
apiKey: config.apiKey || '',
baseUrl: config.baseUrl || '',
model: `${config.providerId}:${config.modelId}`,
providerType: config.providerType,
requiresApiKey: config.requiresApiKey,
};
}