client-py connected
This commit is contained in:
@@ -19,11 +19,33 @@ export interface ModelConfig {
|
||||
maxTokens?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* License tier model configuration
|
||||
*/
|
||||
export interface LicenseTierModels {
|
||||
default: string;
|
||||
cost_optimized: string;
|
||||
complex: string;
|
||||
allowed_models?: string[];
|
||||
blocked_models?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* License models configuration
|
||||
*/
|
||||
export interface LicenseModelsConfig {
|
||||
free: LicenseTierModels;
|
||||
pro: LicenseTierModels;
|
||||
enterprise: LicenseTierModels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider configuration with API keys
|
||||
*/
|
||||
export interface ProviderConfig {
|
||||
anthropicApiKey?: string;
|
||||
defaultModel?: ModelConfig;
|
||||
licenseModels?: LicenseModelsConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,15 +99,26 @@ export class LLMProviderFactory {
|
||||
* Get default model based on environment
|
||||
*/
|
||||
getDefaultModel(): ModelConfig {
|
||||
if (this.config.defaultModel) {
|
||||
return this.config.defaultModel;
|
||||
}
|
||||
|
||||
if (!this.config.anthropicApiKey) {
|
||||
throw new Error('Anthropic API key not configured');
|
||||
}
|
||||
|
||||
return {
|
||||
provider: LLMProvider.ANTHROPIC,
|
||||
model: 'claude-3-5-sonnet-20241022',
|
||||
model: 'claude-sonnet-4-6',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get license models configuration
|
||||
*/
|
||||
getLicenseModelsConfig(): LicenseModelsConfig | undefined {
|
||||
return this.config.licenseModels;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,14 +127,14 @@ export class LLMProviderFactory {
|
||||
export const MODELS = {
|
||||
CLAUDE_SONNET: {
|
||||
provider: LLMProvider.ANTHROPIC,
|
||||
model: 'claude-3-5-sonnet-20241022',
|
||||
model: 'claude-sonnet-4-6',
|
||||
},
|
||||
CLAUDE_HAIKU: {
|
||||
provider: LLMProvider.ANTHROPIC,
|
||||
model: 'claude-3-5-haiku-20241022',
|
||||
model: 'claude-haiku-4-5-20251001',
|
||||
},
|
||||
CLAUDE_OPUS: {
|
||||
provider: LLMProvider.ANTHROPIC,
|
||||
model: 'claude-3-opus-20240229',
|
||||
model: 'claude-opus-4-6',
|
||||
},
|
||||
} as const satisfies Record<string, ModelConfig>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
||||
import type { FastifyBaseLogger } from 'fastify';
|
||||
import { LLMProviderFactory, type ModelConfig, LLMProvider } from './provider.js';
|
||||
import { LLMProviderFactory, type ModelConfig, LLMProvider, type LicenseModelsConfig } from './provider.js';
|
||||
import type { UserLicense } from '../types/user.js';
|
||||
|
||||
/**
|
||||
@@ -25,11 +25,13 @@ export class ModelRouter {
|
||||
private factory: LLMProviderFactory;
|
||||
private logger: FastifyBaseLogger;
|
||||
private defaultModel: ModelConfig;
|
||||
private licenseModels?: LicenseModelsConfig;
|
||||
|
||||
constructor(factory: LLMProviderFactory, logger: FastifyBaseLogger) {
|
||||
this.factory = factory;
|
||||
this.logger = logger;
|
||||
this.defaultModel = factory.getDefaultModel();
|
||||
this.licenseModels = factory.getLicenseModelsConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,37 +99,53 @@ export class ModelRouter {
|
||||
private routeByComplexity(message: string, license: UserLicense): ModelConfig {
|
||||
const isComplex = this.isComplexQuery(message);
|
||||
|
||||
// Use configuration if available
|
||||
if (this.licenseModels) {
|
||||
const tierConfig = this.licenseModels[license.licenseType];
|
||||
if (tierConfig) {
|
||||
const model = isComplex ? tierConfig.complex : tierConfig.default;
|
||||
return { provider: this.defaultModel.provider as LLMProvider, model };
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to hardcoded defaults
|
||||
if (license.licenseType === 'enterprise') {
|
||||
// Enterprise users get best models for complex queries
|
||||
return isComplex
|
||||
? { provider: LLMProvider.ANTHROPIC, model: 'claude-3-opus-20240229' }
|
||||
: { provider: LLMProvider.ANTHROPIC, model: 'claude-3-5-sonnet-20241022' };
|
||||
? { provider: LLMProvider.ANTHROPIC, model: 'claude-opus-4-6' }
|
||||
: { provider: LLMProvider.ANTHROPIC, model: 'claude-sonnet-4-6' };
|
||||
}
|
||||
|
||||
if (license.licenseType === 'pro') {
|
||||
// Pro users get good models
|
||||
return isComplex
|
||||
? { provider: LLMProvider.ANTHROPIC, model: 'claude-3-5-sonnet-20241022' }
|
||||
: { provider: LLMProvider.ANTHROPIC, model: 'claude-3-5-haiku-20241022' };
|
||||
? { provider: LLMProvider.ANTHROPIC, model: 'claude-sonnet-4-6' }
|
||||
: { provider: LLMProvider.ANTHROPIC, model: 'claude-haiku-4-5-20251001' };
|
||||
}
|
||||
|
||||
// Free users get efficient models
|
||||
return { provider: LLMProvider.ANTHROPIC, model: 'claude-3-5-haiku-20241022' };
|
||||
return { provider: LLMProvider.ANTHROPIC, model: 'claude-haiku-4-5-20251001' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Route based on license tier
|
||||
*/
|
||||
private routeByLicenseTier(license: UserLicense): ModelConfig {
|
||||
// Use configuration if available
|
||||
if (this.licenseModels) {
|
||||
const tierConfig = this.licenseModels[license.licenseType];
|
||||
if (tierConfig) {
|
||||
return { provider: this.defaultModel.provider as LLMProvider, model: tierConfig.default };
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to hardcoded defaults
|
||||
switch (license.licenseType) {
|
||||
case 'enterprise':
|
||||
return { provider: LLMProvider.ANTHROPIC, model: 'claude-3-5-sonnet-20241022' };
|
||||
return { provider: LLMProvider.ANTHROPIC, model: 'claude-sonnet-4-6' };
|
||||
|
||||
case 'pro':
|
||||
return { provider: LLMProvider.ANTHROPIC, model: 'claude-3-5-sonnet-20241022' };
|
||||
return { provider: LLMProvider.ANTHROPIC, model: 'claude-sonnet-4-6' };
|
||||
|
||||
case 'free':
|
||||
return { provider: LLMProvider.ANTHROPIC, model: 'claude-3-5-haiku-20241022' };
|
||||
return { provider: LLMProvider.ANTHROPIC, model: 'claude-haiku-4-5-20251001' };
|
||||
|
||||
default:
|
||||
return this.defaultModel;
|
||||
@@ -137,24 +155,50 @@ export class ModelRouter {
|
||||
/**
|
||||
* Route to cheapest available model
|
||||
*/
|
||||
private routeByCost(_license: UserLicense): ModelConfig {
|
||||
// All tiers: use Haiku for cost efficiency
|
||||
return { provider: LLMProvider.ANTHROPIC, model: 'claude-3-5-haiku-20241022' };
|
||||
private routeByCost(license: UserLicense): ModelConfig {
|
||||
// Use configuration if available
|
||||
if (this.licenseModels) {
|
||||
const tierConfig = this.licenseModels[license.licenseType];
|
||||
if (tierConfig) {
|
||||
return { provider: this.defaultModel.provider as LLMProvider, model: tierConfig.cost_optimized };
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: use Haiku for cost efficiency
|
||||
return { provider: LLMProvider.ANTHROPIC, model: 'claude-haiku-4-5-20251001' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if model is allowed for user's license
|
||||
*/
|
||||
private isModelAllowed(model: ModelConfig, license: UserLicense): boolean {
|
||||
// Free tier: only Haiku
|
||||
// Use configuration if available
|
||||
if (this.licenseModels) {
|
||||
const tierConfig = this.licenseModels[license.licenseType];
|
||||
if (tierConfig) {
|
||||
// Check allowed_models list if defined
|
||||
if (tierConfig.allowed_models && tierConfig.allowed_models.length > 0) {
|
||||
return tierConfig.allowed_models.includes(model.model);
|
||||
}
|
||||
|
||||
// Check blocked_models list if defined
|
||||
if (tierConfig.blocked_models && tierConfig.blocked_models.length > 0) {
|
||||
return !tierConfig.blocked_models.includes(model.model);
|
||||
}
|
||||
|
||||
// No restrictions if neither list is defined
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to hardcoded defaults
|
||||
if (license.licenseType === 'free') {
|
||||
const allowedModels = ['claude-3-5-haiku-20241022'];
|
||||
const allowedModels = ['claude-haiku-4-5-20251001'];
|
||||
return allowedModels.includes(model.model);
|
||||
}
|
||||
|
||||
// Pro: all except Opus
|
||||
if (license.licenseType === 'pro') {
|
||||
const blockedModels = ['claude-3-opus-20240229'];
|
||||
const blockedModels = ['claude-opus-4-6'];
|
||||
return !blockedModels.includes(model.model);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user