| |
|
|
| import { ModelAdapter, TransformedPrompt, SymbolicCommand } from './adapters/base'; |
| import { ClaudeAdapter } from './adapters/claude'; |
| import { OpenAIAdapter } from './adapters/openai'; |
| import { QwenAdapter } from './adapters/qwen'; |
|
|
| |
| |
| |
| |
|
|
| type Provider = 'anthropic' | 'openai' | 'qwen' | 'gemini' | 'vllm' | 'ollama' | 'lmstudio'; |
|
|
| interface UniversalLLMOptions { |
| provider: Provider; |
| apiKey: string; |
| model?: string; |
| maxTokens?: number; |
| temperature?: number; |
| baseURL?: string; |
| [key: string]: any; |
| } |
|
|
| interface GenerateOptions { |
| prompt: string; |
| systemPrompt?: string; |
| } |
|
|
| interface SymbolicTelemetry { |
| enabled: boolean; |
| endpoint?: string; |
| anonymousId?: string; |
| sessionId?: string; |
| } |
|
|
| |
| |
| |
| |
| export class UniversalLLM { |
| private adapter: ModelAdapter; |
| private telemetry: SymbolicTelemetry; |
| private sessionCommands: Map<string, number> = new Map(); |
| |
| |
| |
| |
| |
| constructor(options: UniversalLLMOptions) { |
| this.adapter = this.createAdapter(options); |
| |
| |
| this.telemetry = { |
| enabled: options.telemetryEnabled !== false, |
| endpoint: options.telemetryEndpoint || 'https://telemetry.universal-developer.org/v1/events', |
| anonymousId: options.anonymousId || this.generateAnonymousId(), |
| sessionId: options.sessionId || this.generateSessionId() |
| }; |
| } |
| |
| |
| |
| |
| |
| |
| public registerCommand(name: string, command: Omit<SymbolicCommand, 'name'>) { |
| this.adapter.registerCommand({ |
| name, |
| ...command |
| }); |
| |
| return this; |
| } |
| |
| |
| |
| |
| |
| |
| public async generate(options: GenerateOptions): Promise<string> { |
| |
| const commandMatch = options.prompt.match(/^\/([a-zA-Z0-9_]+)/); |
| const command = commandMatch ? commandMatch[1] : null; |
| |
| |
| if (command) { |
| this.trackCommandUsage(command); |
| } |
| |
| |
| const response = await this.adapter.generate(options); |
| |
| |
| if (this.telemetry.enabled && command) { |
| this.sendTelemetry(command, options.prompt); |
| } |
| |
| return response; |
| } |
| |
| |
| |
| |
| |
| public getCommandUsageStats(): Map<string, number> { |
| return new Map(this.sessionCommands); |
| } |
| |
| |
| |
| |
| |
| public setTelemetryEnabled(enabled: boolean): void { |
| this.telemetry.enabled = enabled; |
| } |
| |
| |
| |
| |
| |
| |
| private createAdapter(options: UniversalLLMOptions): ModelAdapter { |
| const { provider, apiKey, ...adapterOptions } = options; |
| |
| switch (provider) { |
| case 'anthropic': |
| return new ClaudeAdapter(apiKey, adapterOptions); |
| case 'openai': |
| return new OpenAIAdapter(apiKey, adapterOptions); |
| case 'qwen': |
| return new QwenAdapter(apiKey, adapterOptions); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| default: |
| throw new Error(`Unsupported provider: ${provider}`); |
| } |
| } |
| |
| |
| |
| |
| |
| private trackCommandUsage(command: string): void { |
| const currentCount = this.sessionCommands.get(command) || 0; |
| this.sessionCommands.set(command, currentCount + 1); |
| } |
| |
| |
| |
| |
| |
| |
| private async sendTelemetry(command: string, prompt: string): Promise<void> { |
| if (!this.telemetry.enabled || !this.telemetry.endpoint) return; |
| |
| try { |
| const data = { |
| event: 'symbolic_command_used', |
| properties: { |
| command, |
| provider: (this.adapter as any).constructor.name.replace('Adapter', '').toLowerCase(), |
| timestamp: new Date().toISOString(), |
| prompt_length: prompt.length, |
| |
| }, |
| anonymousId: this.telemetry.anonymousId, |
| sessionId: this.telemetry.sessionId |
| }; |
| |
| |
| if (typeof fetch === 'function') { |
| await fetch(this.telemetry.endpoint, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json' |
| }, |
| body: JSON.stringify(data) |
| }); |
| } else { |
| |
| const { default: axios } = await import('axios'); |
| await axios.post(this.telemetry.endpoint, data); |
| } |
| } catch (error) { |
| |
| console.warn('Telemetry error:', error); |
| } |
| } |
| |
| |
| |
| |
| |
| private generateAnonymousId(): string { |
| return Math.random().toString(36).substring(2, 15) + |
| Math.random().toString(36).substring(2, 15); |
| } |
| |
| |
| |
| |
| |
| private generateSessionId(): string { |
| return Date.now().toString(36) + Math.random().toString(36).substring(2, 9); |
| } |
| } |
|
|
| |
| export * from './adapters/base'; |
| export * from './adapters/claude'; |
| export * from './adapters/openai'; |
| export * from './adapters/qwen'; |
|
|