@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and
469 lines (468 loc) • 17.1 kB
JavaScript
/**
* Model Resolver for NeuroLink CLI Commands
* Provides model resolution, search, and recommendation functionality
* Part of Phase 4.1 - Models Command System
*/
import { MODEL_REGISTRY, MODEL_ALIASES, USE_CASE_RECOMMENDATIONS, getAllModels, getModelById, getModelsByProvider, getAvailableProviders, calculateCost, formatModelForDisplay, } from "./modelRegistry.js";
import { isNonNullObject } from "../utils/typeUtils.js";
/**
* Model resolver class with advanced search and recommendation functionality
*/
export class ModelResolver {
/**
* Resolve model ID from alias or fuzzy name
*/
static resolveModel(query) {
const normalizedQuery = query.toLowerCase().trim();
// Exact match first
if (MODEL_REGISTRY[query]) {
return MODEL_REGISTRY[query];
}
// Alias match
if (MODEL_ALIASES[normalizedQuery]) {
const resolvedId = MODEL_ALIASES[normalizedQuery];
return MODEL_REGISTRY[resolvedId] || null;
}
// Fuzzy matching
const allModels = getAllModels();
// Try partial matching on ID
const idMatch = allModels.find((model) => model.id.toLowerCase().includes(normalizedQuery) ||
normalizedQuery.includes(model.id.toLowerCase()));
if (idMatch) {
return idMatch;
}
// Try partial matching on name
const nameMatch = allModels.find((model) => model.name.toLowerCase().includes(normalizedQuery) ||
normalizedQuery.includes(model.name.toLowerCase()));
if (nameMatch) {
return nameMatch;
}
// Try provider-specific matching
const providerMatch = allModels.find((model) => {
const providerQuery = `${model.provider}-${normalizedQuery}`;
return (model.id.toLowerCase().includes(providerQuery) ||
model.name.toLowerCase().includes(normalizedQuery));
});
if (providerMatch) {
return providerMatch;
}
return null;
}
/**
* Search models with advanced filtering
*/
static searchModels(filters) {
const allModels = getAllModels();
const results = [];
for (const model of allModels) {
const matchResult = this.evaluateModelMatch(model, filters);
if (matchResult.score > 0) {
results.push(matchResult);
}
}
// Sort by score (highest first)
return results.sort((a, b) => b.score - a.score);
}
/**
* Get best model for specific use case
*/
static getBestModel(context) {
const allModels = getAllModels();
const scored = allModels.map((model) => ({
model,
score: this.scoreModelForContext(model, context),
}));
// Sort by score
scored.sort((a, b) => b.score - a.score);
const best = scored[0];
const alternatives = scored.slice(1, 4).map((s) => s.model);
return {
model: best.model,
score: best.score,
reasoning: this.generateRecommendationReasoning(best.model, context),
alternatives,
};
}
/**
* Compare multiple models
*/
static compareModels(modelIds) {
const models = modelIds
.map((id) => this.resolveModel(id))
.filter((model) => model !== null);
if (models.length === 0) {
throw new Error(`No valid models found for comparison. Invalid IDs: ${modelIds.join(", ")}. Use 'models list' to see available model IDs.`);
}
// Build comparison data
const capabilities = {
vision: [],
functionCalling: [],
codeGeneration: [],
reasoning: [],
multimodal: [],
streaming: [],
jsonMode: [],
};
// Group models by capabilities
for (const model of models) {
for (const [capability, supported] of Object.entries(model.capabilities)) {
if (supported) {
capabilities[capability].push(model);
}
}
}
// Find pricing extremes
const sortedByCost = [...models].sort((a, b) => a.pricing.inputCostPer1K +
a.pricing.outputCostPer1K -
(b.pricing.inputCostPer1K + b.pricing.outputCostPer1K));
// Find context size extremes
const sortedByContext = [...models].sort((a, b) => b.limits.maxContextTokens - a.limits.maxContextTokens);
// Group by performance
const performance = {
fast: models.filter((m) => m.performance.speed === "fast"),
medium: models.filter((m) => m.performance.speed === "medium"),
slow: models.filter((m) => m.performance.speed === "slow"),
highQuality: models.filter((m) => m.performance.quality === "high"),
mediumQuality: models.filter((m) => m.performance.quality === "medium"),
lowQuality: models.filter((m) => m.performance.quality === "low"),
};
return {
models,
comparison: {
capabilities,
pricing: {
cheapest: sortedByCost[0],
mostExpensive: sortedByCost[sortedByCost.length - 1],
},
performance,
contextSize: {
largest: sortedByContext[0],
smallest: sortedByContext[sortedByContext.length - 1],
},
},
};
}
/**
* Get models by category
*/
static getModelsByCategory(category) {
return getAllModels().filter((model) => model.category === category);
}
/**
* Get recommended models for use case
*/
static getRecommendedModelsForUseCase(useCase) {
const recommendedIds = USE_CASE_RECOMMENDATIONS[useCase] || [];
return recommendedIds
.map((id) => getModelById(id))
.filter((model) => model !== null);
}
/**
* Calculate cost comparison for models
*/
static calculateCostComparison(models, inputTokens = 1000, outputTokens = 500) {
return models
.map((model) => ({
model,
cost: calculateCost(model, inputTokens, outputTokens),
costPer1K: model.pricing.inputCostPer1K + model.pricing.outputCostPer1K,
}))
.sort((a, b) => a.cost - b.cost);
}
/**
* Get model statistics
*/
static getModelStatistics() {
const allModels = getAllModels();
const providers = getAvailableProviders();
// Count by provider
const byProvider = providers.reduce((acc, provider) => {
acc[provider] = getModelsByProvider(provider).length;
return acc;
}, {});
// Count by category
const byCategory = allModels.reduce((acc, model) => {
acc[model.category] = (acc[model.category] || 0) + 1;
return acc;
}, {});
// Capability statistics
const capabilityStats = allModels.reduce((acc, model) => {
for (const [capability, supported] of Object.entries(model.capabilities)) {
if (supported) {
acc[capability] = (acc[capability] || 0) + 1;
}
}
return acc;
}, {});
// Cost statistics
const costs = allModels.map((m) => m.pricing.inputCostPer1K + m.pricing.outputCostPer1K);
const avgCost = costs.reduce((a, b) => a + b, 0) / costs.length;
const minCost = Math.min(...costs);
const maxCost = Math.max(...costs);
return {
total: allModels.length,
providers: Object.keys(byProvider).length,
byProvider,
byCategory,
capabilities: capabilityStats,
pricing: {
average: avgCost,
min: minCost,
max: maxCost,
free: allModels.filter((m) => m.pricing.inputCostPer1K === 0).length,
},
deprecated: allModels.filter((m) => m.deprecated).length,
};
}
/**
* Evaluate how well a model matches search filters
*/
static evaluateModelMatch(model, filters) {
let score = 0;
const matchReasons = [];
// Provider filter
if (filters.provider) {
const providers = Array.isArray(filters.provider)
? filters.provider
: [filters.provider];
if (providers.includes(model.provider)) {
score += 10;
matchReasons.push(`Provider: ${model.provider}`);
}
else {
return { model, score: 0, matchReasons: [] };
}
}
// Capability filter
if (filters.capability) {
const capabilities = Array.isArray(filters.capability)
? filters.capability
: [filters.capability];
for (const capability of capabilities) {
if (model.capabilities[capability]) {
score += 15;
matchReasons.push(`Has capability: ${capability}`);
}
}
}
// Use case filter
if (filters.useCase) {
const useCaseScore = model.useCases[filters.useCase];
if (useCaseScore >= 7) {
score += useCaseScore * 2;
matchReasons.push(`Good for ${filters.useCase} (${useCaseScore}/10)`);
}
}
// Cost filter
if (filters.maxCost) {
const totalCost = model.pricing.inputCostPer1K + model.pricing.outputCostPer1K;
if (totalCost <= filters.maxCost) {
score += 5;
matchReasons.push(`Within cost limit ($${totalCost.toFixed(6)}/1K)`);
}
else {
score -= 5;
}
}
// Context size filters
if (filters.minContextSize &&
model.limits.maxContextTokens >= filters.minContextSize) {
score += 5;
matchReasons.push(`Meets min context: ${model.limits.maxContextTokens}`);
}
if (filters.maxContextSize &&
model.limits.maxContextTokens <= filters.maxContextSize) {
score += 3;
matchReasons.push(`Within max context: ${model.limits.maxContextTokens}`);
}
// Performance filters
if (filters.performance) {
if (model.performance.speed === filters.performance ||
model.performance.quality === filters.performance) {
score += 8;
matchReasons.push(`Performance: ${filters.performance}`);
}
}
// Category filter
if (filters.category) {
const categories = Array.isArray(filters.category)
? filters.category
: [filters.category];
if (categories.includes(model.category)) {
score += 7;
matchReasons.push(`Category: ${model.category}`);
}
}
// Base relevance score
if (score === 0) {
score = 1; // Minimal relevance for model
matchReasons.push("Basic match");
}
return { model, score, matchReasons };
}
/**
* Score model for recommendation context
*/
static scoreModelForContext(model, context) {
let score = 0;
// Use case scoring
if (context.useCase) {
score += model.useCases[context.useCase] * 10;
}
// Cost scoring
if (context.maxCost) {
const totalCost = model.pricing.inputCostPer1K + model.pricing.outputCostPer1K;
if (totalCost <= context.maxCost) {
score += 20;
}
else {
score -= 30; // Heavy penalty for exceeding cost
}
}
// Quality scoring
if (context.minQuality) {
const qualityScore = context.minQuality === "high"
? 3
: context.minQuality === "medium"
? 2
: 1;
const modelQuality = model.performance.quality === "high"
? 3
: model.performance.quality === "medium"
? 2
: 1;
if (modelQuality >= qualityScore) {
score += 15;
}
else {
score -= 10;
}
}
// Required capabilities
if (context.requireCapabilities) {
for (const capability of context.requireCapabilities) {
if (model.capabilities[capability]) {
score += 12;
}
else {
score -= 25; // Heavy penalty for missing required capability
}
}
}
// Provider exclusions
if (context.excludeProviders?.includes(model.provider)) {
score -= 50;
}
// Context size requirements
if (context.contextSize &&
model.limits.maxContextTokens >= context.contextSize) {
score += 10;
}
// Local preference
if (context.preferLocal && model.isLocal) {
score += 15;
}
// Deprecated models penalty
if (model.deprecated) {
score -= 20;
}
return Math.max(0, score); // Ensure non-negative score
}
/**
* Generate recommendation reasoning
*/
static generateRecommendationReasoning(model, context) {
const reasons = [];
if (context.useCase) {
const score = model.useCases[context.useCase];
reasons.push(`Excellent for ${context.useCase} (${score}/10 rating)`);
}
if (model.pricing.inputCostPer1K === 0) {
reasons.push("Free to use (local execution)");
}
else {
const costPer1K = model.pricing.inputCostPer1K + model.pricing.outputCostPer1K;
if (costPer1K < 0.001) {
reasons.push("Very cost-effective");
}
}
if (model.performance.quality === "high") {
reasons.push("High-quality outputs");
}
if (model.performance.speed === "fast") {
reasons.push("Fast response times");
}
if (model.limits.maxContextTokens > 100000) {
reasons.push("Large context window for complex tasks");
}
const strongCapabilities = Object.entries(model.capabilities)
.filter(([_, supported]) => supported)
.map(([capability]) => capability);
if (strongCapabilities.length > 0) {
reasons.push(`Supports: ${strongCapabilities.join(", ")}`);
}
if (model.releaseDate) {
const releaseYear = new Date(model.releaseDate).getFullYear();
if (releaseYear >= 2024) {
reasons.push("Recent model with latest capabilities");
}
}
return reasons;
}
}
/**
* Utility functions for CLI integration
*/
/**
* Format search results for CLI display
*/
export function formatSearchResults(results) {
return results.map((result) => {
const modelDisplay = formatModelForDisplay(result.model);
return {
...(isNonNullObject(modelDisplay)
? modelDisplay
: {}),
relevanceScore: result.score,
matchReasons: result.matchReasons,
};
});
}
/**
* Format recommendation for CLI display
*/
export function formatRecommendation(recommendation) {
return {
recommended: formatModelForDisplay(recommendation.model),
score: recommendation.score,
reasoning: recommendation.reasoning,
alternatives: recommendation.alternatives.map(formatModelForDisplay),
};
}
/**
* Format model comparison for CLI display
*/
export function formatComparison(comparison) {
return {
models: comparison.models.map(formatModelForDisplay),
summary: {
capabilities: Object.entries(comparison.comparison.capabilities).map(([capability, models]) => ({
capability,
supportedBy: models.map((m) => m.id),
count: models.length,
})),
pricing: {
cheapest: comparison.comparison.pricing.cheapest.id,
mostExpensive: comparison.comparison.pricing.mostExpensive.id,
},
contextSize: {
largest: comparison.comparison.contextSize.largest.id,
smallest: comparison.comparison.contextSize.smallest.id,
},
performance: Object.entries(comparison.comparison.performance).map(([type, models]) => ({
type,
models: models.map((m) => m.id),
})),
},
};
}