UNPKG

manifest

Version:

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

309 lines 15.2 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 ModelDiscoveryService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.ModelDiscoveryService = void 0; const common_1 = require("@nestjs/common"); const typeorm_1 = require("@nestjs/typeorm"); const typeorm_2 = require("typeorm"); const user_provider_entity_1 = require("../entities/user-provider.entity"); const custom_provider_entity_1 = require("../entities/custom-provider.entity"); const provider_model_fetcher_service_1 = require("./provider-model-fetcher.service"); const provider_model_registry_service_1 = require("./provider-model-registry.service"); const crypto_util_1 = require("../common/utils/crypto.util"); const quality_score_util_1 = require("../database/quality-score.util"); const pricing_sync_service_1 = require("../database/pricing-sync.service"); const models_dev_sync_service_1 = require("../database/models-dev-sync.service"); const openai_oauth_types_1 = require("../routing/oauth/openai-oauth.types"); const qwen_region_1 = require("../routing/qwen-region"); const copilot_token_service_1 = require("../routing/proxy/copilot-token.service"); const anthropic_subscription_probe_1 = require("./anthropic-subscription-probe"); const model_fallback_1 = require("./model-fallback"); const known_model_prices_1 = require("./known-model-prices"); const customProviderKey = (id) => `custom:${id}`; const customModelKey = (id, modelName) => `custom:${id}/${modelName}`; function isQwenProvider(providerId) { const lower = providerId.toLowerCase(); return lower === 'qwen' || lower === 'alibaba'; } let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryService { providerRepo; customProviderRepo; fetcher; pricingSync; modelsDevSync; modelRegistry; copilotTokenService; logger = new common_1.Logger(ModelDiscoveryService_1.name); constructor(providerRepo, customProviderRepo, fetcher, pricingSync, modelsDevSync, modelRegistry, copilotTokenService) { this.providerRepo = providerRepo; this.customProviderRepo = customProviderRepo; this.fetcher = fetcher; this.pricingSync = pricingSync; this.modelsDevSync = modelsDevSync; this.modelRegistry = modelRegistry; this.copilotTokenService = copilotTokenService; } async discoverModels(provider) { let apiKey = ''; let endpointOverride; const lowerProvider = provider.provider.toLowerCase(); if (provider.api_key_encrypted) { try { apiKey = (0, crypto_util_1.decrypt)(provider.api_key_encrypted, (0, crypto_util_1.getEncryptionSecret)()); } catch { this.logger.warn(`Failed to decrypt key for provider ${provider.provider}`); return []; } } if (provider.auth_type === 'subscription' && apiKey) { if (lowerProvider === 'openai' || lowerProvider === 'minimax') { const blob = (0, openai_oauth_types_1.parseOAuthTokenBlob)(apiKey); if (blob?.t) { apiKey = blob.t; if (lowerProvider === 'minimax' && blob.u) { endpointOverride = blob.u; } } } else if (lowerProvider === 'copilot' && this.copilotTokenService) { try { apiKey = await this.copilotTokenService.getCopilotToken(apiKey); } catch { this.logger.warn('Copilot token exchange failed for model discovery — falling back to known models'); apiKey = ''; } } } if (isQwenProvider(provider.provider) && (0, qwen_region_1.isQwenResolvedRegion)(provider.region)) { endpointOverride = (0, qwen_region_1.getQwenCompatibleBaseUrl)(provider.region); } let raw; if (provider.auth_type === 'subscription' && !apiKey) { raw = (0, model_fallback_1.buildSubscriptionFallbackModels)(this.pricingSync, provider.provider); if (raw.length > 0) { this.logger.log(`No token for subscription provider ${provider.provider} — using ${raw.length} fallback models`); } } else { raw = await this.fetcher.fetch(provider.provider, apiKey, provider.auth_type, endpointOverride); if (raw.length > 0 && this.modelRegistry) { this.modelRegistry.registerModels(provider.provider, raw.map((m) => m.id)); } if (raw.length === 0) { raw = (0, model_fallback_1.buildModelsDevFallback)(this.modelsDevSync, provider.provider); if (raw.length > 0) { this.logger.log(`Native API returned 0 models for ${provider.provider} — using ${raw.length} models from models.dev`); } } if (raw.length === 0 && !isQwenProvider(provider.provider)) { const confirmed = this.modelRegistry?.getConfirmedModels(provider.provider) ?? null; raw = (0, model_fallback_1.buildFallbackModels)(this.pricingSync, provider.provider, confirmed); if (raw.length > 0) { this.logger.log(`No models.dev data for ${provider.provider} — using ${raw.length} models from OpenRouter`); } } } if (provider.auth_type === 'subscription') { raw = (0, model_fallback_1.supplementWithKnownModels)(raw, provider.provider); } if (lowerProvider === 'anthropic' && provider.auth_type === 'subscription' && apiKey) { raw = await (0, anthropic_subscription_probe_1.filterBySubscriptionAccess)(raw, apiKey); } const authType = provider.auth_type === 'subscription' ? 'subscription' : 'api_key'; const enriched = raw.map((model) => ({ ...this.enrichModel(model, provider.provider), authType: authType, })); const filtered = enriched.filter((model) => { const mdEntry = this.modelsDevSync?.lookupModel(provider.provider, model.id); if (mdEntry && mdEntry.toolCall === false) return false; return true; }); provider.cached_models = filtered; provider.models_fetched_at = new Date().toISOString(); await this.providerRepo.save(provider); this.logger.log(`Discovered ${filtered.length} models for provider ${provider.provider} (agent ${provider.agent_id})`); return filtered; } async discoverAllForAgent(agentId) { const providers = await this.providerRepo.find({ where: { agent_id: agentId, is_active: true }, }); await Promise.all(providers .filter((p) => !p.provider.startsWith('custom:')) .map((p) => this.discoverModels(p).catch((err) => { this.logger.warn(`Discovery failed for ${p.provider}: ${err}`); }))); } async getModelsForAgent(agentId) { const providers = await this.providerRepo.find({ where: { agent_id: agentId, is_active: true }, }); const models = []; const seen = new Map(); for (const p of providers) { if (p.provider.startsWith('custom:')) continue; const cached = p.cached_models; if (!Array.isArray(cached)) continue; const providerAuthType = p.auth_type === 'subscription' ? 'subscription' : 'api_key'; for (const m of cached) { const effectiveAuthType = m.authType ?? providerAuthType; const dedupeKey = `${m.id}::${effectiveAuthType}`; if (!seen.has(dedupeKey)) { seen.set(dedupeKey, models.length); models.push({ ...m, authType: effectiveAuthType }); } } } const customProviders = await this.customProviderRepo.find({ where: { agent_id: agentId }, }); for (const cp of customProviders) { if (!Array.isArray(cp.models)) continue; const cpKey = customProviderKey(cp.id); for (const m of cp.models) { const modelKey = customModelKey(cp.id, m.model_name); if (seen.has(modelKey)) continue; seen.set(modelKey, models.length); const inputPerToken = m.input_price_per_million_tokens != null ? m.input_price_per_million_tokens / 1_000_000 : null; const outputPerToken = m.output_price_per_million_tokens != null ? m.output_price_per_million_tokens / 1_000_000 : null; models.push({ id: modelKey, displayName: m.model_name, provider: cpKey, contextWindow: m.context_window ?? 128000, inputPricePerToken: inputPerToken, outputPricePerToken: outputPerToken, capabilityReasoning: false, capabilityCode: false, qualityScore: 2, }); } } return models; } async getModelForAgent(agentId, modelName) { const all = await this.getModelsForAgent(agentId); return all.find((m) => m.id === modelName); } enrichModel(model, providerId) { if (model.inputPricePerToken !== null && model.inputPricePerToken >= 0 && model.outputPricePerToken !== null && model.outputPricePerToken >= 0) { return this.computeScore(this.applyCapabilities(model, providerId)); } if (this.modelsDevSync) { const mdEntry = this.modelsDevSync.lookupModel(providerId, model.id); if (mdEntry && mdEntry.inputPricePerToken !== null) { return this.computeScore({ ...model, inputPricePerToken: mdEntry.inputPricePerToken, outputPricePerToken: mdEntry.outputPricePerToken, contextWindow: mdEntry.contextWindow ?? model.contextWindow, displayName: mdEntry.name || model.displayName, capabilityReasoning: mdEntry.reasoning ?? model.capabilityReasoning, capabilityCode: mdEntry.toolCall ?? model.capabilityCode, }); } } if (this.pricingSync) { const orPrefix = (0, model_fallback_1.findOpenRouterPrefix)(providerId); if (orPrefix) { const orPricing = (0, model_fallback_1.lookupWithVariants)(this.pricingSync, orPrefix, model.id); if (orPricing) { return this.computeScore({ ...model, inputPricePerToken: orPricing.input, outputPricePerToken: orPricing.output, contextWindow: orPricing.contextWindow ?? model.contextWindow, displayName: orPricing.displayName || model.displayName, }); } } const exactPricing = this.pricingSync.lookupPricing(model.id); if (exactPricing) { return this.computeScore({ ...model, inputPricePerToken: exactPricing.input, outputPricePerToken: exactPricing.output, contextWindow: exactPricing.contextWindow ?? model.contextWindow, displayName: exactPricing.displayName || model.displayName, }); } } const known = (0, known_model_prices_1.lookupKnownPrice)(model.id); if (known) { return this.computeScore({ ...model, inputPricePerToken: known.input, outputPricePerToken: known.output, }); } return this.computeScore(model); } applyCapabilities(model, providerId) { if (!this.modelsDevSync) return model; const mdEntry = this.modelsDevSync.lookupModel(providerId, model.id); if (!mdEntry) return model; return { ...model, capabilityReasoning: mdEntry.reasoning ?? model.capabilityReasoning, capabilityCode: mdEntry.toolCall ?? model.capabilityCode, }; } computeScore(model) { const score = (0, quality_score_util_1.computeQualityScore)({ model_name: model.id, input_price_per_token: model.inputPricePerToken, output_price_per_token: model.outputPricePerToken, capability_reasoning: model.capabilityReasoning, capability_code: model.capabilityCode, context_window: model.contextWindow, }); return { ...model, qualityScore: score }; } }; exports.ModelDiscoveryService = ModelDiscoveryService; exports.ModelDiscoveryService = ModelDiscoveryService = ModelDiscoveryService_1 = __decorate([ (0, common_1.Injectable)(), __param(0, (0, typeorm_1.InjectRepository)(user_provider_entity_1.UserProvider)), __param(1, (0, typeorm_1.InjectRepository)(custom_provider_entity_1.CustomProvider)), __param(3, (0, common_1.Optional)()), __param(3, (0, common_1.Inject)(pricing_sync_service_1.PricingSyncService)), __param(4, (0, common_1.Optional)()), __param(4, (0, common_1.Inject)(models_dev_sync_service_1.ModelsDevSyncService)), __param(5, (0, common_1.Optional)()), __param(5, (0, common_1.Inject)(provider_model_registry_service_1.ProviderModelRegistryService)), __param(6, (0, common_1.Optional)()), __param(6, (0, common_1.Inject)(copilot_token_service_1.CopilotTokenService)), __metadata("design:paramtypes", [typeorm_2.Repository, typeorm_2.Repository, provider_model_fetcher_service_1.ProviderModelFetcherService, Object, Object, Object, Object]) ], ModelDiscoveryService); //# sourceMappingURL=model-discovery.service.js.map