| | |
| |
|
| | 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'; |
| |
|