Spaces:
Running
Running
| // ===== KIMI MEMORY MANAGER ===== | |
| class KimiMemory { | |
| constructor(database) { | |
| this.db = database; | |
| this.preferences = { | |
| voiceRate: 1.1, | |
| voicePitch: 1.1, | |
| voiceVolume: 0.8, | |
| lastInteraction: null, | |
| totalInteractions: 0, | |
| favoriteWords: [], | |
| emotionalState: "neutral" | |
| }; | |
| this.isReady = false; | |
| // affectionTrait will be loaded from database during init() | |
| this.affectionTrait = 50; // Temporary default until loaded from DB | |
| } | |
| async init() { | |
| if (!this.db) { | |
| console.warn("Database not available, using local mode"); | |
| return; | |
| } | |
| try { | |
| this.selectedCharacter = await this.db.getSelectedCharacter(); | |
| // Start with lower favorability level - relationships must be built over time | |
| this.favorabilityLevel = await this.db.getPreference(`favorabilityLevel_${this.selectedCharacter}`, 50); | |
| // Load affection trait from personality database with coherent defaults | |
| const charDefAff = | |
| (window.KIMI_CHARACTERS && window.KIMI_CHARACTERS[this.selectedCharacter]?.traits?.affection) || null; | |
| const genericAff = (window.getTraitDefaults && window.getTraitDefaults().affection) || 65; | |
| const defaultAff = typeof charDefAff === "number" ? charDefAff : genericAff; | |
| this.affectionTrait = await this.db.getPersonalityTrait("affection", defaultAff, this.selectedCharacter); | |
| this.preferences = { | |
| voiceRate: await this.db.getPreference(`voiceRate_${this.selectedCharacter}`, 1.1), | |
| voicePitch: await this.db.getPreference(`voicePitch_${this.selectedCharacter}`, 1.1), | |
| voiceVolume: await this.db.getPreference(`voiceVolume_${this.selectedCharacter}`, 0.8), | |
| lastInteraction: await this.db.getPreference(`lastInteraction_${this.selectedCharacter}`, null), | |
| totalInteractions: await this.db.getPreference(`totalInteractions_${this.selectedCharacter}`, 0), | |
| favoriteWords: await this.db.getPreference(`favoriteWords_${this.selectedCharacter}`, []), | |
| emotionalState: await this.db.getPreference(`emotionalState_${this.selectedCharacter}`, "neutral") | |
| }; | |
| // affectionTrait already loaded above with coherent default | |
| this.isReady = true; | |
| this.updateFavorabilityBar(); | |
| } catch (error) { | |
| console.error("KimiMemory initialization error:", error); | |
| } | |
| } | |
| async saveConversation(userText, kimiResponse, tokenInfo = null) { | |
| if (!this.db) return; | |
| try { | |
| const character = await this.db.getSelectedCharacter(); | |
| await this.db.saveConversation(userText, kimiResponse, this.favorabilityLevel, new Date(), character); | |
| // Legacy interactions counter kept for backward compatibility (not shown in UI now) | |
| let total = await this.db.getPreference(`totalInteractions_${character}`, 0); | |
| total = Number(total) + 1; | |
| await this.db.setPreference(`totalInteractions_${character}`, total); | |
| this.preferences.totalInteractions = total; | |
| // Update tokens usage if provided (in/out) | |
| if (tokenInfo && typeof tokenInfo.tokensIn === "number" && typeof tokenInfo.tokensOut === "number") { | |
| const prevIn = Number(await this.db.getPreference(`totalTokensIn_${character}`, 0)) || 0; | |
| const prevOut = Number(await this.db.getPreference(`totalTokensOut_${character}`, 0)) || 0; | |
| await this.db.setPreference(`totalTokensIn_${character}`, prevIn + tokenInfo.tokensIn); | |
| await this.db.setPreference(`totalTokensOut_${character}`, prevOut + tokenInfo.tokensOut); | |
| } | |
| let first = await this.db.getPreference(`firstInteraction_${character}`, null); | |
| if (!first) { | |
| first = new Date().toISOString(); | |
| await this.db.setPreference(`firstInteraction_${character}`, first); | |
| } | |
| this.preferences.lastInteraction = new Date().toISOString(); | |
| await this.db.setPreference(`lastInteraction_${character}`, this.preferences.lastInteraction); | |
| } catch (error) { | |
| console.error("Error saving conversation:", error); | |
| } | |
| } | |
| async updateFavorability(change) { | |
| try { | |
| this.affectionTrait = Math.max(0, Math.min(100, this.affectionTrait + change)); | |
| if (this.db) { | |
| await this.db.setPersonalityTrait("affection", this.affectionTrait, this.selectedCharacter); | |
| } | |
| this.updateFavorabilityBar(); | |
| } catch (error) { | |
| console.error("Error updating favorability:", error); | |
| } | |
| } | |
| async updateAffectionTrait() { | |
| if (!this.db) return; | |
| try { | |
| this.selectedCharacter = await this.db.getSelectedCharacter(); | |
| // Use unified default that matches KimiEmotionSystem | |
| this.affectionTrait = await this.db.getPersonalityTrait("affection", 50, this.selectedCharacter); | |
| this.updateFavorabilityBar(); | |
| } catch (error) { | |
| console.error("Error updating affection trait:", error); | |
| } | |
| } | |
| updateFavorabilityBar() { | |
| try { | |
| const favorabilityBar = document.getElementById("favorability-bar"); | |
| const favorabilityText = document.getElementById("favorability-text"); | |
| const value = Number(this.affectionTrait) || 0; | |
| const clamped = Math.max(0, Math.min(100, value)); | |
| if (favorabilityBar) { | |
| favorabilityBar.style.width = `${clamped}%`; | |
| } | |
| if (favorabilityText) { | |
| favorabilityText.textContent = `${clamped.toFixed(2)}%`; | |
| } | |
| } catch (error) { | |
| console.error("Error updating favorability bar:", error); | |
| } | |
| } | |
| getGreeting() { | |
| const i18n = window.kimiI18nManager; | |
| if (this.affectionTrait <= 10) { | |
| return i18n?.t("greeting_low") || "Hello."; | |
| } | |
| if (this.affectionTrait < 40) { | |
| return i18n?.t("greeting_mid") || "Hi. How can I help you?"; | |
| } | |
| return i18n?.t("greeting_high") || "Hello my love! π"; | |
| } | |
| } | |
| // Export to global scope | |
| window.KimiMemory = KimiMemory; | |
| export default KimiMemory; | |