manifest
Version:
Self-hosted Manifest LLM router with embedded server, SQLite database, and dashboard
227 lines • 11.6 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var ProxyFallbackService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProxyFallbackService = void 0;
exports.normalizeProviderModel = normalizeProviderModel;
exports.resolveApiKey = resolveApiKey;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const provider_key_service_1 = require("../routing-core/provider-key.service");
const custom_provider_entity_1 = require("../../entities/custom-provider.entity");
const custom_provider_service_1 = require("../custom-provider/custom-provider.service");
const openai_oauth_service_1 = require("../oauth/openai-oauth.service");
const minimax_oauth_service_1 = require("../oauth/minimax-oauth.service");
const model_pricing_cache_service_1 = require("../../model-prices/model-pricing-cache.service");
const provider_client_1 = require("./provider-client");
const provider_endpoints_1 = require("./provider-endpoints");
const copilot_token_service_1 = require("./copilot-token.service");
const fallback_status_codes_1 = require("./fallback-status-codes");
const provider_aliases_1 = require("../../common/utils/provider-aliases");
const provider_base_url_1 = require("../provider-base-url");
const qwen_region_1 = require("../qwen-region");
const anthropic_model_id_1 = require("../../common/utils/anthropic-model-id");
const proxy_transport_1 = require("./proxy-transport");
let ProxyFallbackService = ProxyFallbackService_1 = class ProxyFallbackService {
providerKeyService;
customProviderRepo;
openaiOauth;
minimaxOauth;
providerClient;
copilotToken;
pricingCache;
logger = new common_1.Logger(ProxyFallbackService_1.name);
constructor(providerKeyService, customProviderRepo, openaiOauth, minimaxOauth, providerClient, copilotToken, pricingCache) {
this.providerKeyService = providerKeyService;
this.customProviderRepo = customProviderRepo;
this.openaiOauth = openaiOauth;
this.minimaxOauth = minimaxOauth;
this.providerClient = providerClient;
this.copilotToken = copilotToken;
this.pricingCache = pricingCache;
}
async tryFallbacks(agentId, userId, fallbackModels, body, stream, sessionKey, primaryModel, signal, primaryProvider, primaryAuthType, signatureLookup) {
const failures = [];
const failedAuthByProvider = new Map();
if (primaryProvider && primaryAuthType) {
failedAuthByProvider.set(primaryProvider.toLowerCase(), new Set([primaryAuthType]));
}
for (let i = 0; i < fallbackModels.length; i++) {
const requestedModel = fallbackModels[i];
const pricing = this.pricingCache.getByModel(requestedModel);
let provider;
if (custom_provider_service_1.CustomProviderService.isCustom(requestedModel)) {
const slashIdx = requestedModel.indexOf('/');
provider = slashIdx > 0 ? requestedModel.substring(0, slashIdx) : requestedModel;
}
else {
const prefix = (0, provider_aliases_1.inferProviderFromModelName)(requestedModel);
provider =
(prefix && (await this.providerKeyService.hasActiveProvider(agentId, prefix))
? prefix
: undefined) ??
pricing?.provider ??
(await this.providerKeyService.findProviderForModel(agentId, requestedModel));
}
if (!provider) {
this.logger.debug(`Fallback ${i}: skipping model=${requestedModel} (no provider data)`);
continue;
}
const model = normalizeProviderModel(provider, requestedModel);
const excludeAuth = failedAuthByProvider.get(provider.toLowerCase());
const authType = await this.providerKeyService.getAuthType(agentId, provider, excludeAuth);
let apiKey = await this.providerKeyService.getProviderApiKey(agentId, provider, authType);
if (apiKey === null) {
this.logger.debug(`Fallback ${i}: skipping model=${model} provider=${provider} (no API key)`);
continue;
}
const resolvedCredentials = await resolveApiKey(provider, apiKey, authType, agentId, userId, this.openaiOauth, this.minimaxOauth);
const providerRegion = await this.providerKeyService.getProviderRegion(agentId, provider, authType);
this.logger.log(`Fallback ${i}: trying model=${model} provider=${provider} auth_type=${authType} (primary=${primaryModel})`);
const forward = await this.tryForwardToProvider({
provider,
apiKey: resolvedCredentials.apiKey,
model,
body,
stream,
sessionKey,
signal,
authType,
resourceUrl: resolvedCredentials.resourceUrl,
providerRegion,
signatureLookup,
});
if (forward.response.ok) {
return { success: { forward, model, provider, fallbackIndex: i }, failures };
}
const errorBody = await forward.response.text();
failures.push({
model,
provider,
fallbackIndex: i,
status: forward.response.status,
errorBody,
});
const existing = failedAuthByProvider.get(provider.toLowerCase());
const updated = new Set(existing);
updated.add(authType);
failedAuthByProvider.set(provider.toLowerCase(), updated);
if (!(0, fallback_status_codes_1.shouldTriggerFallback)(forward.response.status))
break;
}
return { success: null, failures };
}
async tryForwardToProvider(opts) {
try {
return await this.forwardToProvider(opts);
}
catch (error) {
if (opts.signal?.aborted)
throw error;
if (!(0, proxy_transport_1.isTransportError)(error))
throw error;
const failureResponse = (0, proxy_transport_1.buildTransportErrorResponse)(error);
const message = (0, proxy_transport_1.describeTransportError)(error);
this.logger.warn(`Provider transport failure: provider=${opts.provider} model=${opts.model} status=${failureResponse.status} message=${message}`);
return {
response: failureResponse,
isGoogle: false,
isAnthropic: false,
isChatGpt: false,
};
}
}
async forwardToProvider(opts) {
const { provider, body, stream, signal, authType, resourceUrl, providerRegion, signatureLookup, } = opts;
const extraHeaders = {};
if (provider === 'xai') {
extraHeaders['x-grok-conv-id'] = opts.sessionKey;
}
const hasExtraHeaders = Object.keys(extraHeaders).length > 0;
let effectiveKey = opts.apiKey;
if (provider.toLowerCase() === 'copilot') {
effectiveKey = await this.copilotToken.getCopilotToken(opts.apiKey);
}
let customEndpoint;
let forwardModel = opts.model;
if (provider.toLowerCase() === 'copilot' && forwardModel.startsWith('copilot/')) {
forwardModel = forwardModel.substring('copilot/'.length);
}
if (custom_provider_service_1.CustomProviderService.isCustom(provider)) {
const cpId = custom_provider_service_1.CustomProviderService.extractId(provider);
const cp = await this.customProviderRepo.findOne({ where: { id: cpId } });
if (cp) {
customEndpoint = (0, provider_endpoints_1.buildCustomEndpoint)(cp.base_url);
forwardModel = custom_provider_service_1.CustomProviderService.rawModelName(opts.model);
}
}
else if ((0, provider_endpoints_1.resolveEndpointKey)(provider) === 'qwen' && (0, qwen_region_1.isQwenResolvedRegion)(providerRegion)) {
customEndpoint = (0, provider_endpoints_1.buildEndpointOverride)((0, qwen_region_1.getQwenCompatibleBaseUrl)(providerRegion), 'qwen');
}
else if (authType === 'subscription' && provider.toLowerCase() === 'minimax' && resourceUrl) {
const minimaxBaseUrl = (0, provider_base_url_1.normalizeMinimaxSubscriptionBaseUrl)(resourceUrl);
if (minimaxBaseUrl) {
customEndpoint = (0, provider_endpoints_1.buildEndpointOverride)(minimaxBaseUrl, 'minimax-subscription');
}
else {
this.logger.warn('Ignoring invalid MiniMax subscription resource URL');
}
}
return this.providerClient.forward({
provider,
apiKey: effectiveKey,
model: forwardModel,
body,
stream,
signal,
extraHeaders: hasExtraHeaders ? extraHeaders : undefined,
customEndpoint,
authType,
signatureLookup,
});
}
};
exports.ProxyFallbackService = ProxyFallbackService;
exports.ProxyFallbackService = ProxyFallbackService = ProxyFallbackService_1 = __decorate([
(0, common_1.Injectable)(),
__param(1, (0, typeorm_1.InjectRepository)(custom_provider_entity_1.CustomProvider)),
__metadata("design:paramtypes", [provider_key_service_1.ProviderKeyService,
typeorm_2.Repository,
openai_oauth_service_1.OpenaiOauthService,
minimax_oauth_service_1.MinimaxOauthService,
provider_client_1.ProviderClient,
copilot_token_service_1.CopilotTokenService,
model_pricing_cache_service_1.ModelPricingCacheService])
], ProxyFallbackService);
function normalizeProviderModel(provider, model) {
return provider.toLowerCase() === 'anthropic' ? (0, anthropic_model_id_1.normalizeAnthropicShortModelId)(model) : model;
}
async function resolveApiKey(provider, apiKey, authType, agentId, userId, openaiOauth, minimaxOauth) {
if (authType === 'subscription') {
const lower = provider.toLowerCase();
if (lower === 'openai') {
const unwrapped = await openaiOauth.unwrapToken(apiKey, agentId, userId);
if (unwrapped)
return { apiKey: unwrapped };
}
if (lower === 'minimax') {
const unwrapped = await minimaxOauth.unwrapToken(apiKey, agentId, userId);
if (unwrapped)
return { apiKey: unwrapped.t, resourceUrl: unwrapped.u };
}
}
return { apiKey };
}
//# sourceMappingURL=proxy-fallback.service.js.map