Feat: tenant llm provider (#14595)

### What problem does this PR solve?

Python implementation of the Go-based model_provider API suite.

### Type of change

- [x] New Feature (non-breaking change which adds functionality)

---------

Co-authored-by: bill <yibie_jingnian@163.com>
This commit is contained in:
Lynn
2026-05-29 17:39:41 +08:00
committed by GitHub
parent b79f79d9b9
commit dc4b82523b
148 changed files with 6059 additions and 3075 deletions

View File

@@ -11,7 +11,6 @@ export default {
setting: `${restAPIv1}/users/me`,
userInfo: `${restAPIv1}/users/me`,
tenantInfo: `${restAPIv1}/users/me/models`,
setTenantInfo: `${restAPIv1}/users/me/models`,
loginChannels: `${restAPIv1}/auth/login/channels`,
loginChannel: (channel: string) => `${restAPIv1}/auth/login/${channel}`,
@@ -25,14 +24,49 @@ export default {
agreeTenant: (tenantId: string) => `${restAPIv1}/tenants/${tenantId}`,
// llm model
factoriesList: `${webAPI}/llm/factories`,
llmList: `${webAPI}/llm/list`,
myLlm: `${webAPI}/llm/my_llms`,
setApiKey: `${webAPI}/llm/set_api_key`,
addLlm: `${webAPI}/llm/add_llm`,
deleteLlm: `${webAPI}/llm/delete_llm`,
enableLlm: `${webAPI}/llm/enable_llm`,
deleteFactory: `${webAPI}/llm/delete_factory`,
listAllAddedModels: `${restAPIv1}/models`,
defaultModel: `${restAPIv1}/models/default`,
listProviders: `${restAPIv1}/providers`,
addProvider: `${restAPIv1}/providers/`,
addProviderInstance: ({ llm_factory }: { llm_factory: string }) =>
`${restAPIv1}/providers/${llm_factory}/instances`,
listProviderInstances: ({ provider_name }: { provider_name: string }) =>
`${restAPIv1}/providers/${provider_name}/instances`,
listInstanceModels: ({
provider_name,
instance_name,
}: {
provider_name: string;
instance_name: string;
}) =>
`${restAPIv1}/providers/${provider_name}/instances/${instance_name}/models`,
showProviderInstance: ({
provider_name,
instance_name,
}: {
provider_name: string;
instance_name: string;
}) => `${restAPIv1}/providers/${provider_name}/instances/${instance_name}`,
addInstanceModel: ({
provider_name,
instance_name,
}: {
provider_name: string;
instance_name: string;
}) =>
`${restAPIv1}/providers/${provider_name}/instances/${instance_name}/models`,
deleteProviderInstance: ({ provider_name }: { provider_name: string }) =>
`${restAPIv1}/providers/${provider_name}/instances`,
updateModelStatus: ({
provider_name,
instance_name,
model_name,
}: {
provider_name: string;
instance_name: string;
model_name: string;
}) =>
`${restAPIv1}/providers/${provider_name}/instances/${instance_name}/models/${model_name}`,
// data source
dataSourceUpdate: (id: string) => `${restAPIv1}/connectors/${id}`,

View File

@@ -1,29 +1,10 @@
import { IThirdOAIModel } from '@/interfaces/database/llm';
import { getCachedLlmList } from './llm-cache';
export const getLLMIconName = (fid: string, llm_name: string) => {
if (fid === 'FastEmbed') {
return llm_name.split('/').at(0) ?? '';
}
return fid;
};
export const getLlmNameAndFIdByLlmId = (llmId?: string) => {
const [llmName, fId] = llmId?.split('@') || [];
return { fId, llmName };
};
// The names of the large models returned by the interface are similar to "deepseek-r1___OpenAI-API"
export function getRealModelName(llmName: string) {
return llmName.split('__').at(0) ?? '';
}
export function buildLlmUuid(llm: IThirdOAIModel) {
return `${llm.llm_name}@${llm.fid}`;
}
// Get tenant model ID from LLM list by model name and factory ID
export function getTenantModelId(
llmList: Record<string, any>,
@@ -53,12 +34,37 @@ export function getTenantModelId(
return '';
}
// Extract model name and factory ID from a model UUID (e.g., "model_name@factory_id")
/** Build "modelName@instanceName@providerName" */
export function buildModelValue(model: {
model_name: string;
model_instance: string;
model_provider: string;
}) {
return `${model.model_name}@${model.model_instance}@${model.model_provider}`;
}
/** Parse "modelName@instanceName@providerName" */
export function parseModelValue(val: string) {
if (!val) return null;
const firstAt = val.indexOf('@');
const lastAt = val.lastIndexOf('@');
if (firstAt === -1 || firstAt === lastAt) return null;
return {
model_name: val.substring(0, firstAt),
model_instance: val.substring(firstAt + 1, lastAt),
model_provider: val.substring(lastAt + 1),
};
}
// Extract model name and factory ID from a model UUID
// Supports both "model_name@factory_id" and "model_name@factory_id#instance_name"
export function parseModelUuid(uuid: string): {
modelName: string;
factoryId: string;
} {
const [modelName, factoryId] = uuid.split('@');
const hashIndex = uuid.indexOf('#');
const core = hashIndex === -1 ? uuid : uuid.slice(0, hashIndex);
const [modelName, factoryId] = core.split('@');
return { modelName, factoryId };
}

View File

@@ -148,8 +148,6 @@ request.interceptors.response.use(
return response;
},
function (error) {
console.log('🚀 ~ error:', error);
// Handle HTTP 401 (token expired / invalid)
const status = error?.response?.status;
if (status === 401) {