data fixes, partial custom indicator support

This commit is contained in:
2026-04-08 21:28:31 -04:00
parent b701554996
commit a70dcd954f
81 changed files with 5438 additions and 1852 deletions

View File

@@ -1,5 +1,5 @@
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
import { ChatAnthropic } from '@langchain/anthropic';
import { ChatOpenAI } from '@langchain/openai';
import type { FastifyBaseLogger } from 'fastify';
import { type ModelMiddleware, NoopMiddleware, AnthropicCachingMiddleware } from './middleware.js';
@@ -10,7 +10,7 @@ export { NoopMiddleware, AnthropicCachingMiddleware };
* Supported LLM providers
*/
export enum LLMProvider {
ANTHROPIC = 'anthropic',
DEEP_INFRA = 'deepinfra',
}
/**
@@ -47,11 +47,13 @@ export interface LicenseModelsConfig {
* Provider configuration with API keys
*/
export interface ProviderConfig {
anthropicApiKey?: string;
deepinfraApiKey?: string;
defaultModel?: ModelConfig;
licenseModels?: LicenseModelsConfig;
}
const DEEP_INFRA_BASE_URL = 'https://api.deepinfra.com/v1/openai';
/**
* LLM Provider factory
* Creates model instances with unified interface across providers
@@ -75,8 +77,8 @@ export class LLMProviderFactory {
);
switch (modelConfig.provider) {
case LLMProvider.ANTHROPIC:
return this.createAnthropicModel(modelConfig);
case LLMProvider.DEEP_INFRA:
return this.createDeepInfraModel(modelConfig);
default:
throw new Error(`Unsupported provider: ${modelConfig.provider}`);
@@ -84,22 +86,24 @@ export class LLMProviderFactory {
}
/**
* Create Anthropic Claude model
* Create Deep Infra model via OpenAI-compatible API
*/
private createAnthropicModel(config: ModelConfig): { model: ChatAnthropic; middleware: AnthropicCachingMiddleware } {
if (!this.config.anthropicApiKey) {
throw new Error('Anthropic API key not configured');
private createDeepInfraModel(config: ModelConfig): { model: ChatOpenAI; middleware: NoopMiddleware } {
if (!this.config.deepinfraApiKey) {
throw new Error('Deep Infra API key not configured');
}
const model = new ChatAnthropic({
const model = new ChatOpenAI({
model: config.model,
temperature: config.temperature ?? 0.7,
maxTokens: config.maxTokens ?? 4096,
anthropicApiKey: this.config.anthropicApiKey,
clientOptions: { defaultHeaders: { 'anthropic-beta': 'prompt-caching-2024-07-31' } },
apiKey: this.config.deepinfraApiKey,
configuration: {
baseURL: DEEP_INFRA_BASE_URL,
},
});
return { model, middleware: new AnthropicCachingMiddleware() };
return { model, middleware: new NoopMiddleware() };
}
/**
@@ -110,13 +114,13 @@ export class LLMProviderFactory {
return this.config.defaultModel;
}
if (!this.config.anthropicApiKey) {
throw new Error('Anthropic API key not configured');
if (!this.config.deepinfraApiKey) {
throw new Error('Deep Infra API key not configured');
}
return {
provider: LLMProvider.ANTHROPIC,
model: 'claude-sonnet-4-6',
provider: LLMProvider.DEEP_INFRA,
model: 'zai-org/GLM-5',
};
}
@@ -132,16 +136,12 @@ export class LLMProviderFactory {
* Predefined model configurations
*/
export const MODELS = {
CLAUDE_SONNET: {
provider: LLMProvider.ANTHROPIC,
model: 'claude-sonnet-4-6',
GLM_5: {
provider: LLMProvider.DEEP_INFRA,
model: 'zai-org/GLM-5',
},
CLAUDE_HAIKU: {
provider: LLMProvider.ANTHROPIC,
model: 'claude-haiku-4-5-20251001',
},
CLAUDE_OPUS: {
provider: LLMProvider.ANTHROPIC,
model: 'claude-opus-4-6',
QWEN_235B: {
provider: LLMProvider.DEEP_INFRA,
model: 'Qwen/Qwen3-235B-A22B-Instruct-2507',
},
} as const satisfies Record<string, ModelConfig>;

View File

@@ -113,17 +113,17 @@ export class ModelRouter {
// Fallback to hardcoded defaults
if (license.licenseType === 'enterprise') {
return isComplex
? { provider: LLMProvider.ANTHROPIC, model: 'claude-opus-4-6' }
: { provider: LLMProvider.ANTHROPIC, model: 'claude-sonnet-4-6' };
? { provider: LLMProvider.DEEP_INFRA, model: 'Qwen/Qwen3-235B-A22B-Instruct-2507' }
: { provider: LLMProvider.DEEP_INFRA, model: 'zai-org/GLM-5' };
}
if (license.licenseType === 'pro') {
return isComplex
? { provider: LLMProvider.ANTHROPIC, model: 'claude-sonnet-4-6' }
: { provider: LLMProvider.ANTHROPIC, model: 'claude-haiku-4-5-20251001' };
? { provider: LLMProvider.DEEP_INFRA, model: 'zai-org/GLM-5' }
: { provider: LLMProvider.DEEP_INFRA, model: 'zai-org/GLM-5' };
}
return { provider: LLMProvider.ANTHROPIC, model: 'claude-haiku-4-5-20251001' };
return { provider: LLMProvider.DEEP_INFRA, model: 'zai-org/GLM-5' };
}
/**
@@ -141,13 +141,13 @@ export class ModelRouter {
// Fallback to hardcoded defaults
switch (license.licenseType) {
case 'enterprise':
return { provider: LLMProvider.ANTHROPIC, model: 'claude-sonnet-4-6' };
return { provider: LLMProvider.DEEP_INFRA, model: 'zai-org/GLM-5' };
case 'pro':
return { provider: LLMProvider.ANTHROPIC, model: 'claude-sonnet-4-6' };
return { provider: LLMProvider.DEEP_INFRA, model: 'zai-org/GLM-5' };
case 'free':
return { provider: LLMProvider.ANTHROPIC, model: 'claude-haiku-4-5-20251001' };
return { provider: LLMProvider.DEEP_INFRA, model: 'zai-org/GLM-5' };
default:
return this.defaultModel;
@@ -166,8 +166,8 @@ export class ModelRouter {
}
}
// Fallback: use Haiku for cost efficiency
return { provider: LLMProvider.ANTHROPIC, model: 'claude-haiku-4-5-20251001' };
// Fallback: use GLM-5
return { provider: LLMProvider.DEEP_INFRA, model: 'zai-org/GLM-5' };
}
/**
@@ -195,12 +195,12 @@ export class ModelRouter {
// Fallback to hardcoded defaults
if (license.licenseType === 'free') {
const allowedModels = ['claude-haiku-4-5-20251001'];
const allowedModels = ['zai-org/GLM-5'];
return allowedModels.includes(model.model);
}
if (license.licenseType === 'pro') {
const blockedModels = ['claude-opus-4-6'];
const blockedModels = ['Qwen/Qwen3-235B-A22B-Instruct-2507'];
return !blockedModels.includes(model.model);
}