UNPKG

@phantasy/sheesh-agent

Version:

AI Agent Framework with multi-provider support and real-time broadcasting

1,651 lines (1,640 loc) 90.5 kB
import OpenAI from 'openai'; // src/core/types.ts var AIFrameworkError = class extends Error { constructor(message, code, provider, details) { super(message); this.code = code; this.provider = provider; this.details = details; this.name = "AIFrameworkError"; } }; var ProviderError = class extends AIFrameworkError { constructor(message, provider, details) { super(message, "PROVIDER_ERROR", provider, details); this.name = "ProviderError"; } }; var ConfigurationError = class extends AIFrameworkError { constructor(message, details) { super(message, "CONFIGURATION_ERROR", void 0, details); this.name = "ConfigurationError"; } }; var RateLimitError = class extends AIFrameworkError { constructor(message, provider, retryAfter) { super(message, "RATE_LIMIT_ERROR", provider, { retryAfter }); this.name = "RateLimitError"; } }; // src/core/base-provider.ts var BaseProvider = class { constructor(id, name, type) { this.id = id; this.name = name; this.type = type; } config = {}; initialized = false; async initialize(config) { this.config = { ...this.config, ...config }; await this.onInitialize(); this.initialized = true; } async isAvailable() { if (!this.initialized) { return false; } try { return await this.checkAvailability(); } catch { return false; } } async getUsage() { if (!this.initialized) { throw new Error(`Provider ${this.name} not initialized`); } return this.fetchUsage(); } ensureInitialized() { if (!this.initialized) { throw new Error(`Provider ${this.name} not initialized. Call initialize() first.`); } } }; // src/core/agent-controller.ts var AgentController = class { constructor(conversationService, initialPersonality) { this.conversationService = conversationService; this.state = { personality: initialPersonality, currentEmotion: "neutral", energyLevel: 80, tipGoals: [], activeAnimations: [], cameraPosition: { x: 0, y: 0, z: 5 }, lastInteraction: /* @__PURE__ */ new Date(), conversationContext: [] }; } state; memory = /* @__PURE__ */ new Map(); actionQueue = []; async processUserMessage(userId, message, metadata) { this.updateUserMemory(userId, message); const baseResponse = await this.conversationService.processMessage( userId, message, this.state.personality, { ...metadata, agentState: this.getPublicState(), userMemory: this.getUserMemory(userId) } ); const enhancedActions = await this.analyzeAndGenerateActions(message, baseResponse, userId); this.updateAgentState(message, enhancedActions); const { actions: _, ...responseWithoutActions } = baseResponse; return { ...responseWithoutActions, actions: enhancedActions }; } async analyzeAndGenerateActions(message, response, userId) { const actions = []; const lowerMessage = message.toLowerCase(); this.getUserMemory(userId); if (lowerMessage.includes("dance") || lowerMessage.includes("move")) { actions.push({ type: "vrm", action: "animation", value: "dance-basic", duration: 5e3 }); } if (lowerMessage.includes("smile") || response.emotion === "happy") { actions.push({ type: "vrm", action: "expression", value: "smile", duration: 3e3, intensity: 0.8 }); } if (lowerMessage.includes("wave") || lowerMessage.includes("hello")) { actions.push({ type: "vrm", action: "gesture", value: "wave", duration: 2e3 }); } if (lowerMessage.includes("closer") || lowerMessage.includes("zoom")) { actions.push({ type: "camera", action: "zoom", value: 3, duration: 2e3, easing: "ease-in-out" }); } if (lowerMessage.includes("face") || lowerMessage.includes("look at me")) { actions.push({ type: "camera", action: "preset", value: "face-closeup", duration: 1500 }); } if (lowerMessage.includes("tip") || lowerMessage.includes("support")) { actions.push({ type: "tip", action: "request", value: "Thank you for wanting to support me! Tips help me learn new animations!", amount: 0.1, currency: "SOL", animation: "thank-you" }); } if (lowerMessage.includes("draw") || lowerMessage.includes("picture")) { actions.push({ type: "canvas", action: "draw", value: "Generate a cute illustration based on our conversation", position: { x: 0.7, y: 0.3 }, size: { width: 300, height: 300 } }); } if (response.emotion === "excited") { actions.push({ type: "audio", action: "play", value: "excitement-ambient", volume: 0.3, fadeIn: 1e3 }); } return actions; } updateUserMemory(userId, message) { if (!this.memory.has(userId)) { this.memory.set(userId, { userId, interactions: 0, favoriteTopics: [], tipHistory: [], preferences: {} }); } const memory = this.memory.get(userId); memory.interactions++; memory.favoriteTopics = this.extractTopics(message); } updateAgentState(message, actions) { this.state.lastInteraction = /* @__PURE__ */ new Date(); this.state.conversationContext.push(message); if (this.state.conversationContext.length > 10) { this.state.conversationContext = this.state.conversationContext.slice(-10); } this.state.energyLevel = Math.min(100, this.state.energyLevel + 2); actions.forEach((action) => { if (action.type === "vrm" && action.action === "animation") { const animationValue = String(action.value); this.state.activeAnimations.push(animationValue); setTimeout(() => { const index = this.state.activeAnimations.indexOf(animationValue); if (index > -1) { this.state.activeAnimations.splice(index, 1); } }, action.duration || 3e3); } }); } extractTopics(message) { const words = message.toLowerCase().split(" "); const topics = ["dance", "music", "art", "games", "anime", "tech", "food"]; return topics.filter((topic) => words.includes(topic)); } getUserMemory(userId) { return this.memory.get(userId); } getPublicState() { const { conversationContext: _conversationContext, ...publicState } = this.state; return publicState; } addTipGoal(goal) { const id = `goal-${Date.now()}`; this.state.tipGoals.push({ ...goal, id, currentAmount: 0, isActive: true }); return id; } updateTipGoal(goalId, amount) { const goal = this.state.tipGoals.find((g) => g.id === goalId); if (!goal) return false; goal.currentAmount += amount; if (goal.currentAmount >= goal.targetAmount) { goal.isActive = false; this.actionQueue.push({ type: "vrm", action: "animation", value: goal.animation || "celebration", duration: 5e3 }); } return true; } getActiveTipGoals() { return this.state.tipGoals.filter((goal) => goal.isActive); } queueAction(action) { this.actionQueue.push(action); } getNextAction() { return this.actionQueue.shift(); } updatePersonality(updates) { this.state.personality = { ...this.state.personality, ...updates }; } setEmotion(emotion) { this.state.currentEmotion = emotion; } getCameraPosition() { return this.state.cameraPosition; } setCameraPosition(position) { this.state.cameraPosition = position; } }; var OpenAIText = class extends BaseProvider { client; constructor() { super("openai-text", "OpenAI Text", "text"); } async onInitialize() { if (!this.config.apiKey) { throw new ProviderError("OpenAI API key is required", this.id); } this.client = new OpenAI({ apiKey: this.config.apiKey, baseURL: this.config.baseUrl, timeout: this.config.timeout || 3e4, maxRetries: this.config.maxRetries || 3 }); } async checkAvailability() { try { const models = await this.client.models.list(); return models.data.length > 0; } catch { return false; } } async fetchUsage() { return { tokensUsed: 0, requestsCount: 0, cost: 0 }; } async generate(prompt, options) { this.ensureInitialized(); const messages = []; if (options?.systemPrompt) { messages.push({ role: "system", content: options.systemPrompt }); } messages.push({ role: "user", content: prompt }); return this.generateWithMessages(messages, options); } async generateWithMessages(messages, options) { this.ensureInitialized(); try { const completion = await this.client.chat.completions.create({ model: options?.model || "gpt-4-turbo-preview", messages: messages.map((m) => ({ role: m.role, content: m.content })), temperature: options?.temperature, max_tokens: options?.maxTokens, top_p: options?.topP, frequency_penalty: options?.frequencyPenalty, presence_penalty: options?.presencePenalty, stop: options?.stop, stream: false }); const choice = completion.choices[0]; return { text: choice.message.content || "", model: completion.model, usage: completion.usage ? { promptTokens: completion.usage.prompt_tokens, completionTokens: completion.usage.completion_tokens, totalTokens: completion.usage.total_tokens } : void 0, finishReason: choice.finish_reason || void 0 }; } catch (error) { if (error.status === 429) { throw new ProviderError("Rate limit exceeded", this.id, { retryAfter: error.headers?.["retry-after"] }); } throw new ProviderError(error.message || "Failed to generate text", this.id, error); } } async *stream(prompt, options) { this.ensureInitialized(); const messages = []; if (options?.systemPrompt) { messages.push({ role: "system", content: options.systemPrompt }); } messages.push({ role: "user", content: prompt }); try { const stream = await this.client.chat.completions.create({ model: options?.model || "gpt-4-turbo-preview", messages: messages.map((m) => ({ role: m.role, content: m.content })), temperature: options?.temperature, max_tokens: options?.maxTokens, top_p: options?.topP, frequency_penalty: options?.frequencyPenalty, presence_penalty: options?.presencePenalty, stop: options?.stop, stream: true }); for await (const chunk of stream) { const content = chunk.choices[0]?.delta?.content; if (content) { yield content; } } } catch (error) { throw new ProviderError(error.message || "Failed to stream text", this.id, error); } } }; // src/providers/text/alkahest.ts var AlkahestText = class extends BaseProvider { baseUrl = ""; apiKey; constructor() { super("alkahest-text", "Alkahest Text", "text"); } async onInitialize() { this.baseUrl = this.config.baseUrl || "http://localhost:9999"; this.apiKey = this.config.apiKey; if (!this.apiKey && !this.config.privyToken) { throw new ProviderError("Alkahest API key or Privy token is required", this.id); } } async checkAvailability() { try { const response = await fetch(`${this.baseUrl}/health`); return response.ok; } catch { return false; } } async fetchUsage() { try { const response = await fetch(`${this.baseUrl}/api/v1/mana/balance`, { headers: this.getHeaders() }); if (!response.ok) return { tokensUsed: 0, requestsCount: 0, cost: 0 }; const data = await response.json(); return { remaining: data.balance, tokensUsed: 0, requestsCount: 0, cost: 0 }; } catch { return { tokensUsed: 0, requestsCount: 0, cost: 0 }; } } getHeaders() { const headers = { "Content-Type": "application/json" }; if (this.apiKey) { headers["Authorization"] = `Bearer ${this.apiKey}`; } else if (this.config.privyToken) { headers["Authorization"] = `Bearer ${this.config.privyToken}`; } return headers; } async generate(prompt, options) { const messages = []; if (options?.systemPrompt) { messages.push({ role: "system", content: options.systemPrompt }); } messages.push({ role: "user", content: prompt }); return this.generateWithMessages(messages, options); } async generateWithMessages(messages, options) { this.ensureInitialized(); try { const response = await fetch(`${this.baseUrl}/api/v1/chat/completions`, { method: "POST", headers: this.getHeaders(), body: JSON.stringify({ model: options?.model || "gpt-4o", messages: messages.map((m) => ({ role: m.role, content: m.content })), stream: false, temperature: options?.temperature, max_tokens: options?.maxTokens, top_p: options?.topP, frequency_penalty: options?.frequencyPenalty, presence_penalty: options?.presencePenalty, stop: options?.stop }) }); if (!response.ok) { const error = await response.json().catch(() => ({ error: "Unknown error" })); if (response.status === 429) { const retryAfter = response.headers.get("X-RateLimit-Reset"); throw new RateLimitError( "Rate limit exceeded", this.id, retryAfter ? parseInt(retryAfter) : void 0 ); } if (response.status === 402) { throw new ProviderError("Insufficient mana balance", this.id, error); } throw new ProviderError( error.error?.message || error.error || "Failed to generate text", this.id, error ); } const data = await response.json(); const choice = data.choices[0]; return { text: choice.message.content || "", model: data.model, usage: data.usage ? { promptTokens: data.usage.prompt_tokens, completionTokens: data.usage.completion_tokens, totalTokens: data.usage.total_tokens } : void 0, finishReason: choice.finish_reason || void 0 }; } catch (error) { if (error instanceof ProviderError || error instanceof RateLimitError) { throw error; } throw new ProviderError(error.message || "Failed to generate text", this.id, error); } } async *stream(prompt, options) { const messages = []; if (options?.systemPrompt) { messages.push({ role: "system", content: options.systemPrompt }); } messages.push({ role: "user", content: prompt }); yield* this.streamWithMessages(messages, options); } async *streamWithMessages(messages, options) { this.ensureInitialized(); try { const response = await fetch(`${this.baseUrl}/api/v1/chat/completions`, { method: "POST", headers: this.getHeaders(), body: JSON.stringify({ model: options?.model || "gpt-4o", messages: messages.map((m) => ({ role: m.role, content: m.content })), stream: true, temperature: options?.temperature, max_tokens: options?.maxTokens, top_p: options?.topP, frequency_penalty: options?.frequencyPenalty, presence_penalty: options?.presencePenalty, stop: options?.stop }) }); if (!response.ok) { const error = await response.json().catch(() => ({ error: "Unknown error" })); throw new ProviderError( error.error?.message || error.error || "Failed to stream text", this.id, error ); } const reader = response.body?.getReader(); if (!reader) { throw new ProviderError("No response body", this.id); } const decoder = new TextDecoder(); let buffer = ""; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop() || ""; for (const line of lines) { if (line.startsWith("data: ")) { const data = line.slice(6); if (data === "[DONE]") return; try { const parsed = JSON.parse(data); const content = parsed.choices[0]?.delta?.content; if (content) { yield content; } } catch (_e) { } } } } } catch (error) { if (error instanceof ProviderError) { throw error; } throw new ProviderError(error.message || "Failed to stream text", this.id, error); } } }; var MahoText = class extends BaseProvider { client; constructor() { super("maho-text", "Maho Text", "text"); } async onInitialize() { if (!this.config.apiKey) { throw new ProviderError("Maho API key is required", this.id); } this.client = new OpenAI({ apiKey: this.config.apiKey, baseURL: this.config.baseUrl || "https://maho.sh/api/v1", timeout: this.config.timeout || 3e4, maxRetries: this.config.maxRetries || 3 }); } async checkAvailability() { try { const models = await this.client.models.list(); return models.data.length > 0; } catch { return false; } } async fetchUsage() { return { tokensUsed: 0, requestsCount: 0, cost: 0 }; } async generate(prompt, options) { this.ensureInitialized(); const messages = []; if (options?.systemPrompt) { messages.push({ role: "system", content: options.systemPrompt }); } messages.push({ role: "user", content: prompt }); return this.generateWithMessages(messages, options); } async generateWithMessages(messages, options) { this.ensureInitialized(); try { const completion = await this.client.chat.completions.create({ model: options?.model || "gpt-3.5-turbo", messages: messages.map((m) => ({ role: m.role, content: m.content })), temperature: options?.temperature || 0.7, max_tokens: options?.maxTokens || 2048, top_p: options?.topP, frequency_penalty: options?.frequencyPenalty, presence_penalty: options?.presencePenalty, stop: options?.stop, stream: false }); const choice = completion.choices[0]; return { text: choice.message.content || "", model: completion.model, usage: completion.usage ? { promptTokens: completion.usage.prompt_tokens, completionTokens: completion.usage.completion_tokens, totalTokens: completion.usage.total_tokens } : void 0, finishReason: choice.finish_reason || void 0 }; } catch (error) { if (error.status === 429) { throw new ProviderError("Rate limit exceeded", this.id, { retryAfter: error.headers?.["retry-after"] }); } throw new ProviderError(error.message || "Failed to generate text", this.id, error); } } async *stream(prompt, options) { this.ensureInitialized(); const messages = []; if (options?.systemPrompt) { messages.push({ role: "system", content: options.systemPrompt }); } messages.push({ role: "user", content: prompt }); try { const stream = await this.client.chat.completions.create({ model: options?.model || "gpt-3.5-turbo", messages: messages.map((m) => ({ role: m.role, content: m.content })), temperature: options?.temperature || 0.7, max_tokens: options?.maxTokens || 2048, top_p: options?.topP, frequency_penalty: options?.frequencyPenalty, presence_penalty: options?.presencePenalty, stop: options?.stop, stream: true }); for await (const chunk of stream) { const content = chunk.choices[0]?.delta?.content; if (content) { yield content; } } } catch (error) { throw new ProviderError(error.message || "Failed to stream text", this.id, error); } } }; var AkashText = class extends BaseProvider { client; constructor() { super("akash-text", "Akash Text", "text"); } async onInitialize() { if (!this.config.apiKey) { throw new ProviderError("Akash API key is required", this.id); } this.client = new OpenAI({ apiKey: this.config.apiKey, baseURL: this.config.baseUrl || "https://chatapi.akash.network/api/v1", timeout: this.config.timeout || 3e4, maxRetries: this.config.maxRetries || 3 }); } async checkAvailability() { try { const models = await this.client.models.list(); return models.data.length > 0; } catch { return false; } } async fetchUsage() { return { tokensUsed: 0, requestsCount: 0, cost: 0 }; } async generate(prompt, options) { this.ensureInitialized(); const messages = []; if (options?.systemPrompt) { messages.push({ role: "system", content: options.systemPrompt }); } messages.push({ role: "user", content: prompt }); return this.generateWithMessages(messages, options); } async generateWithMessages(messages, options) { this.ensureInitialized(); try { const completion = await this.client.chat.completions.create({ model: options?.model || "llama-3.1-8b-instruct", messages: messages.map((m) => ({ role: m.role, content: m.content })), temperature: options?.temperature || 0.7, max_tokens: options?.maxTokens || 2048, top_p: options?.topP, frequency_penalty: options?.frequencyPenalty, presence_penalty: options?.presencePenalty, stop: options?.stop, stream: false }); const choice = completion.choices[0]; return { text: choice.message.content || "", model: completion.model, usage: completion.usage ? { promptTokens: completion.usage.prompt_tokens, completionTokens: completion.usage.completion_tokens, totalTokens: completion.usage.total_tokens } : void 0, finishReason: choice.finish_reason || void 0 }; } catch (error) { if (error.status === 429) { throw new ProviderError("Rate limit exceeded", this.id, { retryAfter: error.headers?.["retry-after"] }); } throw new ProviderError(error.message || "Failed to generate text", this.id, error); } } async *stream(prompt, options) { this.ensureInitialized(); const messages = []; if (options?.systemPrompt) { messages.push({ role: "system", content: options.systemPrompt }); } messages.push({ role: "user", content: prompt }); try { const stream = await this.client.chat.completions.create({ model: options?.model || "llama-3.1-8b-instruct", messages: messages.map((m) => ({ role: m.role, content: m.content })), temperature: options?.temperature || 0.7, max_tokens: options?.maxTokens || 2048, top_p: options?.topP, frequency_penalty: options?.frequencyPenalty, presence_penalty: options?.presencePenalty, stop: options?.stop, stream: true }); for await (const chunk of stream) { const content = chunk.choices[0]?.delta?.content; if (content) { yield content; } } } catch (error) { throw new ProviderError(error.message || "Failed to stream text", this.id, error); } } }; // src/core/provider-manager.ts var ProviderManager = class { constructor(config) { this.config = config; this.fallbackOrder = [config.primary, ...config.fallbacks]; } providers = /* @__PURE__ */ new Map(); fallbackOrder = []; async initialize() { for (const providerName of this.fallbackOrder) { const providerConfig = this.config.configs[providerName]; if (!providerConfig) continue; let provider; switch (providerName) { case "maho": provider = new MahoText(); break; case "alkahest": provider = new AlkahestText(); break; case "akash": provider = new AkashText(); break; case "openai": provider = new OpenAIText(); break; default: console.warn(`Unknown provider: ${providerName}`); continue; } try { await provider.initialize(providerConfig); this.providers.set(providerName, provider); console.log(`\u2705 Initialized ${providerName} provider`); } catch (error) { console.warn(`\u26A0\uFE0F Failed to initialize ${providerName}:`, error); } } } async generate(prompt, options) { let lastError = null; for (const providerName of this.fallbackOrder) { const provider = this.providers.get(providerName); if (!provider) continue; try { console.log(`\u{1F504} Trying ${providerName} provider...`); const result = await provider.generate(prompt, options); console.log(`\u2705 Success with ${providerName}`); return result; } catch (error) { console.warn(`\u274C ${providerName} failed:`, error.message); lastError = error; if (error instanceof ProviderError && error.details?.retryAfter) { await new Promise((resolve) => setTimeout(resolve, 1e3)); } } } throw new ProviderError( `All providers failed. Last error: ${lastError?.message}`, "provider-manager", lastError ); } async generateWithMessages(messages, options) { let lastError = null; for (const providerName of this.fallbackOrder) { const provider = this.providers.get(providerName); if (!provider) continue; try { console.log(`\u{1F504} Trying ${providerName} provider...`); const result = await provider.generateWithMessages(messages, options); console.log(`\u2705 Success with ${providerName}`); return result; } catch (error) { console.warn(`\u274C ${providerName} failed:`, error.message); lastError = error; if (error instanceof ProviderError && error.details?.retryAfter) { await new Promise((resolve) => setTimeout(resolve, 1e3)); } } } throw new ProviderError( `All providers failed. Last error: ${lastError?.message}`, "provider-manager", lastError ); } getAvailableProviders() { return Array.from(this.providers.keys()); } getProvider(name) { return this.providers.get(name); } }; var OpenAITTS = class _OpenAITTS extends BaseProvider { client; static VOICES = [ { id: "alloy", name: "Alloy", gender: "neutral" }, { id: "echo", name: "Echo", gender: "male" }, { id: "fable", name: "Fable", gender: "neutral" }, { id: "onyx", name: "Onyx", gender: "male" }, { id: "nova", name: "Nova", gender: "female" }, { id: "shimmer", name: "Shimmer", gender: "female" } ]; constructor() { super("openai-tts", "OpenAI TTS", "tts"); } async onInitialize() { if (!this.config.apiKey) { throw new ProviderError("OpenAI API key is required", this.id); } this.client = new OpenAI({ apiKey: this.config.apiKey, baseURL: this.config.baseUrl, timeout: this.config.timeout || 6e4, // Longer timeout for audio maxRetries: this.config.maxRetries || 3 }); } async checkAvailability() { try { const response = await this.client.audio.speech.create({ model: "tts-1", voice: "alloy", input: "test", response_format: "mp3" }); return response instanceof Response; } catch { return false; } } async fetchUsage() { return { tokensUsed: 0, requestsCount: 0, cost: 0 }; } async synthesize(text, options) { this.ensureInitialized(); try { const format = this.mapAudioFormat(options?.format || "mp3"); const response = await this.client.audio.speech.create({ model: options?.model || "tts-1", voice: options?.voice || "alloy", input: text, response_format: format, speed: options?.speed || 1 }); const arrayBuffer = await response.arrayBuffer(); return { audio: arrayBuffer, format: options?.format || "mp3", voice: options?.voice || "alloy" }; } catch (error) { if (error.status === 429) { throw new ProviderError("Rate limit exceeded", this.id, { retryAfter: error.headers?.["retry-after"] }); } throw new ProviderError(error.message || "Failed to synthesize speech", this.id, error); } } async getVoices() { this.ensureInitialized(); return _OpenAITTS.VOICES; } async stream(text, options) { this.ensureInitialized(); try { const format = this.mapAudioFormat(options?.format || "mp3"); const response = await this.client.audio.speech.create({ model: options?.model || "tts-1", voice: options?.voice || "alloy", input: text, response_format: format, speed: options?.speed || 1 }); if (!response.body) { throw new Error("No response body"); } return response.body; } catch (error) { throw new ProviderError(error.message || "Failed to stream speech", this.id, error); } } mapAudioFormat(format) { switch (format) { case "mp3": case "opus": case "aac": case "flac": return format; case "wav": case "ogg": return "mp3"; default: return "mp3"; } } }; // src/providers/tts/alkahest-tts.ts var AlkahestTTS = class extends BaseProvider { baseUrl = ""; apiKey; voices = []; constructor() { super("alkahest-tts", "Alkahest TTS", "tts"); } async onInitialize() { this.baseUrl = this.config.baseUrl || "http://localhost:9999"; this.apiKey = this.config.apiKey; if (!this.apiKey && !this.config.privyToken) { throw new ProviderError("Alkahest API key or Privy token is required", this.id); } await this.fetchVoices(); } async checkAvailability() { try { const response = await fetch(`${this.baseUrl}/api/v1/audio/voices`, { headers: this.getHeaders() }); return response.ok; } catch { return false; } } async fetchUsage() { try { const response = await fetch(`${this.baseUrl}/api/v1/mana/balance`, { headers: this.getHeaders() }); if (!response.ok) return { tokensUsed: 0, requestsCount: 0, cost: 0 }; const data = await response.json(); return { remaining: data.balance, tokensUsed: 0, requestsCount: 0, cost: 0 }; } catch { return { tokensUsed: 0, requestsCount: 0, cost: 0 }; } } getHeaders() { const headers = { "Content-Type": "application/json" }; if (this.apiKey) { headers["Authorization"] = `Bearer ${this.apiKey}`; } else if (this.config.privyToken) { headers["Authorization"] = `Bearer ${this.config.privyToken}`; } return headers; } async fetchVoices() { try { const response = await fetch(`${this.baseUrl}/api/v1/audio/voices`, { headers: this.getHeaders() }); if (response.ok) { const data = await response.json(); this.voices = data.voices?.map((v) => ({ id: v.id || v.voice_id, name: v.name, gender: v.gender, language: v.language || "en", previewUrl: v.preview_url, labels: v.labels || {} })) || []; } } catch (error) { console.warn("Failed to fetch voices:", error); this.voices = [ { id: "alloy", name: "Alloy", gender: "neutral" }, { id: "echo", name: "Echo", gender: "male" }, { id: "fable", name: "Fable", gender: "neutral" }, { id: "onyx", name: "Onyx", gender: "male" }, { id: "nova", name: "Nova", gender: "female" }, { id: "shimmer", name: "Shimmer", gender: "female" } ]; } } async synthesize(text, options) { this.ensureInitialized(); try { const format = this.mapAudioFormat(options?.format || "mp3"); const response = await fetch(`${this.baseUrl}/api/v1/audio/speech`, { method: "POST", headers: this.getHeaders(), body: JSON.stringify({ model: options?.model || "tts-1", input: text, voice: options?.voice || "alloy", response_format: format, speed: options?.speed || 1 }) }); if (!response.ok) { const error = await response.json().catch(() => ({ error: "Unknown error" })); if (response.status === 429) { throw new ProviderError("Rate limit exceeded", this.id, { retryAfter: response.headers.get("X-RateLimit-Reset") }); } if (response.status === 402) { throw new ProviderError("Insufficient mana balance", this.id, error); } throw new ProviderError( error.error?.message || error.error || "Failed to synthesize speech", this.id, error ); } const arrayBuffer = await response.arrayBuffer(); return { audio: arrayBuffer, format: options?.format || "mp3", voice: options?.voice || "alloy" }; } catch (error) { if (error instanceof ProviderError) { throw error; } throw new ProviderError(error.message || "Failed to synthesize speech", this.id, error); } } async getVoices() { this.ensureInitialized(); return this.voices; } async stream(text, options) { this.ensureInitialized(); try { const format = this.mapAudioFormat(options?.format || "mp3"); const response = await fetch(`${this.baseUrl}/api/v1/audio/speech`, { method: "POST", headers: this.getHeaders(), body: JSON.stringify({ model: options?.model || "tts-1", input: text, voice: options?.voice || "alloy", response_format: format, speed: options?.speed || 1, stream: true // Enable streaming if supported }) }); if (!response.ok) { const error = await response.json().catch(() => ({ error: "Unknown error" })); throw new ProviderError( error.error?.message || error.error || "Failed to stream speech", this.id, error ); } if (!response.body) { throw new Error("No response body"); } return response.body; } catch (error) { if (error instanceof ProviderError) { throw error; } throw new ProviderError(error.message || "Failed to stream speech", this.id, error); } } mapAudioFormat(format) { switch (format) { case "mp3": case "opus": case "aac": case "flac": return format; case "wav": return "mp3"; case "ogg": return "opus"; default: return "mp3"; } } }; // src/providers/image/alkahest.ts var AlkahestImage = class extends BaseProvider { baseUrl = ""; apiKey; constructor() { super("alkahest-image", "Alkahest Image", "image"); } async onInitialize() { this.baseUrl = this.config.baseUrl || "http://localhost:9999"; this.apiKey = this.config.apiKey; if (!this.apiKey && !this.config.privyToken) { throw new ProviderError("Alkahest API key or Privy token is required", this.id); } } async checkAvailability() { try { const response = await fetch(`${this.baseUrl}/api/v1/models`, { headers: this.getHeaders() }); if (!response.ok) return false; const data = await response.json(); return data.data?.some( (model) => model.id.includes("dall-e") || model.id.includes("stable-diffusion") || model.capabilities?.includes("image") ) || false; } catch { return false; } } async fetchUsage() { try { const response = await fetch(`${this.baseUrl}/api/v1/mana/balance`, { headers: this.getHeaders() }); if (!response.ok) return { tokensUsed: 0, requestsCount: 0, cost: 0 }; const data = await response.json(); return { remaining: data.balance, tokensUsed: 0, requestsCount: 0, cost: 0 }; } catch { return { tokensUsed: 0, requestsCount: 0, cost: 0 }; } } getHeaders() { const headers = { "Content-Type": "application/json" }; if (this.apiKey) { headers["Authorization"] = `Bearer ${this.apiKey}`; } else if (this.config.privyToken) { headers["Authorization"] = `Bearer ${this.config.privyToken}`; } return headers; } async generate(prompt, options) { this.ensureInitialized(); try { const response = await fetch(`${this.baseUrl}/api/v1/images/generations`, { method: "POST", headers: this.getHeaders(), body: JSON.stringify({ prompt, n: options?.n || 1, size: options?.size || "1024x1024", model: options?.model || "dall-e-3", quality: options?.quality || "standard", style: options?.style || "vivid" }) }); if (!response.ok) { const error = await response.json().catch(() => ({ error: "Unknown error" })); if (response.status === 429) { throw new ProviderError("Rate limit exceeded", this.id, { retryAfter: response.headers.get("X-RateLimit-Reset") }); } if (response.status === 402) { throw new ProviderError("Insufficient mana balance", this.id, error); } throw new ProviderError( error.error?.message || error.error || "Failed to generate image", this.id, error ); } const data = await response.json(); return { images: data.data.map((item) => ({ url: item.url, base64: item.b64_json, revisedPrompt: item.revised_prompt })), model: options?.model || "dall-e-3" }; } catch (error) { if (error instanceof ProviderError) { throw error; } throw new ProviderError(error.message || "Failed to generate image", this.id, error); } } }; // src/services/conversation.ts var ConversationService = class { constructor(textProvider, ttsProvider, options = {}) { this.textProvider = textProvider; this.ttsProvider = ttsProvider; this.options = options; } conversations = /* @__PURE__ */ new Map(); async processMessage(userId, message, personality, metadata) { const conversationId = `${userId}-${Date.now()}`; let context = this.conversations.get(conversationId); if (!context) { context = { conversationId, userId, messages: [], metadata: metadata || {} }; this.conversations.set(conversationId, context); } context.messages.push({ role: "user", content: message, timestamp: /* @__PURE__ */ new Date() }); const messages = this.prepareMessages(context, personality); const textOptions = { temperature: this.options.temperature || 0.7, maxTokens: 500 }; const textResponse = await this.textProvider.generateWithMessages(messages, textOptions); context.messages.push({ role: "assistant", content: textResponse.text, timestamp: /* @__PURE__ */ new Date() }); this.trimContext(context); let audio; if (this.options.enableTTS && this.ttsProvider) { const ttsOptions = { voice: metadata?.voice || "nova", speed: 1 }; try { const audioResponse = await this.ttsProvider.synthesize(textResponse.text, ttsOptions); audio = audioResponse.audio; } catch (error) { console.error("TTS generation failed:", error); } } const actions = this.options.enableActions ? this.extractActions(textResponse.text) : void 0; const emotion = this.detectEmotion(textResponse.text); return { text: textResponse.text, audio, emotion, actions, metadata: { conversationId, model: textResponse.model, usage: textResponse.usage } }; } prepareMessages(context, personality) { const messages = []; if (personality) { messages.push({ role: "system", content: this.buildPersonalityPrompt(personality) }); } const maxMessages = this.options.maxContextMessages || 10; const recentMessages = context.messages.slice(-maxMessages); messages.push(...recentMessages); return messages; } buildPersonalityPrompt(personality) { let prompt = `You are ${personality.name}. ${personality.description} `; prompt += `Your personality traits: ${personality.traits.join(", ")}. `; prompt += `Communication tone: ${personality.tone}. `; if (personality.guidelines.length > 0) { prompt += ` Guidelines: `; personality.guidelines.forEach((guideline) => { prompt += `- ${guideline} `; }); } if (personality.examples && personality.examples.length > 0) { prompt += ` Example interactions: `; personality.examples.forEach((example) => { prompt += `User: ${example.input} `; prompt += `You: ${example.output} `; }); } return prompt; } trimContext(context) { const maxMessages = (this.options.maxContextMessages || 10) * 2; if (context.messages.length > maxMessages) { const systemMessages = context.messages.filter((m) => m.role === "system"); const recentMessages = context.messages.slice(-maxMessages); context.messages = [...systemMessages, ...recentMessages]; } } extractActions(text) { const actions = []; if (text.includes("*waves*") || text.includes("*waving*")) { actions.push({ type: "animation", value: "wave", duration: 2e3 }); } if (text.includes("*dances*") || text.includes("*dancing*")) { actions.push({ type: "animation", value: "dance", duration: 5e3 }); } if (text.includes(":)") || text.includes("\u{1F60A}")) { actions.push({ type: "expression", value: "happy", duration: 3e3 }); } return actions.length > 0 ? actions : void 0; } detectEmotion(text) { const emotions = { happy: ["happy", "joy", "excited", "great", "wonderful", "\u{1F60A}", ":)"], sad: ["sad", "sorry", "disappointed", "unfortunate", "\u{1F622}", ":("], angry: ["angry", "mad", "frustrated", "annoyed", "\u{1F620}"], surprised: ["wow", "amazing", "surprised", "unexpected", "\u{1F62E}"], neutral: ["okay", "alright", "understand", "got it"] }; const lowerText = text.toLowerCase(); for (const [emotion, keywords] of Object.entries(emotions)) { if (keywords.some((keyword) => lowerText.includes(keyword))) { return emotion; } } return "neutral"; } clearConversation(conversationId) { this.conversations.delete(conversationId); } getConversation(conversationId) { return this.conversations.get(conversationId); } }; // src/services/tip-management.ts var TipManagementService = class { goals = /* @__PURE__ */ new Map(); transactions = /* @__PURE__ */ new Map(); milestones = []; tipperStats = /* @__PURE__ */ new Map(); totalEarned = { SOL: 0, NEAR: 0, ETH: 0, USD: 0 }; constructor() { this.initializeDefaultMilestones(); } initializeDefaultMilestones() { this.milestones = [ { amount: 1, currency: "SOL", reward: { type: "animation", value: "thank-you-wave", duration: 3e3 }, message: "Thank you for your first tip! \u{1F496}", achieved: false }, { amount: 5, currency: "SOL", reward: { type: "animation", value: "dance-happy", duration: 5e3 }, message: "Wow! You are amazing! Time to dance! \u{1F483}", achieved: false }, { amount: 10, currency: "SOL", reward: { type: "unlock", value: "special-animations", unlocks: ["dance-special", "pose-cute", "gesture-heart"], message: "You unlocked special animations! \u2728" }, message: "Special animations unlocked! You are incredible! \u{1F31F}", achieved: false }, { amount: 25, currency: "SOL", reward: { type: "content", value: "custom-image-generation" }, message: "Custom images unlocked! I can draw anything for you! \u{1F3A8}", achieved: false }, { amount: 50, currency: "SOL", reward: { type: "special", value: "vip-access", unlocks: ["private-chat", "custom-personality", "priority-responses"] }, message: "VIP access granted! You are truly special! \u{1F451}", achieved: false } ]; } createGoal(goal) { const id = `goal_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const newGoal = { ...goal, id, currentAmount: 0, createdAt: /* @__PURE__ */ new Date() }; this.goals.set(id, newGoal); return id; } processTip(userId, amount, currency, goalId, message) { const transactionId = `tx_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const transaction = { id: transactionId, userId, amount, currency, goalId, message, timestamp: /* @__PURE__ */ new Date(), status: "confirmed" // In real implementation, this would be 'pending' initially }; this.transactions.set(transactionId, transaction); if (currency in this.totalEarned) { this.totalEarned[currency] += amount; } this.updateTipperStats(userId, amount, currency); const actions = []; const achievedMilestones = []; let goalCompleted; if (goalId && this.goals.has(goalId)) { const goal = this.goals.get(goalId); goal.currentAmount += amount; if (goal.currentAmount >= goal.targetAmount && goal.isActive) { goal.isActive = false; goal.completedAt = /* @__PURE__ */ new Date(); goalCompleted = goal; actions.push(this.createRewardAction(goal.reward)); if (goal.reward.message) { actions.push({ type: "audio", action: "play", value: "goal-completion-sound", volume: 0.8 }); } } } const userStats = this.tipperStats.get(userId); if (userStats) { this.milestones.forEach((milestone) => { if (!milestone.achieved && userStats.totalTipped >= milestone.amount && userStats.favoriteCurrency === milestone.currency) { milestone.achieved = true; milestone.achievedAt = /* @__PURE__ */ new Date(); achievedMilestones.push(milestone); actions.push(this.createRewardAction(milestone.reward)); } }); } actions.push({ type: "vrm", action: "expression", value: "smile-big", duration: 3e3, intensity: 0.9 }); actions.push({ type: "vrm", action: "gesture", value: "heart-hands", duration: 2e3 }); if (amount >= 5) { actions.push({ type: "canvas", action: "effect", value: "hearts", duration: 5e3 }); } if (amount >= 10) { actions.push({ type: "vrm", action: "animation", value: "dance-celebration", duration: 8e3 }); } return { success: true, actions, milestones: achievedMilestones, goalCompleted }; } createRewardAction(reward) { switch (reward.type) { case "animation": return { type: "vrm", action: "animation", value: reward.value, duration: reward.duration || 5e3 }; case "content": return { type: "canvas", action: "draw", value: reward.value, position: { x: 0.7, y: 0.3 }, size: { width: 400, height: 400 } }; case "special": return { type: "audio", action: "play", value: "special-unlock-sound", volume: 0.7 }; case "unlock": return { type: "canvas", action: "overlay", value: "unlock-notification", duration: 5e3 }; default: return { type: "vrm", action: "animation", value: "celebration", duration: 3e3 }; } } updateTipperStats(userId, amount, currency) { let stats = this.tipperStats.get(userId); if (!