client-py connected

This commit is contained in:
2026-03-27 16:33:40 -04:00
parent c76887ab92
commit c3a8fae132
55 changed files with 1598 additions and 426 deletions

View File

@@ -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);
}