UNPKG

manifest

Version:

Self-hosted Manifest LLM router with embedded server, SQLite database, and dashboard

227 lines 11.6 kB
"use strict"; 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