| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- import { DEFAULT_MODELS } from "../constant";
- import { LLMModel } from "../client/api";
- const CustomSeq = {
- val: -1000, //To ensure the custom model located at front, start from -1000, refer to constant.ts
- cache: new Map<string, number>(),
- next: (id: string) => {
- if (CustomSeq.cache.has(id)) {
- return CustomSeq.cache.get(id) as number;
- } else {
- let seq = CustomSeq.val++;
- CustomSeq.cache.set(id, seq);
- return seq;
- }
- },
- };
- const customProvider = (providerName: string) => ({
- id: providerName.toLowerCase(),
- providerName: providerName,
- providerType: "custom",
- sorted: CustomSeq.next(providerName),
- });
- /**
- * Sorts an array of models based on specified rules.
- *
- * First, sorted by provider; if the same, sorted by model
- */
- const sortModelTable = (models: ReturnType<typeof collectModels>) =>
- models.sort((a, b) => {
- if (a.provider && b.provider) {
- let cmp = a.provider.sorted - b.provider.sorted;
- return cmp === 0 ? a.sorted - b.sorted : cmp;
- } else {
- return a.sorted - b.sorted;
- }
- });
- /**
- * get model name and provider from a formatted string,
- * e.g. `gpt-4@OpenAi` or `claude-3-5-sonnet@20240620@Google`
- * @param modelWithProvider model name with provider separated by last `@` char,
- * @returns [model, provider] tuple, if no `@` char found, provider is undefined
- */
- export function getModelProvider(modelWithProvider: string): [string, string?] {
- const [model, provider] = modelWithProvider.split(/@(?!.*@)/);
- return [model, provider];
- }
- export function collectModelTable(
- models: readonly LLMModel[],
- customModels: string,
- ) {
- const modelTable: Record<
- string,
- {
- available: boolean;
- name: string;
- displayName: string;
- sorted: number;
- provider?: LLMModel["provider"]; // Marked as optional
- isDefault?: boolean;
- }
- > = {};
- // default models
- models.forEach((m) => {
- // using <modelName>@<providerId> as fullName
- modelTable[`${m.name}@${m?.provider?.id}`] = {
- ...m,
- displayName: m.name, // 'provider' is copied over if it exists
- };
- });
- // server custom models
- customModels
- .split(",")
- .filter((v) => !!v && v.length > 0)
- .forEach((m) => {
- const available = !m.startsWith("-");
- const nameConfig =
- m.startsWith("+") || m.startsWith("-") ? m.slice(1) : m;
- let [name, displayName] = nameConfig.split("=");
- // enable or disable all models
- if (name === "all") {
- Object.values(modelTable).forEach(
- (model) => (model.available = available),
- );
- } else {
- // 1. find model by name, and set available value
- const [customModelName, customProviderName] = getModelProvider(name);
- let count = 0;
- for (const fullName in modelTable) {
- const [modelName, providerName] = getModelProvider(fullName);
- if (
- customModelName == modelName &&
- (customProviderName === undefined ||
- customProviderName === providerName)
- ) {
- count += 1;
- modelTable[fullName]["available"] = available;
- // swap name and displayName for bytedance
- if (providerName === "bytedance") {
- [name, displayName] = [displayName, modelName];
- modelTable[fullName]["name"] = name;
- }
- if (displayName) {
- modelTable[fullName]["displayName"] = displayName;
- }
- }
- }
- // 2. if model not exists, create new model with available value
- if (count === 0) {
- let [customModelName, customProviderName] = getModelProvider(name);
- const provider = customProvider(
- customProviderName || customModelName,
- );
- // swap name and displayName for bytedance
- if (displayName && provider.providerName == "ByteDance") {
- [customModelName, displayName] = [displayName, customModelName];
- }
- modelTable[`${customModelName}@${provider?.id}`] = {
- name: customModelName,
- displayName: displayName || customModelName,
- available,
- provider, // Use optional chaining
- sorted: CustomSeq.next(`${customModelName}@${provider?.id}`),
- };
- }
- }
- });
- return modelTable;
- }
- export function collectModelTableWithDefaultModel(
- models: readonly LLMModel[],
- customModels: string,
- defaultModel: string,
- ) {
- let modelTable = collectModelTable(models, customModels);
- if (defaultModel && defaultModel !== "") {
- if (defaultModel.includes("@")) {
- if (defaultModel in modelTable) {
- modelTable[defaultModel].isDefault = true;
- }
- } else {
- for (const key of Object.keys(modelTable)) {
- if (
- modelTable[key].available &&
- getModelProvider(key)[0] == defaultModel
- ) {
- modelTable[key].isDefault = true;
- break;
- }
- }
- }
- }
- return modelTable;
- }
- /**
- * Generate full model table.
- */
- export function collectModels(
- models: readonly LLMModel[],
- customModels: string,
- ) {
- const modelTable = collectModelTable(models, customModels);
- let allModels = Object.values(modelTable);
- allModels = sortModelTable(allModels);
- return allModels;
- }
- export function collectModelsWithDefaultModel(
- models: readonly LLMModel[],
- customModels: string,
- defaultModel: string,
- ) {
- const modelTable = collectModelTableWithDefaultModel(
- models,
- customModels,
- defaultModel,
- );
- let allModels = Object.values(modelTable);
- allModels = sortModelTable(allModels);
- return allModels;
- }
- export function isModelAvailableInServer(
- customModels: string,
- modelName: string,
- providerName: string,
- ) {
- const fullName = `${modelName}@${providerName}`;
- const modelTable = collectModelTable(DEFAULT_MODELS, customModels);
- return modelTable[fullName]?.available === false;
- }
- /**
- * Check if the model name is a GPT-4 related model
- *
- * @param modelName The name of the model to check
- * @returns True if the model is a GPT-4 related model (excluding gpt-4o-mini)
- */
- export function isGPT4Model(modelName: string): boolean {
- return (
- (modelName.startsWith("gpt-4") ||
- modelName.startsWith("chatgpt-4o") ||
- modelName.startsWith("o1")) &&
- !modelName.startsWith("gpt-4o-mini")
- );
- }
- /**
- * Checks if a model is not available on any of the specified providers in the server.
- *
- * @param {string} customModels - A string of custom models, comma-separated.
- * @param {string} modelName - The name of the model to check.
- * @param {string|string[]} providerNames - A string or array of provider names to check against.
- *
- * @returns {boolean} True if the model is not available on any of the specified providers, false otherwise.
- */
- export function isModelNotavailableInServer(
- customModels: string,
- modelName: string,
- providerNames: string | string[],
- ): boolean {
- // Check DISABLE_GPT4 environment variable
- if (
- process.env.DISABLE_GPT4 === "1" &&
- isGPT4Model(modelName.toLowerCase())
- ) {
- return true;
- }
- const modelTable = collectModelTable(DEFAULT_MODELS, customModels);
- const providerNamesArray = Array.isArray(providerNames)
- ? providerNames
- : [providerNames];
- for (const providerName of providerNamesArray) {
- const fullName = `${modelName}@${providerName.toLowerCase()}`;
- if (modelTable?.[fullName]?.available === true) return false;
- }
- return true;
- }
|