Spaces:
Running
Running
Upload 32 files
Browse files- CHANGELOG.md +0 -10
- CONTRIBUTING.md +0 -7
- index.html +12 -11
- kimi-js/kimi-config.js +1 -1
- kimi-js/kimi-constants.js +118 -251
- kimi-js/kimi-database.js +9 -2
- kimi-js/kimi-llm-manager.js +97 -8
- kimi-js/kimi-memory-system.js +1 -2
- kimi-js/kimi-module.js +7 -12
- kimi-js/kimi-script.js +11 -99
- kimi-js/kimi-utils.js +40 -8
- kimi-locale/de.json +1 -1
- kimi-locale/en.json +1 -1
- kimi-locale/es.json +1 -1
- kimi-locale/fr.json +1 -1
- kimi-locale/it.json +1 -1
- kimi-locale/ja.json +1 -1
- kimi-locale/zh.json +1 -1
CHANGELOG.md
CHANGED
@@ -1,15 +1,5 @@
|
|
1 |
# Virtual Kimi Changelog
|
2 |
|
3 |
-
# [1.0.8] - 2025-08-19
|
4 |
-
|
5 |
-
### Changed
|
6 |
-
|
7 |
-
- Improved fallback logic for LLM responses: now uses localized emotional responses if the LLM reply is empty or invalid.
|
8 |
-
- Made emotional response selection dynamic and robust, based on available variants.
|
9 |
-
- Enhanced error handling for missing API keys, network issues, and API errors, ensuring the user always receives a meaningful message.
|
10 |
-
- Refactored code patching to avoid accidental code removal or misplaced edits.
|
11 |
-
- Clarified and documented emotional response logic for maintainability.
|
12 |
-
|
13 |
## [1.0.7] - 2025-08-19
|
14 |
|
15 |
### Changed
|
|
|
1 |
# Virtual Kimi Changelog
|
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
## [1.0.7] - 2025-08-19
|
4 |
|
5 |
### Changed
|
CONTRIBUTING.md
CHANGED
@@ -331,13 +331,6 @@ Include the following information:
|
|
331 |
|
332 |
## Development Tips
|
333 |
|
334 |
-
### Debugging
|
335 |
-
|
336 |
-
- Use browser DevTools for JavaScript debugging
|
337 |
-
- Check console for errors and warnings
|
338 |
-
- Use the health check system: `window.KimiHealthCheck`
|
339 |
-
- Enable debug mode: `window.KIMI_DEBUG = true`
|
340 |
-
|
341 |
### Performance Optimization
|
342 |
|
343 |
- Minimize DOM manipulations
|
|
|
331 |
|
332 |
## Development Tips
|
333 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
334 |
### Performance Optimization
|
335 |
|
336 |
- Minimize DOM manipulations
|
index.html
CHANGED
@@ -55,7 +55,7 @@
|
|
55 |
},
|
56 |
"dateCreated": "2025-07-16",
|
57 |
"dateModified": "2025-08-19",
|
58 |
-
"version": "v1.0.
|
59 |
}
|
60 |
</script>
|
61 |
|
@@ -397,7 +397,8 @@
|
|
397 |
|
398 |
<div class="config-row">
|
399 |
<div class="config-label-group">
|
400 |
-
<label class="config-label" id="api-key-label" data-i18n="
|
|
|
401 |
<span id="api-key-info" class="help-icon" data-i18n-title="api_key_help_title"
|
402 |
title="Saved = your API key is stored for this provider. Use Test API Key to verify the connection.">
|
403 |
<i class="fas fa-info-circle"></i>
|
@@ -407,11 +408,11 @@
|
|
407 |
title="Green = API key saved for current provider. Grey = no key saved."></span>
|
408 |
</div>
|
409 |
<div class="config-control">
|
410 |
-
<input type="password" class="kimi-input" id="
|
411 |
-
|
412 |
-
autocorrect="off" spellcheck="false" inputmode="text"
|
413 |
-
|
414 |
-
data-form-type="other" />
|
415 |
<button class="kimi-button" id="toggle-api-key" type="button" aria-pressed="false"
|
416 |
aria-label="Show API key">
|
417 |
<i class="fas fa-eye"></i>
|
@@ -455,7 +456,7 @@
|
|
455 |
<label class="config-label" data-i18n="max_tokens">Max Tokens</label>
|
456 |
<div class="config-control">
|
457 |
<div class="slider-container">
|
458 |
-
<input type="range" class="kimi-slider" id="llm-max-tokens" min="10" max="
|
459 |
step="10" value="400" />
|
460 |
<span class="slider-value" id="llm-max-tokens-value">400</span>
|
461 |
</div>
|
@@ -1013,8 +1014,7 @@
|
|
1013 |
</div>
|
1014 |
</div>
|
1015 |
|
1016 |
-
<script src="dexie.min.js"></script>
|
1017 |
-
<script src="kimi-locale/i18n.js" defer></script>
|
1018 |
<script type="module" src="kimi-js/kimi-main.js"></script>
|
1019 |
<script type="module" src="kimi-js/kimi-config.js"></script>
|
1020 |
<script type="module" src="kimi-js/kimi-error-manager.js"></script>
|
@@ -1023,6 +1023,7 @@
|
|
1023 |
<script type="module" src="kimi-js/kimi-constants.js"></script>
|
1024 |
<script type="module" src="kimi-js/kimi-memory-ui.js"></script>
|
1025 |
<script type="module" src="kimi-js/kimi-appearance.js"></script>
|
|
|
1026 |
<script type="module" src="kimi-js/kimi-module.js"></script>
|
1027 |
<script type="module" src="kimi-js/kimi-script.js"></script>
|
1028 |
<script type="module" src="kimi-js/kimi-plugin-manager.js"></script>
|
@@ -1054,7 +1055,7 @@
|
|
1054 |
"name": "Jean & Kimi"
|
1055 |
},
|
1056 |
"dateCreated": "2025-07-19",
|
1057 |
-
"version": "v1.0.
|
1058 |
}
|
1059 |
}
|
1060 |
</script>
|
|
|
55 |
},
|
56 |
"dateCreated": "2025-07-16",
|
57 |
"dateModified": "2025-08-19",
|
58 |
+
"version": "v1.0.7"
|
59 |
}
|
60 |
</script>
|
61 |
|
|
|
397 |
|
398 |
<div class="config-row">
|
399 |
<div class="config-label-group">
|
400 |
+
<label class="config-label" id="api-key-label" data-i18n="openrouter_api_key">OpenRouter
|
401 |
+
API Key</label>
|
402 |
<span id="api-key-info" class="help-icon" data-i18n-title="api_key_help_title"
|
403 |
title="Saved = your API key is stored for this provider. Use Test API Key to verify the connection.">
|
404 |
<i class="fas fa-info-circle"></i>
|
|
|
408 |
title="Green = API key saved for current provider. Grey = no key saved."></span>
|
409 |
</div>
|
410 |
<div class="config-control">
|
411 |
+
<input type="password" class="kimi-input" id="openrouter-api-key"
|
412 |
+
name="openrouter_api_key" placeholder="sk-or-v1-..." autocomplete="new-password"
|
413 |
+
autocapitalize="none" autocorrect="off" spellcheck="false" inputmode="text"
|
414 |
+
aria-autocomplete="none" data-lpignore="true" data-1p-ignore="true"
|
415 |
+
data-bwignore="true" data-form-type="other" />
|
416 |
<button class="kimi-button" id="toggle-api-key" type="button" aria-pressed="false"
|
417 |
aria-label="Show API key">
|
418 |
<i class="fas fa-eye"></i>
|
|
|
456 |
<label class="config-label" data-i18n="max_tokens">Max Tokens</label>
|
457 |
<div class="config-control">
|
458 |
<div class="slider-container">
|
459 |
+
<input type="range" class="kimi-slider" id="llm-max-tokens" min="10" max="8192"
|
460 |
step="10" value="400" />
|
461 |
<span class="slider-value" id="llm-max-tokens-value">400</span>
|
462 |
</div>
|
|
|
1014 |
</div>
|
1015 |
</div>
|
1016 |
|
1017 |
+
<script src="kimi-js/dexie.min.js"></script>
|
|
|
1018 |
<script type="module" src="kimi-js/kimi-main.js"></script>
|
1019 |
<script type="module" src="kimi-js/kimi-config.js"></script>
|
1020 |
<script type="module" src="kimi-js/kimi-error-manager.js"></script>
|
|
|
1023 |
<script type="module" src="kimi-js/kimi-constants.js"></script>
|
1024 |
<script type="module" src="kimi-js/kimi-memory-ui.js"></script>
|
1025 |
<script type="module" src="kimi-js/kimi-appearance.js"></script>
|
1026 |
+
<script src="kimi-locale/i18n.js"></script>
|
1027 |
<script type="module" src="kimi-js/kimi-module.js"></script>
|
1028 |
<script type="module" src="kimi-js/kimi-script.js"></script>
|
1029 |
<script type="module" src="kimi-js/kimi-plugin-manager.js"></script>
|
|
|
1055 |
"name": "Jean & Kimi"
|
1056 |
},
|
1057 |
"dateCreated": "2025-07-19",
|
1058 |
+
"version": "v1.0.7"
|
1059 |
}
|
1060 |
}
|
1061 |
</script>
|
kimi-js/kimi-config.js
CHANGED
@@ -25,7 +25,7 @@ window.KIMI_CONFIG = {
|
|
25 |
VOICE_VOLUME: { min: 0.0, max: 1.0 },
|
26 |
INTERFACE_OPACITY: { min: 0.1, max: 1.0 },
|
27 |
LLM_TEMPERATURE: { min: 0.0, max: 1.0 },
|
28 |
-
LLM_MAX_TOKENS: { min: 10, max:
|
29 |
LLM_TOP_P: { min: 0.0, max: 1.0 },
|
30 |
LLM_FREQUENCY_PENALTY: { min: 0.0, max: 2.0 },
|
31 |
LLM_PRESENCE_PENALTY: { min: 0.0, max: 2.0 }
|
|
|
25 |
VOICE_VOLUME: { min: 0.0, max: 1.0 },
|
26 |
INTERFACE_OPACITY: { min: 0.1, max: 1.0 },
|
27 |
LLM_TEMPERATURE: { min: 0.0, max: 1.0 },
|
28 |
+
LLM_MAX_TOKENS: { min: 10, max: 8192 },
|
29 |
LLM_TOP_P: { min: 0.0, max: 1.0 },
|
30 |
LLM_FREQUENCY_PENALTY: { min: 0.0, max: 2.0 },
|
31 |
LLM_PRESENCE_PENALTY: { min: 0.0, max: 2.0 }
|
kimi-js/kimi-constants.js
CHANGED
@@ -2,212 +2,88 @@
|
|
2 |
|
3 |
window.KIMI_CONTEXT_KEYWORDS = {
|
4 |
en: {
|
5 |
-
surprise: [
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
"gosh",
|
16 |
-
"astonishing"
|
17 |
-
],
|
18 |
-
laughing: [
|
19 |
-
"haha",
|
20 |
-
"lol",
|
21 |
-
"laugh",
|
22 |
-
"funny",
|
23 |
-
"hilarious",
|
24 |
-
"rofl",
|
25 |
-
"lmao",
|
26 |
-
"giggle",
|
27 |
-
"chuckle",
|
28 |
-
"snicker",
|
29 |
-
"you’re kidding"
|
30 |
-
],
|
31 |
-
shy: [
|
32 |
-
"shy",
|
33 |
-
"embarrassed",
|
34 |
-
"blush",
|
35 |
-
"bashful",
|
36 |
-
"intimidated",
|
37 |
-
"awkward",
|
38 |
-
"nervous",
|
39 |
-
"timid",
|
40 |
-
"reserved",
|
41 |
-
"self-conscious"
|
42 |
-
],
|
43 |
-
confident: [
|
44 |
-
"confidence",
|
45 |
-
"proud",
|
46 |
-
"confident",
|
47 |
-
"strong",
|
48 |
-
"determined",
|
49 |
-
"assertive",
|
50 |
-
"bold",
|
51 |
-
"fearless",
|
52 |
-
"self-assured",
|
53 |
-
"leader"
|
54 |
-
],
|
55 |
-
romantic: [
|
56 |
-
"love",
|
57 |
-
"romantic",
|
58 |
-
"tender",
|
59 |
-
"hug",
|
60 |
-
"kiss",
|
61 |
-
"sweetheart",
|
62 |
-
"darling",
|
63 |
-
"my love",
|
64 |
-
"beloved",
|
65 |
-
"heart",
|
66 |
-
"passionate"
|
67 |
-
],
|
68 |
-
flirtatious: [
|
69 |
-
"flirty",
|
70 |
-
"teasing",
|
71 |
-
"seduce",
|
72 |
-
"charm",
|
73 |
-
"flirt",
|
74 |
-
"wink",
|
75 |
-
"sassy",
|
76 |
-
"saucy",
|
77 |
-
"playful",
|
78 |
-
"seductive",
|
79 |
-
"come hither"
|
80 |
-
],
|
81 |
-
goodbye: [
|
82 |
-
"goodbye",
|
83 |
-
"bye",
|
84 |
-
"see you",
|
85 |
-
"see you soon",
|
86 |
-
"ciao",
|
87 |
-
"take care",
|
88 |
-
"farewell",
|
89 |
-
"see ya",
|
90 |
-
"later",
|
91 |
-
"catch you later"
|
92 |
-
],
|
93 |
-
kiss: ["kiss", "kisses", "embrace", "smooch", "peck", "lip lock", "kissy", "mwah"],
|
94 |
-
dancing: ["dance", "dancing", "move", "groove", "step", "boogie", "twirl", "spin", "shake", "jig"],
|
95 |
-
listening: [
|
96 |
-
"listen",
|
97 |
-
"listening",
|
98 |
-
"hear",
|
99 |
-
"question",
|
100 |
-
"ask",
|
101 |
-
"tell me",
|
102 |
-
"pay attention",
|
103 |
-
"hear me out",
|
104 |
-
"focus",
|
105 |
-
"tune in",
|
106 |
-
"lend an ear"
|
107 |
-
]
|
108 |
},
|
109 |
fr: {
|
110 |
-
surprise: ["oh", "surprise", "incroyable", "wahou", "étonnant"
|
111 |
-
laughing: ["haha", "mdr", "rire", "drôle", "hilarant"
|
112 |
-
shy: ["timide", "gêné", "rougir", "honteux", "intimidé"
|
113 |
-
confident: ["confiance", "fier", "sûr", "fort", "déterminé"
|
114 |
-
romantic: ["amour", "romantique", "tendre", "câlin", "bisou", "
|
115 |
-
flirtatious: [
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
"aguiche",
|
121 |
-
"clin d’œil",
|
122 |
-
"coquin",
|
123 |
-
"séducteur",
|
124 |
-
"taquine",
|
125 |
-
"aguicheur"
|
126 |
-
],
|
127 |
-
goodbye: ["au revoir", "bye", "à bientôt", "ciao", "salut", "prends soin de toi", "à plus", "à la prochaine", "bye bye"],
|
128 |
-
kiss: ["bisou", "baiser", "embrasser", "smack", "bisou bisou", "bécot", "embrassade"],
|
129 |
-
dancing: ["danse", "bouge", "remue", "tourne", "spin", "danser", "tourbillon", "bouger", "remuer", "gigoter"],
|
130 |
-
listening: [
|
131 |
-
"écoute",
|
132 |
-
"écouter",
|
133 |
-
"parle",
|
134 |
-
"question",
|
135 |
-
"demande",
|
136 |
-
"dis-moi",
|
137 |
-
"écoute-moi",
|
138 |
-
"sois attentif",
|
139 |
-
"prête l’oreille",
|
140 |
-
"concentre-toi"
|
141 |
-
]
|
142 |
},
|
143 |
es: {
|
144 |
-
surprise: ["wow", "oh", "sorpresa", "increíble", "asombroso"
|
145 |
-
laughing: ["jaja", "lol", "reír", "gracioso", "divertido"
|
146 |
-
shy: ["tímido", "avergonzado", "sonrojar", "tímida", "intimidado"
|
147 |
-
confident: ["confianza", "orgulloso", "seguro", "fuerte", "determinado"
|
148 |
-
romantic: ["amor", "romántico", "tierno", "abrazo", "beso", "
|
149 |
-
flirtatious: ["coqueto", "provocar", "seducir", "encanto", "flirtear"
|
150 |
-
goodbye: ["adiós", "bye", "hasta pronto", "ciao", "hasta luego"
|
151 |
-
kiss: ["beso", "besos", "abrazar"
|
152 |
-
dancing: ["bailar", "baile", "mover", "ritmo", "paso"
|
153 |
-
listening: [
|
154 |
-
"escucha",
|
155 |
-
"escuchar",
|
156 |
-
"oír",
|
157 |
-
"habla",
|
158 |
-
"pregunta",
|
159 |
-
"preguntar",
|
160 |
-
"dime",
|
161 |
-
"escúchame",
|
162 |
-
"pon atención",
|
163 |
-
"presta oído",
|
164 |
-
"concéntrate"
|
165 |
-
]
|
166 |
},
|
167 |
de: {
|
168 |
-
surprise: ["wow", "oh", "überraschung", "unglaublich", "erstaunlich"
|
169 |
-
laughing: ["haha", "lol", "lachen", "lustig", "witzig"
|
170 |
-
shy: ["schüchtern", "verlegen", "erröten", "beschämt", "eingeschüchtert"
|
171 |
-
confident: ["vertrauen", "stolz", "sicher", "stark", "entschlossen"
|
172 |
-
romantic: ["liebe", "romantisch", "zärtlich", "umarmung", "kuss", "
|
173 |
-
flirtatious: ["flirten", "necken", "verführen", "charme", "flirt"
|
174 |
-
goodbye: ["auf wiedersehen", "bye", "bis bald", "ciao", "bis später"
|
175 |
-
kiss: ["kuss", "küsse", "umarmen", "
|
176 |
-
dancing: ["tanzen", "tanz", "bewegen", "groove", "schritt"
|
177 |
-
listening: [
|
178 |
-
"hör",
|
179 |
-
"hören",
|
180 |
-
"zuhören",
|
181 |
-
"sprich",
|
182 |
-
"frage",
|
183 |
-
"fragen",
|
184 |
-
"sag mir",
|
185 |
-
"hör zu",
|
186 |
-
"sei aufmerksam",
|
187 |
-
"konzentriere dich"
|
188 |
-
]
|
189 |
},
|
190 |
it: {
|
191 |
-
surprise: ["wow", "oh", "sorpresa", "incredibile", "stupefacente"
|
192 |
-
laughing: ["haha", "lol", "ridere", "divertente", "esilarante"
|
193 |
-
shy: [
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
],
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
211 |
}
|
212 |
};
|
213 |
|
@@ -709,28 +585,16 @@ window.KIMI_PERSONALITY_KEYWORDS = {
|
|
709 |
}
|
710 |
},
|
711 |
ja: {
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
romantic: ["愛", "ロマンチック", "優しい", "抱擁", "キス", "愛しい"],
|
717 |
-
flirtatious: ["いちゃつく", "からかう", "誘惑", "魅力", "フリート"],
|
718 |
-
goodbye: ["さようなら", "バイバイ", "また今度", "チャオ", "またね"],
|
719 |
-
kiss: ["キス", "抱擁", "チュー"],
|
720 |
-
dancing: ["踊る", "ダンス", "動く", "グルーブ", "ステップ"],
|
721 |
-
listening: ["聞いて", "聞く", "聞いてください", "話して", "話す", "質問", "尋ねる", "教えて"]
|
722 |
},
|
723 |
zh: {
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
romantic: ["爱", "浪漫", "温柔", "拥抱", "吻", "亲爱的"],
|
729 |
-
flirtatious: ["调情", "挑逗", "诱惑", "魅力", "撒娇"],
|
730 |
-
goodbye: ["再见", "拜拜", "回头见", "拜", "下次见"],
|
731 |
-
kiss: ["吻", "亲吻", "拥抱", "亲"],
|
732 |
-
dancing: ["跳舞", "舞蹈", "移动", "律动", "步伐"],
|
733 |
-
listening: ["听", "听听", "倾听", "说", "说话", "问题", "提问", "告诉我"]
|
734 |
}
|
735 |
};
|
736 |
|
@@ -850,16 +714,10 @@ window.KIMI_COMMON_WORDS = {
|
|
850 |
zh: ["的", "一", "是", "在", "不", "了", "有", "和", "人", "这", "中", "大", "为", "上", "个", "国", "我", "以", "要"]
|
851 |
};
|
852 |
|
853 |
-
// Build Set version for fast lookup (must be outside the object)
|
854 |
-
window.KIMI_COMMON_WORDS_SET = {};
|
855 |
-
Object.keys(window.KIMI_COMMON_WORDS).forEach(lang => {
|
856 |
-
window.KIMI_COMMON_WORDS_SET[lang] = new Set(window.KIMI_COMMON_WORDS[lang]);
|
857 |
-
});
|
858 |
-
|
859 |
// Helper function to check if a word is common
|
860 |
window.isCommonWord = function (word, language = "en") {
|
861 |
-
const
|
862 |
-
return
|
863 |
};
|
864 |
|
865 |
// Emotion detection sensitivity configuration (per language and emotion)
|
@@ -882,42 +740,42 @@ window.KIMI_EMOTION_SENSITIVITY = {
|
|
882 |
},
|
883 |
// Example language-specific overrides (can be adjusted via settings if needed)
|
884 |
fr: { romantic: 1.1, laughing: 0.95 },
|
885 |
-
es: { romantic: 1.05
|
886 |
-
|
887 |
-
de: {
|
888 |
-
|
889 |
-
ja: {
|
890 |
-
zh: {
|
891 |
};
|
892 |
|
893 |
// Personality trait adjustment multipliers
|
894 |
// Allows fine-tuning how fast traits evolve globally and per emotion/trait.
|
895 |
window.KIMI_TRAIT_ADJUSTMENT = {
|
896 |
-
globalGain: 1.
|
897 |
-
globalLoss: 0
|
898 |
// Per-emotion gain scaling (keys must match KimiEmotionSystem.EMOTIONS values)
|
899 |
emotionGain: {
|
900 |
-
positive: 1.
|
901 |
-
negative: 0
|
902 |
-
romantic: 1.
|
903 |
-
laughing: 1.
|
904 |
-
dancing: 1.
|
905 |
-
shy: 0
|
906 |
-
confident: 1.
|
907 |
-
flirtatious: 1.
|
908 |
},
|
909 |
// Per-trait scaling
|
910 |
traitGain: {
|
911 |
-
affection: 1.
|
912 |
-
romance: 1.
|
913 |
-
empathy: 1.
|
914 |
-
playfulness: 1.
|
915 |
-
humor: 1.
|
916 |
-
intelligence: 1.
|
917 |
},
|
918 |
traitLoss: {
|
919 |
-
affection: 0
|
920 |
-
romance: 0
|
921 |
empathy: 1.0,
|
922 |
playfulness: 1.0,
|
923 |
humor: 1.0,
|
@@ -1112,7 +970,16 @@ window.getLocalizedEmotionalResponse = function (type, index = null) {
|
|
1112 |
: "";
|
1113 |
}
|
1114 |
|
1115 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1116 |
const randomIndex = index !== null ? index : Math.floor(Math.random() * count) + 1;
|
1117 |
|
1118 |
return (
|
|
|
2 |
|
3 |
window.KIMI_CONTEXT_KEYWORDS = {
|
4 |
en: {
|
5 |
+
surprise: ["wow", "oh", "surprise", "incredible", "amazing"],
|
6 |
+
laughing: ["haha", "lol", "laugh", "funny", "hilarious"],
|
7 |
+
shy: ["shy", "embarrassed", "blush", "bashful", "intimidated"],
|
8 |
+
confident: ["confidence", "proud", "confident", "strong", "determined"],
|
9 |
+
romantic: ["love", "romantic", "tender", "hug", "kiss", "dear"],
|
10 |
+
flirtatious: ["flirty", "teasing", "seduce", "charm", "flirt"],
|
11 |
+
goodbye: ["goodbye", "bye", "see you", "see you soon", "ciao"],
|
12 |
+
kiss: ["kiss", "kisses", "embrace", "bise"],
|
13 |
+
dancing: ["dance", "dancing", "move", "groove", "step"],
|
14 |
+
listening: ["listen", "listening", "hear", "talk", "speak", "question", "ask", "tell me"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
},
|
16 |
fr: {
|
17 |
+
surprise: ["oh", "surprise", "incroyable", "wahou", "étonnant"],
|
18 |
+
laughing: ["haha", "mdr", "rire", "drôle", "hilarant"],
|
19 |
+
shy: ["timide", "gêné", "rougir", "honteux", "intimidé"],
|
20 |
+
confident: ["confiance", "fier", "sûr", "fort", "déterminé"],
|
21 |
+
romantic: ["amour", "romantique", "tendre", "câlin", "bisou", "cher"],
|
22 |
+
flirtatious: ["flirt", "taquin", "séduire", "charme", "aguiche"],
|
23 |
+
goodbye: ["au revoir", "bye", "à bientôt", "ciao", "salut"],
|
24 |
+
kiss: ["bisou", "baiser", "embrasser", "câlin"],
|
25 |
+
dancing: ["danse", "bouge", "remue", "tourne", "spin"],
|
26 |
+
listening: ["écoute", "ecoute", "écouter", "parle", "parler", "question", "demande", "dis-moi"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
},
|
28 |
es: {
|
29 |
+
surprise: ["wow", "oh", "sorpresa", "increíble", "asombroso"],
|
30 |
+
laughing: ["jaja", "lol", "reír", "gracioso", "divertido"],
|
31 |
+
shy: ["tímido", "avergonzado", "sonrojar", "tímida", "intimidado"],
|
32 |
+
confident: ["confianza", "orgulloso", "seguro", "fuerte", "determinado"],
|
33 |
+
romantic: ["amor", "romántico", "tierno", "abrazo", "beso", "querido"],
|
34 |
+
flirtatious: ["coqueto", "provocar", "seducir", "encanto", "flirtear"],
|
35 |
+
goodbye: ["adiós", "bye", "hasta pronto", "ciao", "hasta luego"],
|
36 |
+
kiss: ["beso", "besos", "abrazar"],
|
37 |
+
dancing: ["bailar", "baile", "mover", "ritmo", "paso"],
|
38 |
+
listening: ["escucha", "escuchar", "oír", "habla", "hablar", "pregunta", "preguntar", "dime"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
},
|
40 |
de: {
|
41 |
+
surprise: ["wow", "oh", "überraschung", "unglaublich", "erstaunlich"],
|
42 |
+
laughing: ["haha", "lol", "lachen", "lustig", "witzig"],
|
43 |
+
shy: ["schüchtern", "verlegen", "erröten", "beschämt", "eingeschüchtert"],
|
44 |
+
confident: ["vertrauen", "stolz", "sicher", "stark", "entschlossen"],
|
45 |
+
romantic: ["liebe", "romantisch", "zärtlich", "umarmung", "kuss", "lieber"],
|
46 |
+
flirtatious: ["flirten", "necken", "verführen", "charme", "flirt"],
|
47 |
+
goodbye: ["auf wiedersehen", "bye", "bis bald", "ciao", "bis später"],
|
48 |
+
kiss: ["kuss", "küsse", "umarmen", "küsschen"],
|
49 |
+
dancing: ["tanzen", "tanz", "bewegen", "groove", "schritt"],
|
50 |
+
listening: ["hör", "hören", "zuhören", "sprich", "sprechen", "frage", "fragen", "sag mir"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
},
|
52 |
it: {
|
53 |
+
surprise: ["wow", "oh", "sorpresa", "incredibile", "stupefacente"],
|
54 |
+
laughing: ["haha", "lol", "ridere", "divertente", "esilarante"],
|
55 |
+
shy: ["timido", "imbarazzato", "arrossire", "vergognoso", "intimidito"],
|
56 |
+
confident: ["fiducia", "orgoglioso", "sicuro", "forte", "determinato"],
|
57 |
+
romantic: ["amore", "romantico", "tenero", "abbraccio", "bacio", "caro"],
|
58 |
+
flirtatious: ["civettare", "provocare", "sedurre", "fascino", "flirtare"],
|
59 |
+
goodbye: ["arrivederci", "bye", "a presto", "ciao"],
|
60 |
+
kiss: ["bacio", "baci", "abbracciare", "bacetto"],
|
61 |
+
dancing: ["ballare", "ballo", "muovere", "ritmo", "passo"],
|
62 |
+
listening: ["ascolta", "ascoltare", "senti", "parla", "parlare", "domanda", "chiedere", "dimmi"]
|
63 |
+
},
|
64 |
+
ja: {
|
65 |
+
surprise: ["わお", "おお", "驚き", "信じられない", "すごい"],
|
66 |
+
laughing: ["はは", "笑", "笑う", "面白い", "愉快"],
|
67 |
+
shy: ["恥ずかしい", "照れる", "赤面", "内気", "遠慮"],
|
68 |
+
confident: ["自信", "誇り", "確信", "強い", "決意"],
|
69 |
+
romantic: ["愛", "ロマンチック", "優しい", "抱擁", "キス", "愛しい"],
|
70 |
+
flirtatious: ["いちゃつく", "からかう", "誘惑", "魅力", "フリート"],
|
71 |
+
goodbye: ["さようなら", "バイバイ", "また今度", "チャオ", "またね"],
|
72 |
+
kiss: ["キス", "抱擁", "チュー"],
|
73 |
+
dancing: ["踊る", "ダンス", "動く", "グルーブ", "ステップ"],
|
74 |
+
listening: ["聞いて", "聞く", "聞いてください", "話して", "話す", "質問", "尋ねる", "教えて"]
|
75 |
+
},
|
76 |
+
zh: {
|
77 |
+
surprise: ["哇", "哦", "惊喜", "难以置信", "惊人"],
|
78 |
+
laughing: ["哈哈", "笑", "大笑", "有趣", "搞笑"],
|
79 |
+
shy: ["害羞", "尴尬", "脸红", "羞涩", "胆怯"],
|
80 |
+
confident: ["自信", "骄傲", "确信", "强壮", "坚定"],
|
81 |
+
romantic: ["爱", "浪漫", "温柔", "拥抱", "吻", "亲爱的"],
|
82 |
+
flirtatious: ["调情", "挑逗", "诱惑", "魅力", "撒娇"],
|
83 |
+
goodbye: ["再见", "拜拜", "回头见", "拜", "下次见"],
|
84 |
+
kiss: ["吻", "亲吻", "拥抱", "亲"],
|
85 |
+
dancing: ["跳舞", "舞蹈", "移动", "律动", "步伐"],
|
86 |
+
listening: ["听", "听听", "倾听", "说", "说话", "问题", "提问", "告诉我"]
|
87 |
}
|
88 |
};
|
89 |
|
|
|
585 |
}
|
586 |
},
|
587 |
ja: {
|
588 |
+
affection: {
|
589 |
+
positive: ["愛情", "優しさ", "近い", "温かさ", "親切", "思いやり", "抱きしめる", "愛", "敬愛"],
|
590 |
+
negative: ["意地悪", "冷たい", "無関心", "距離がある", "拒絶", "嫌い", "敵対的", "ばか", "くそ", "アホ"]
|
591 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
592 |
},
|
593 |
zh: {
|
594 |
+
affection: {
|
595 |
+
positive: ["感情", "温柔", "亲近", "温暖", "善良", "关怀", "拥抱", "爱", "崇拜"],
|
596 |
+
negative: ["刻薄", "冷漠", "无动于衷", "疏远", "拒绝", "讨厌", "敌对", "笨蛋", "傻", "婊子"]
|
597 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
598 |
}
|
599 |
};
|
600 |
|
|
|
714 |
zh: ["的", "一", "是", "在", "不", "了", "有", "和", "人", "这", "中", "大", "为", "上", "个", "国", "我", "以", "要"]
|
715 |
};
|
716 |
|
|
|
|
|
|
|
|
|
|
|
|
|
717 |
// Helper function to check if a word is common
|
718 |
window.isCommonWord = function (word, language = "en") {
|
719 |
+
const commonWords = window.KIMI_COMMON_WORDS[language] || window.KIMI_COMMON_WORDS.en;
|
720 |
+
return commonWords.includes(word.toLowerCase());
|
721 |
};
|
722 |
|
723 |
// Emotion detection sensitivity configuration (per language and emotion)
|
|
|
740 |
},
|
741 |
// Example language-specific overrides (can be adjusted via settings if needed)
|
742 |
fr: { romantic: 1.1, laughing: 0.95 },
|
743 |
+
es: { romantic: 1.05 },
|
744 |
+
en: {},
|
745 |
+
de: {},
|
746 |
+
it: {},
|
747 |
+
ja: {},
|
748 |
+
zh: {}
|
749 |
};
|
750 |
|
751 |
// Personality trait adjustment multipliers
|
752 |
// Allows fine-tuning how fast traits evolve globally and per emotion/trait.
|
753 |
window.KIMI_TRAIT_ADJUSTMENT = {
|
754 |
+
globalGain: 1.0,
|
755 |
+
globalLoss: 1.0,
|
756 |
// Per-emotion gain scaling (keys must match KimiEmotionSystem.EMOTIONS values)
|
757 |
emotionGain: {
|
758 |
+
positive: 1.0,
|
759 |
+
negative: 1.0,
|
760 |
+
romantic: 1.0,
|
761 |
+
laughing: 1.0,
|
762 |
+
dancing: 1.0,
|
763 |
+
shy: 1.0,
|
764 |
+
confident: 1.0,
|
765 |
+
flirtatious: 1.0
|
766 |
},
|
767 |
// Per-trait scaling
|
768 |
traitGain: {
|
769 |
+
affection: 1.0,
|
770 |
+
romance: 1.0,
|
771 |
+
empathy: 1.0,
|
772 |
+
playfulness: 1.0,
|
773 |
+
humor: 1.0,
|
774 |
+
intelligence: 1.0
|
775 |
},
|
776 |
traitLoss: {
|
777 |
+
affection: 1.0,
|
778 |
+
romance: 1.0,
|
779 |
empathy: 1.0,
|
780 |
playfulness: 1.0,
|
781 |
humor: 1.0,
|
|
|
970 |
: "";
|
971 |
}
|
972 |
|
973 |
+
const responses = {
|
974 |
+
positive: 5,
|
975 |
+
negative: 5,
|
976 |
+
neutral: 5,
|
977 |
+
romantic: 5,
|
978 |
+
dancing: 5,
|
979 |
+
cold: 5
|
980 |
+
};
|
981 |
+
|
982 |
+
const count = responses[type] || 5;
|
983 |
const randomIndex = index !== null ? index : Math.floor(Math.random() * count) + 1;
|
984 |
|
985 |
return (
|
kimi-js/kimi-database.js
CHANGED
@@ -126,7 +126,14 @@ class KimiDatabase {
|
|
126 |
{ key: "llmProvider", value: "openrouter" },
|
127 |
{ key: "llmBaseUrl", value: "https://openrouter.ai/api/v1/chat/completions" },
|
128 |
{ key: "llmModelId", value: "mistralai/mistral-small-3.2-24b-instruct" },
|
129 |
-
{ key: "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
130 |
];
|
131 |
}
|
132 |
|
@@ -358,7 +365,7 @@ class KimiDatabase {
|
|
358 |
}
|
359 |
|
360 |
async setPreference(key, value) {
|
361 |
-
if (key === "
|
362 |
const isValid = window.KIMI_VALIDATORS?.validateApiKey(value) || window.KimiSecurityUtils?.validateApiKey(value);
|
363 |
if (!isValid && value.length > 0) {
|
364 |
throw new Error("Invalid API key format");
|
|
|
126 |
{ key: "llmProvider", value: "openrouter" },
|
127 |
{ key: "llmBaseUrl", value: "https://openrouter.ai/api/v1/chat/completions" },
|
128 |
{ key: "llmModelId", value: "mistralai/mistral-small-3.2-24b-instruct" },
|
129 |
+
{ key: "llmApiKey", value: "" },
|
130 |
+
// Explicit default for OpenRouter key to avoid missing key errors
|
131 |
+
{ key: "openrouterApiKey", value: "" },
|
132 |
+
{ key: "apiKey_openai", value: "" },
|
133 |
+
{ key: "apiKey_groq", value: "" },
|
134 |
+
{ key: "apiKey_together", value: "" },
|
135 |
+
{ key: "apiKey_deepseek", value: "" },
|
136 |
+
{ key: "apiKey_custom", value: "" }
|
137 |
];
|
138 |
}
|
139 |
|
|
|
365 |
}
|
366 |
|
367 |
async setPreference(key, value) {
|
368 |
+
if (key === "openrouterApiKey" || key === "llmApiKey" || key.startsWith("apiKey_")) {
|
369 |
const isValid = window.KIMI_VALIDATORS?.validateApiKey(value) || window.KimiSecurityUtils?.validateApiKey(value);
|
370 |
if (!isValid && value.length > 0) {
|
371 |
throw new Error("Invalid API key format");
|
kimi-js/kimi-llm-manager.js
CHANGED
@@ -474,7 +474,7 @@ class KimiLLMManager {
|
|
474 |
const provider = await this.db.getPreference("llmProvider", "openai");
|
475 |
const apiKey = KimiProviderUtils
|
476 |
? await KimiProviderUtils.getApiKey(this.db, provider)
|
477 |
-
: await this.db.getPreference("
|
478 |
const modelId = await this.db.getPreference("llmModelId", this.currentModel || "gpt-4o-mini");
|
479 |
if (!apiKey) {
|
480 |
throw new Error("API key not configured for selected provider");
|
@@ -576,7 +576,7 @@ class KimiLLMManager {
|
|
576 |
}
|
577 |
|
578 |
async chatWithOpenRouter(userMessage, options = {}) {
|
579 |
-
const apiKey = await this.db.getPreference("
|
580 |
if (!apiKey) {
|
581 |
throw new Error("OpenRouter API key not configured");
|
582 |
}
|
@@ -1000,11 +1000,7 @@ class KimiLLMManager {
|
|
1000 |
// Check availability on OpenRouter
|
1001 |
try {
|
1002 |
// getAvailableModelsFromAPI removed
|
1003 |
-
return {
|
1004 |
-
available: true,
|
1005 |
-
model: model,
|
1006 |
-
pricing: model.pricing
|
1007 |
-
};
|
1008 |
} catch (error) {
|
1009 |
return {
|
1010 |
available: false,
|
@@ -1013,12 +1009,105 @@ class KimiLLMManager {
|
|
1013 |
}
|
1014 |
}
|
1015 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1016 |
// Fetch models from OpenRouter API and merge into availableModels
|
1017 |
async refreshRemoteModels() {
|
1018 |
if (this._isRefreshingModels) return;
|
1019 |
this._isRefreshingModels = true;
|
1020 |
try {
|
1021 |
-
const apiKey = await this.db.getPreference("
|
1022 |
const res = await fetch("https://openrouter.ai/api/v1/models", {
|
1023 |
method: "GET",
|
1024 |
headers: {
|
|
|
474 |
const provider = await this.db.getPreference("llmProvider", "openai");
|
475 |
const apiKey = KimiProviderUtils
|
476 |
? await KimiProviderUtils.getApiKey(this.db, provider)
|
477 |
+
: await this.db.getPreference("llmApiKey", "");
|
478 |
const modelId = await this.db.getPreference("llmModelId", this.currentModel || "gpt-4o-mini");
|
479 |
if (!apiKey) {
|
480 |
throw new Error("API key not configured for selected provider");
|
|
|
576 |
}
|
577 |
|
578 |
async chatWithOpenRouter(userMessage, options = {}) {
|
579 |
+
const apiKey = await this.db.getPreference("openrouterApiKey");
|
580 |
if (!apiKey) {
|
581 |
throw new Error("OpenRouter API key not configured");
|
582 |
}
|
|
|
1000 |
// Check availability on OpenRouter
|
1001 |
try {
|
1002 |
// getAvailableModelsFromAPI removed
|
1003 |
+
return { available: true, model, pricing: model.pricing };
|
|
|
|
|
|
|
|
|
1004 |
} catch (error) {
|
1005 |
return {
|
1006 |
available: false,
|
|
|
1009 |
}
|
1010 |
}
|
1011 |
|
1012 |
+
/**
|
1013 |
+
* Minimal API test for any provider. Sends only a short system prompt and a single user message in the chosen language.
|
1014 |
+
* No context, no memory, no previous messages, no extra parameters.
|
1015 |
+
* @param {string} provider - Provider name (e.g. 'openrouter', 'openai', 'ollama', etc.)
|
1016 |
+
* @param {string} language - Language code (e.g. 'en', 'fr', 'es', etc.)
|
1017 |
+
* @returns {Promise<{success: boolean, response?: string, error?: string}>}
|
1018 |
+
*/
|
1019 |
+
async testApiKeyMinimal(modelId) {
|
1020 |
+
const originalModel = this.currentModel;
|
1021 |
+
try {
|
1022 |
+
await this.setCurrentModel(modelId);
|
1023 |
+
const provider = await this.db.getPreference("llmProvider", "openrouter");
|
1024 |
+
const lang = await this.db.getPreference("selectedLanguage", "en");
|
1025 |
+
let testWord;
|
1026 |
+
switch (lang) {
|
1027 |
+
case "fr":
|
1028 |
+
testWord = "Bonjour";
|
1029 |
+
break;
|
1030 |
+
case "es":
|
1031 |
+
testWord = "Hola";
|
1032 |
+
break;
|
1033 |
+
case "de":
|
1034 |
+
testWord = "Hallo";
|
1035 |
+
break;
|
1036 |
+
case "it":
|
1037 |
+
testWord = "Ciao";
|
1038 |
+
break;
|
1039 |
+
case "ja":
|
1040 |
+
testWord = "こんにちは";
|
1041 |
+
break;
|
1042 |
+
case "zh":
|
1043 |
+
testWord = "你好";
|
1044 |
+
break;
|
1045 |
+
default:
|
1046 |
+
testWord = "Hello";
|
1047 |
+
}
|
1048 |
+
const systemPrompt = "You are a helpful assistant.";
|
1049 |
+
let apiKey = await this.db.getPreference("providerApiKey");
|
1050 |
+
let baseUrl = "";
|
1051 |
+
let payload = {
|
1052 |
+
model: modelId,
|
1053 |
+
messages: [
|
1054 |
+
{ role: "system", content: systemPrompt },
|
1055 |
+
{ role: "user", content: testWord }
|
1056 |
+
],
|
1057 |
+
max_tokens: 2
|
1058 |
+
};
|
1059 |
+
let headers = { "Content-Type": "application/json" };
|
1060 |
+
if (provider === "openrouter") {
|
1061 |
+
baseUrl = "https://openrouter.ai/api/v1/chat/completions";
|
1062 |
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
1063 |
+
headers["HTTP-Referer"] = window.location.origin;
|
1064 |
+
headers["X-Title"] = "Kimi - Virtual Companion";
|
1065 |
+
} else if (["openai", "groq", "together", "deepseek"].includes(provider)) {
|
1066 |
+
baseUrl = await this.db.getPreference("llmBaseUrl", "https://api.openai.com/v1/chat/completions");
|
1067 |
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
1068 |
+
} else if (provider === "ollama") {
|
1069 |
+
baseUrl = "http://localhost:11434/api/chat";
|
1070 |
+
payload = {
|
1071 |
+
model: modelId,
|
1072 |
+
messages: [
|
1073 |
+
{ role: "system", content: systemPrompt },
|
1074 |
+
{ role: "user", content: testWord }
|
1075 |
+
],
|
1076 |
+
stream: false
|
1077 |
+
};
|
1078 |
+
} else {
|
1079 |
+
throw new Error("Unknown provider: " + provider);
|
1080 |
+
}
|
1081 |
+
const response = await fetch(baseUrl, {
|
1082 |
+
method: "POST",
|
1083 |
+
headers,
|
1084 |
+
body: JSON.stringify(payload)
|
1085 |
+
});
|
1086 |
+
if (!response.ok) {
|
1087 |
+
const error = await response.text();
|
1088 |
+
return { success: false, error };
|
1089 |
+
}
|
1090 |
+
const data = await response.json();
|
1091 |
+
let content = "";
|
1092 |
+
if (provider === "ollama") {
|
1093 |
+
content = data?.message?.content || data?.choices?.[0]?.message?.content || "";
|
1094 |
+
} else {
|
1095 |
+
content = data?.choices?.[0]?.message?.content || "";
|
1096 |
+
}
|
1097 |
+
return { success: true, response: content };
|
1098 |
+
} catch (error) {
|
1099 |
+
return { success: false, error: error.message };
|
1100 |
+
} finally {
|
1101 |
+
await this.setCurrentModel(originalModel);
|
1102 |
+
}
|
1103 |
+
}
|
1104 |
+
|
1105 |
// Fetch models from OpenRouter API and merge into availableModels
|
1106 |
async refreshRemoteModels() {
|
1107 |
if (this._isRefreshingModels) return;
|
1108 |
this._isRefreshingModels = true;
|
1109 |
try {
|
1110 |
+
const apiKey = await this.db.getPreference("openrouterApiKey", "");
|
1111 |
const res = await fetch("https://openrouter.ai/api/v1/models", {
|
1112 |
method: "GET",
|
1113 |
headers: {
|
kimi-js/kimi-memory-system.js
CHANGED
@@ -840,8 +840,7 @@ class KimiMemorySystem {
|
|
840 |
if (memoryData.content && memoryData.content.length > 24) importance += 0.05;
|
841 |
if (memoryData.confidence && memoryData.confidence > 0.9) importance += 0.05;
|
842 |
|
843 |
-
|
844 |
-
return Math.min(1.0, Math.round(importance * 100) / 100);
|
845 |
}
|
846 |
|
847 |
// Derive semantic tags from memory content to assist prioritization and merging
|
|
|
840 |
if (memoryData.content && memoryData.content.length > 24) importance += 0.05;
|
841 |
if (memoryData.confidence && memoryData.confidence > 0.9) importance += 0.05;
|
842 |
|
843 |
+
return Math.min(1.0, importance);
|
|
|
844 |
}
|
845 |
|
846 |
// Derive semantic tags from memory content to assist prioritization and merging
|
kimi-js/kimi-module.js
CHANGED
@@ -747,10 +747,11 @@ async function loadSettingsData() {
|
|
747 |
"voicePitch",
|
748 |
"voiceVolume",
|
749 |
"selectedLanguage",
|
750 |
-
"
|
751 |
"llmProvider",
|
752 |
"llmBaseUrl",
|
753 |
"llmModelId",
|
|
|
754 |
"selectedCharacter",
|
755 |
"llmTemperature",
|
756 |
"llmMaxTokens",
|
@@ -765,10 +766,11 @@ async function loadSettingsData() {
|
|
765 |
const voicePitch = preferences.voicePitch !== undefined ? preferences.voicePitch : 1.1;
|
766 |
const voiceVolume = preferences.voiceVolume !== undefined ? preferences.voiceVolume : 0.8;
|
767 |
const selectedLanguage = preferences.selectedLanguage || "en";
|
768 |
-
const apiKey = preferences.
|
769 |
const provider = preferences.llmProvider || "openrouter";
|
770 |
const baseUrl = preferences.llmBaseUrl || "https://openrouter.ai/api/v1/chat/completions";
|
771 |
const modelId = preferences.llmModelId || (window.kimiLLM ? window.kimiLLM.currentModel : "");
|
|
|
772 |
const selectedCharacter = preferences.selectedCharacter || "kimi";
|
773 |
const llmTemperature = preferences.llmTemperature !== undefined ? preferences.llmTemperature : 0.8;
|
774 |
const llmMaxTokens = preferences.llmMaxTokens !== undefined ? preferences.llmMaxTokens : 400;
|
@@ -1339,17 +1341,10 @@ async function sendMessage() {
|
|
1339 |
|
1340 |
try {
|
1341 |
const response = await analyzeAndReact(message);
|
1342 |
-
let finalResponse = response;
|
1343 |
-
// Si la réponse du LLM est vide, nulle ou trop courte, utilise le fallback émotionnel
|
1344 |
-
if (!finalResponse || typeof finalResponse !== "string" || finalResponse.trim().length < 2) {
|
1345 |
-
finalResponse = window.getLocalizedEmotionalResponse
|
1346 |
-
? window.getLocalizedEmotionalResponse("neutral")
|
1347 |
-
: "I'm here for you!";
|
1348 |
-
}
|
1349 |
setTimeout(() => {
|
1350 |
-
addMessageToChat("kimi",
|
1351 |
if (window.voiceManager && !message.startsWith("Vous:")) {
|
1352 |
-
window.voiceManager.speak(
|
1353 |
}
|
1354 |
if (waitingIndicator) waitingIndicator.style.display = "none";
|
1355 |
}, 1000);
|
@@ -1616,7 +1611,7 @@ function setupSettingsListeners(kimiDB, kimiMemory) {
|
|
1616 |
if (kimiDB) await kimiDB.setPreference("colorTheme", e.target.value);
|
1617 |
if (window.kimiAppearanceManager && window.kimiAppearanceManager.changeTheme)
|
1618 |
await window.kimiAppearanceManager.changeTheme(e.target.value);
|
1619 |
-
|
1620 |
};
|
1621 |
colorThemeSelect.addEventListener("change", window._kimiColorThemeListener);
|
1622 |
}
|
|
|
747 |
"voicePitch",
|
748 |
"voiceVolume",
|
749 |
"selectedLanguage",
|
750 |
+
"openrouterApiKey",
|
751 |
"llmProvider",
|
752 |
"llmBaseUrl",
|
753 |
"llmModelId",
|
754 |
+
"llmApiKey",
|
755 |
"selectedCharacter",
|
756 |
"llmTemperature",
|
757 |
"llmMaxTokens",
|
|
|
766 |
const voicePitch = preferences.voicePitch !== undefined ? preferences.voicePitch : 1.1;
|
767 |
const voiceVolume = preferences.voiceVolume !== undefined ? preferences.voiceVolume : 0.8;
|
768 |
const selectedLanguage = preferences.selectedLanguage || "en";
|
769 |
+
const apiKey = preferences.openrouterApiKey || "";
|
770 |
const provider = preferences.llmProvider || "openrouter";
|
771 |
const baseUrl = preferences.llmBaseUrl || "https://openrouter.ai/api/v1/chat/completions";
|
772 |
const modelId = preferences.llmModelId || (window.kimiLLM ? window.kimiLLM.currentModel : "");
|
773 |
+
const genericKey = preferences.llmApiKey || "";
|
774 |
const selectedCharacter = preferences.selectedCharacter || "kimi";
|
775 |
const llmTemperature = preferences.llmTemperature !== undefined ? preferences.llmTemperature : 0.8;
|
776 |
const llmMaxTokens = preferences.llmMaxTokens !== undefined ? preferences.llmMaxTokens : 400;
|
|
|
1341 |
|
1342 |
try {
|
1343 |
const response = await analyzeAndReact(message);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1344 |
setTimeout(() => {
|
1345 |
+
addMessageToChat("kimi", response);
|
1346 |
if (window.voiceManager && !message.startsWith("Vous:")) {
|
1347 |
+
window.voiceManager.speak(response);
|
1348 |
}
|
1349 |
if (waitingIndicator) waitingIndicator.style.display = "none";
|
1350 |
}, 1000);
|
|
|
1611 |
if (kimiDB) await kimiDB.setPreference("colorTheme", e.target.value);
|
1612 |
if (window.kimiAppearanceManager && window.kimiAppearanceManager.changeTheme)
|
1613 |
await window.kimiAppearanceManager.changeTheme(e.target.value);
|
1614 |
+
if (window.KimiPluginManager && window.KimiPluginManager.loadPlugins) await window.KimiPluginManager.loadPlugins();
|
1615 |
};
|
1616 |
colorThemeSelect.addEventListener("change", window._kimiColorThemeListener);
|
1617 |
}
|
kimi-js/kimi-script.js
CHANGED
@@ -80,7 +80,7 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
80 |
const ApiUi = {
|
81 |
presenceDot: () => document.getElementById("api-key-presence"),
|
82 |
presenceDotTest: () => document.getElementById("api-key-presence-test"),
|
83 |
-
apiKeyInput: () => document.getElementById("
|
84 |
toggleBtn: () => document.getElementById("toggle-api-key"),
|
85 |
providerSelect: () => document.getElementById("llm-provider"),
|
86 |
baseUrlInput: () => document.getElementById("llm-base-url"),
|
@@ -150,9 +150,7 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
150 |
}
|
151 |
}
|
152 |
// Load the provider-specific key
|
153 |
-
const keyPref = window.KimiProviderUtils
|
154 |
-
? window.KimiProviderUtils.getKeyPrefForProvider(provider)
|
155 |
-
: "providerApiKey";
|
156 |
const storedKey = await window.kimiDB.getPreference(keyPref, "");
|
157 |
if (apiKeyInput) apiKeyInput.value = storedKey || "";
|
158 |
ApiUi.setPresence(storedKey ? "#4caf50" : "#9e9e9e");
|
@@ -243,18 +241,7 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
243 |
baseUrlInput.placeholder = p.url;
|
244 |
baseUrlInput.value = provider === "openrouter" ? placeholders.openrouter.url : p.url;
|
245 |
}
|
246 |
-
if (apiKeyInput)
|
247 |
-
apiKeyInput.placeholder = p.keyPh;
|
248 |
-
// Masquer/désactiver le champ pour Ollama/local
|
249 |
-
if (provider === "ollama") {
|
250 |
-
apiKeyInput.value = "";
|
251 |
-
apiKeyInput.disabled = true;
|
252 |
-
apiKeyInput.style.display = "none";
|
253 |
-
} else {
|
254 |
-
apiKeyInput.disabled = false;
|
255 |
-
apiKeyInput.style.display = "";
|
256 |
-
}
|
257 |
-
}
|
258 |
if (modelIdInput) {
|
259 |
modelIdInput.placeholder = p.model;
|
260 |
// Only populate the field for OpenRouter since those are the models we have in the list
|
@@ -266,11 +253,9 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
266 |
await window.kimiDB.setPreference("llmBaseUrl", provider === "openrouter" ? placeholders.openrouter.url : p.url);
|
267 |
const apiKeyLabel = document.getElementById("api-key-label");
|
268 |
// Load provider-specific key into the input for clarity
|
269 |
-
const keyPref = window.KimiProviderUtils
|
270 |
-
|
271 |
-
|
272 |
-
const storedKey = await window.kimiDB.getPreference("providerApiKey", "");
|
273 |
-
if (apiKeyInput && provider !== "ollama") apiKeyInput.value = storedKey || "";
|
274 |
const color = provider === "ollama" ? "#9e9e9e" : storedKey && storedKey.length > 0 ? "#4caf50" : "#9e9e9e";
|
275 |
ApiUi.setPresence(color);
|
276 |
// Changing provider invalidates previous test state
|
@@ -288,13 +273,7 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
288 |
: "API Key";
|
289 |
}
|
290 |
const savedBadge = ApiUi.savedBadge();
|
291 |
-
if (savedBadge)
|
292 |
-
if (provider !== "ollama" && storedKey) {
|
293 |
-
savedBadge.style.display = "inline";
|
294 |
-
} else {
|
295 |
-
savedBadge.style.display = "none";
|
296 |
-
}
|
297 |
-
}
|
298 |
ApiUi.clearStatus();
|
299 |
}
|
300 |
});
|
@@ -383,10 +362,6 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
383 |
|
384 |
await window.kimiDB.setSelectedCharacter(charKey);
|
385 |
await window.kimiDB.setSystemPromptForCharacter(charKey, prompt);
|
386 |
-
// Ensure memory system uses the correct character
|
387 |
-
if (window.kimiMemorySystem) {
|
388 |
-
window.kimiMemorySystem.selectedCharacter = charKey;
|
389 |
-
}
|
390 |
if (window.kimiVideo && window.kimiVideo.setCharacter) {
|
391 |
window.kimiVideo.setCharacter(charKey);
|
392 |
if (window.kimiVideo.switchToContext) {
|
@@ -403,10 +378,6 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
403 |
settingsPanel.scrollTop = scrollTop;
|
404 |
});
|
405 |
}
|
406 |
-
// Refresh memory tab after character selection
|
407 |
-
if (window.kimiMemoryUI && typeof window.kimiMemoryUI.updateMemoryStats === "function") {
|
408 |
-
await window.kimiMemoryUI.updateMemoryStats();
|
409 |
-
}
|
410 |
saveCharacterBtn.setAttribute("data-i18n", "saved");
|
411 |
saveCharacterBtn.classList.add("success");
|
412 |
saveCharacterBtn.disabled = true;
|
@@ -586,67 +557,19 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
586 |
if (testApiButton) {
|
587 |
testApiButton.addEventListener("click", async () => {
|
588 |
const statusSpan = ApiUi.statusSpan();
|
589 |
-
const apiKeyInput = ApiUi.apiKeyInput();
|
590 |
-
const apiKey = apiKeyInput ? apiKeyInput.value.trim() : "";
|
591 |
const providerSelect = ApiUi.providerSelect();
|
592 |
-
const baseUrlInput = ApiUi.baseUrlInput();
|
593 |
-
const modelIdInput = ApiUi.modelIdInput();
|
594 |
const provider = providerSelect ? providerSelect.value : "openrouter";
|
595 |
-
const
|
596 |
-
const modelId = modelIdInput ? modelIdInput.value.trim() : "";
|
597 |
-
|
598 |
if (!statusSpan) return;
|
599 |
-
|
600 |
-
if (provider !== "ollama" && !apiKey) {
|
601 |
-
statusSpan.textContent = window.kimiI18nManager?.t("api_key_missing") || "API key missing";
|
602 |
-
statusSpan.style.color = "#ff6b6b";
|
603 |
-
return;
|
604 |
-
}
|
605 |
-
|
606 |
-
// Validate API key format before saving/testing
|
607 |
-
if (provider !== "ollama") {
|
608 |
-
const isValid = (window.KIMI_VALIDATORS && window.KIMI_VALIDATORS.validateApiKey(apiKey)) || false;
|
609 |
-
if (!isValid) {
|
610 |
-
statusSpan.textContent =
|
611 |
-
window.kimiI18nManager?.t("api_key_invalid_format") ||
|
612 |
-
"Invalid API key format (must start with sk-or-v1-)";
|
613 |
-
statusSpan.style.color = "#ff6b6b";
|
614 |
-
return;
|
615 |
-
}
|
616 |
-
}
|
617 |
-
|
618 |
-
if (window.kimiDB) {
|
619 |
-
// Save API key under provider-specific preference key (skip for Ollama)
|
620 |
-
if (provider !== "ollama") {
|
621 |
-
const keyPref = window.KimiProviderUtils
|
622 |
-
? window.KimiProviderUtils.getKeyPrefForProvider(provider)
|
623 |
-
: "providerApiKey";
|
624 |
-
await window.kimiDB.setPreference(keyPref, apiKey);
|
625 |
-
}
|
626 |
-
await window.kimiDB.setPreference("llmProvider", provider);
|
627 |
-
if (baseUrl) await window.kimiDB.setPreference("llmBaseUrl", baseUrl);
|
628 |
-
if (modelId) await window.kimiDB.setPreference("llmModelId", modelId);
|
629 |
-
}
|
630 |
-
|
631 |
statusSpan.textContent = "Testing in progress...";
|
632 |
statusSpan.style.color = "#ffa726";
|
633 |
-
|
634 |
try {
|
635 |
if (window.kimiLLM) {
|
636 |
-
|
637 |
-
if (provider === "openrouter") {
|
638 |
-
result = await window.kimiLLM.testModel(window.kimiLLM.currentModel, "Bonjour");
|
639 |
-
} else if (provider === "ollama") {
|
640 |
-
const response = await window.kimiLLM.chatWithLocal("Bonjour", { maxTokens: 2 });
|
641 |
-
result = { success: true, response };
|
642 |
-
} else {
|
643 |
-
const response = await window.kimiLLM.chatWithOpenAICompatible("Bonjour", { maxTokens: 2 });
|
644 |
-
result = { success: true, response };
|
645 |
-
}
|
646 |
if (result.success) {
|
647 |
statusSpan.textContent = "Connection successful!";
|
648 |
statusSpan.style.color = "#4caf50";
|
649 |
-
// Only show saved badge if an actual non-empty API key is stored and provider requires one
|
650 |
const savedBadge = ApiUi.savedBadge();
|
651 |
if (savedBadge) {
|
652 |
const apiKeyInputEl = ApiUi.apiKeyInput();
|
@@ -659,21 +582,16 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
659 |
savedBadge.style.display = "none";
|
660 |
}
|
661 |
}
|
662 |
-
|
663 |
if (result.response) {
|
664 |
setTimeout(() => {
|
665 |
statusSpan.textContent = `Test response: \"${result.response.substring(0, 50)}...\"`;
|
666 |
}, 1000);
|
667 |
}
|
668 |
-
// Mark test success explicitly
|
669 |
ApiUi.setTestPresence("#4caf50");
|
670 |
} else {
|
671 |
statusSpan.textContent = `${result.error}`;
|
672 |
statusSpan.style.color = "#ff6b6b";
|
673 |
ApiUi.setTestPresence("#9e9e9e");
|
674 |
-
if (result.error.includes("similaires disponibles")) {
|
675 |
-
setTimeout(() => {}, 1000);
|
676 |
-
}
|
677 |
}
|
678 |
} else {
|
679 |
statusSpan.textContent = "LLM manager not initialized";
|
@@ -685,10 +603,6 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
685 |
statusSpan.textContent = `Error: ${error.message}`;
|
686 |
statusSpan.style.color = "#ff6b6b";
|
687 |
ApiUi.setTestPresence("#9e9e9e");
|
688 |
-
|
689 |
-
if (error.message.includes("non disponible")) {
|
690 |
-
setTimeout(() => {}, 1000);
|
691 |
-
}
|
692 |
}
|
693 |
});
|
694 |
}
|
@@ -703,9 +617,7 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
703 |
t = setTimeout(async () => {
|
704 |
const providerEl = ApiUi.providerSelect();
|
705 |
const provider = providerEl ? providerEl.value : "openrouter";
|
706 |
-
const keyPref = window.KimiProviderUtils
|
707 |
-
? window.KimiProviderUtils.getKeyPrefForProvider(provider)
|
708 |
-
: "providerApiKey";
|
709 |
const value = input.value.trim();
|
710 |
// Update Test button state immediately
|
711 |
const validNow = !!(window.KIMI_VALIDATORS && window.KIMI_VALIDATORS.validateApiKey(value));
|
|
|
80 |
const ApiUi = {
|
81 |
presenceDot: () => document.getElementById("api-key-presence"),
|
82 |
presenceDotTest: () => document.getElementById("api-key-presence-test"),
|
83 |
+
apiKeyInput: () => document.getElementById("openrouter-api-key"),
|
84 |
toggleBtn: () => document.getElementById("toggle-api-key"),
|
85 |
providerSelect: () => document.getElementById("llm-provider"),
|
86 |
baseUrlInput: () => document.getElementById("llm-base-url"),
|
|
|
150 |
}
|
151 |
}
|
152 |
// Load the provider-specific key
|
153 |
+
const keyPref = window.KimiProviderUtils ? window.KimiProviderUtils.getKeyPrefForProvider(provider) : "llmApiKey";
|
|
|
|
|
154 |
const storedKey = await window.kimiDB.getPreference(keyPref, "");
|
155 |
if (apiKeyInput) apiKeyInput.value = storedKey || "";
|
156 |
ApiUi.setPresence(storedKey ? "#4caf50" : "#9e9e9e");
|
|
|
241 |
baseUrlInput.placeholder = p.url;
|
242 |
baseUrlInput.value = provider === "openrouter" ? placeholders.openrouter.url : p.url;
|
243 |
}
|
244 |
+
if (apiKeyInput) apiKeyInput.placeholder = p.keyPh;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
245 |
if (modelIdInput) {
|
246 |
modelIdInput.placeholder = p.model;
|
247 |
// Only populate the field for OpenRouter since those are the models we have in the list
|
|
|
253 |
await window.kimiDB.setPreference("llmBaseUrl", provider === "openrouter" ? placeholders.openrouter.url : p.url);
|
254 |
const apiKeyLabel = document.getElementById("api-key-label");
|
255 |
// Load provider-specific key into the input for clarity
|
256 |
+
const keyPref = window.KimiProviderUtils ? window.KimiProviderUtils.getKeyPrefForProvider(provider) : "llmApiKey";
|
257 |
+
const storedKey = await window.kimiDB.getPreference(keyPref, "");
|
258 |
+
if (apiKeyInput) apiKeyInput.value = storedKey || "";
|
|
|
|
|
259 |
const color = provider === "ollama" ? "#9e9e9e" : storedKey && storedKey.length > 0 ? "#4caf50" : "#9e9e9e";
|
260 |
ApiUi.setPresence(color);
|
261 |
// Changing provider invalidates previous test state
|
|
|
273 |
: "API Key";
|
274 |
}
|
275 |
const savedBadge = ApiUi.savedBadge();
|
276 |
+
if (savedBadge) savedBadge.style.display = "none";
|
|
|
|
|
|
|
|
|
|
|
|
|
277 |
ApiUi.clearStatus();
|
278 |
}
|
279 |
});
|
|
|
362 |
|
363 |
await window.kimiDB.setSelectedCharacter(charKey);
|
364 |
await window.kimiDB.setSystemPromptForCharacter(charKey, prompt);
|
|
|
|
|
|
|
|
|
365 |
if (window.kimiVideo && window.kimiVideo.setCharacter) {
|
366 |
window.kimiVideo.setCharacter(charKey);
|
367 |
if (window.kimiVideo.switchToContext) {
|
|
|
378 |
settingsPanel.scrollTop = scrollTop;
|
379 |
});
|
380 |
}
|
|
|
|
|
|
|
|
|
381 |
saveCharacterBtn.setAttribute("data-i18n", "saved");
|
382 |
saveCharacterBtn.classList.add("success");
|
383 |
saveCharacterBtn.disabled = true;
|
|
|
557 |
if (testApiButton) {
|
558 |
testApiButton.addEventListener("click", async () => {
|
559 |
const statusSpan = ApiUi.statusSpan();
|
|
|
|
|
560 |
const providerSelect = ApiUi.providerSelect();
|
|
|
|
|
561 |
const provider = providerSelect ? providerSelect.value : "openrouter";
|
562 |
+
const modelIdInput = ApiUi.modelIdInput();
|
563 |
+
const modelId = modelIdInput ? modelIdInput.value.trim() : window.kimiLLM ? window.kimiLLM.currentModel : "model-id";
|
|
|
564 |
if (!statusSpan) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
565 |
statusSpan.textContent = "Testing in progress...";
|
566 |
statusSpan.style.color = "#ffa726";
|
|
|
567 |
try {
|
568 |
if (window.kimiLLM) {
|
569 |
+
const result = await window.kimiLLM.testApiKeyMinimal(modelId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
570 |
if (result.success) {
|
571 |
statusSpan.textContent = "Connection successful!";
|
572 |
statusSpan.style.color = "#4caf50";
|
|
|
573 |
const savedBadge = ApiUi.savedBadge();
|
574 |
if (savedBadge) {
|
575 |
const apiKeyInputEl = ApiUi.apiKeyInput();
|
|
|
582 |
savedBadge.style.display = "none";
|
583 |
}
|
584 |
}
|
|
|
585 |
if (result.response) {
|
586 |
setTimeout(() => {
|
587 |
statusSpan.textContent = `Test response: \"${result.response.substring(0, 50)}...\"`;
|
588 |
}, 1000);
|
589 |
}
|
|
|
590 |
ApiUi.setTestPresence("#4caf50");
|
591 |
} else {
|
592 |
statusSpan.textContent = `${result.error}`;
|
593 |
statusSpan.style.color = "#ff6b6b";
|
594 |
ApiUi.setTestPresence("#9e9e9e");
|
|
|
|
|
|
|
595 |
}
|
596 |
} else {
|
597 |
statusSpan.textContent = "LLM manager not initialized";
|
|
|
603 |
statusSpan.textContent = `Error: ${error.message}`;
|
604 |
statusSpan.style.color = "#ff6b6b";
|
605 |
ApiUi.setTestPresence("#9e9e9e");
|
|
|
|
|
|
|
|
|
606 |
}
|
607 |
});
|
608 |
}
|
|
|
617 |
t = setTimeout(async () => {
|
618 |
const providerEl = ApiUi.providerSelect();
|
619 |
const provider = providerEl ? providerEl.value : "openrouter";
|
620 |
+
const keyPref = window.KimiProviderUtils ? window.KimiProviderUtils.getKeyPrefForProvider(provider) : "llmApiKey";
|
|
|
|
|
621 |
const value = input.value.trim();
|
622 |
// Update Test button state immediately
|
623 |
const validNow = !!(window.KIMI_VALIDATORS && window.KIMI_VALIDATORS.validateApiKey(value));
|
kimi-js/kimi-utils.js
CHANGED
@@ -25,7 +25,7 @@ window.KimiValidationUtils = {
|
|
25 |
voicePitch: { min: 0, max: 2, def: 1.0 },
|
26 |
voiceVolume: { min: 0, max: 1, def: 0.8 },
|
27 |
llmTemperature: { min: 0, max: 1, def: 0.8 },
|
28 |
-
llmMaxTokens: { min: 1, max:
|
29 |
llmTopP: { min: 0, max: 1, def: 0.9 },
|
30 |
llmFrequencyPenalty: { min: 0, max: 2, def: 0.6 },
|
31 |
llmPresencePenalty: { min: 0, max: 2, def: 0.5 },
|
@@ -43,14 +43,26 @@ window.KimiValidationUtils = {
|
|
43 |
|
44 |
// Provider utilities used across the app
|
45 |
const KimiProviderUtils = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
getKeyPrefForProvider(provider) {
|
47 |
-
|
48 |
-
return provider === "ollama" ? null : "providerApiKey";
|
49 |
},
|
50 |
async getApiKey(db, provider) {
|
51 |
if (!db) return null;
|
52 |
if (provider === "ollama") return "__local__";
|
53 |
-
|
|
|
|
|
|
|
54 |
},
|
55 |
getLabelForProvider(provider) {
|
56 |
const labels = {
|
@@ -201,6 +213,8 @@ class KimiSecurityUtils {
|
|
201 |
}
|
202 |
return key.trim().length > 10 && (key.startsWith("sk-") || key.startsWith("sk-or-"));
|
203 |
}
|
|
|
|
|
204 |
}
|
205 |
|
206 |
// Cache management for better performance
|
@@ -370,7 +384,14 @@ class KimiVideoManager {
|
|
370 |
this._consecutiveErrorCount = 0;
|
371 |
}
|
372 |
|
373 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
374 |
static crossfadeVideos(fromVideo, toVideo, duration = 300, onComplete) {
|
375 |
// Resolve duration from CSS variable if present
|
376 |
try {
|
@@ -418,7 +439,12 @@ class KimiVideoManager {
|
|
418 |
if (fromVideo.paused) fromVideo.play().catch(() => {});
|
419 |
}
|
420 |
|
421 |
-
|
|
|
|
|
|
|
|
|
|
|
422 |
static createVideoElement(id, className = "bg-video") {
|
423 |
const video = document.createElement("video");
|
424 |
video.id = id;
|
@@ -433,7 +459,11 @@ class KimiVideoManager {
|
|
433 |
return video;
|
434 |
}
|
435 |
|
436 |
-
|
|
|
|
|
|
|
|
|
437 |
static getVideoElement(selector) {
|
438 |
if (typeof selector === "string") {
|
439 |
if (selector.startsWith("#")) {
|
@@ -961,6 +991,7 @@ class KimiVideoManager {
|
|
961 |
}
|
962 |
|
963 |
// keep only the augmented determineCategory above (with traits)
|
|
|
964 |
selectOptimalVideo(category, specificVideo = null, traits = null, affection = null, emotion = null) {
|
965 |
const availableVideos = this.videoCategories[category] || this.videoCategories.neutral;
|
966 |
|
@@ -1181,7 +1212,7 @@ class KimiVideoManager {
|
|
1181 |
this.isEmotionVideoPlaying = false;
|
1182 |
this.currentEmotionContext = null;
|
1183 |
|
1184 |
-
//
|
1185 |
const category = "neutral";
|
1186 |
const currentVideoSrc = this.activeVideo.querySelector("source").getAttribute("src");
|
1187 |
const available = this.videoCategories[category] || [];
|
@@ -1218,6 +1249,7 @@ class KimiVideoManager {
|
|
1218 |
}
|
1219 |
|
1220 |
// ADVANCED CONTEXTUAL ANALYSIS
|
|
|
1221 |
async analyzeAndSelectVideo(userMessage, kimiResponse, emotionAnalysis, traits = null, affection = null, lang = null) {
|
1222 |
// Do not analyze-switch away while dancing is sticky/playing
|
1223 |
if (this._stickyContext === "dancing" || this.currentContext === "dancing") {
|
|
|
25 |
voicePitch: { min: 0, max: 2, def: 1.0 },
|
26 |
voiceVolume: { min: 0, max: 1, def: 0.8 },
|
27 |
llmTemperature: { min: 0, max: 1, def: 0.8 },
|
28 |
+
llmMaxTokens: { min: 1, max: 8192, def: 400 },
|
29 |
llmTopP: { min: 0, max: 1, def: 0.9 },
|
30 |
llmFrequencyPenalty: { min: 0, max: 2, def: 0.6 },
|
31 |
llmPresencePenalty: { min: 0, max: 2, def: 0.5 },
|
|
|
43 |
|
44 |
// Provider utilities used across the app
|
45 |
const KimiProviderUtils = {
|
46 |
+
keyPrefMap: {
|
47 |
+
openrouter: "openrouterApiKey",
|
48 |
+
openai: "apiKey_openai",
|
49 |
+
groq: "apiKey_groq",
|
50 |
+
together: "apiKey_together",
|
51 |
+
deepseek: "apiKey_deepseek",
|
52 |
+
custom: "apiKey_custom",
|
53 |
+
"openai-compatible": "llmApiKey",
|
54 |
+
ollama: null
|
55 |
+
},
|
56 |
getKeyPrefForProvider(provider) {
|
57 |
+
return this.keyPrefMap[provider] || "llmApiKey";
|
|
|
58 |
},
|
59 |
async getApiKey(db, provider) {
|
60 |
if (!db) return null;
|
61 |
if (provider === "ollama") return "__local__";
|
62 |
+
const pref = this.getKeyPrefForProvider(provider);
|
63 |
+
if (!pref) return null;
|
64 |
+
if (provider === "openrouter") return await db.getPreference("openrouterApiKey");
|
65 |
+
return await db.getPreference(pref);
|
66 |
},
|
67 |
getLabelForProvider(provider) {
|
68 |
const labels = {
|
|
|
213 |
}
|
214 |
return key.trim().length > 10 && (key.startsWith("sk-") || key.startsWith("sk-or-"));
|
215 |
}
|
216 |
+
|
217 |
+
// Removed unused encrypt/decrypt for clarity; storage should rely on secure contexts if reintroduced
|
218 |
}
|
219 |
|
220 |
// Cache management for better performance
|
|
|
384 |
this._consecutiveErrorCount = 0;
|
385 |
}
|
386 |
|
387 |
+
/**
|
388 |
+
* Centralized crossfade transition between two videos.
|
389 |
+
* Ensures both videos are loaded and playing before transition.
|
390 |
+
* @param {HTMLVideoElement} fromVideo - The currently visible video.
|
391 |
+
* @param {HTMLVideoElement} toVideo - The next video to show.
|
392 |
+
* @param {number} duration - Transition duration in ms.
|
393 |
+
* @param {function} [onComplete] - Optional callback after transition.
|
394 |
+
*/
|
395 |
static crossfadeVideos(fromVideo, toVideo, duration = 300, onComplete) {
|
396 |
// Resolve duration from CSS variable if present
|
397 |
try {
|
|
|
439 |
if (fromVideo.paused) fromVideo.play().catch(() => {});
|
440 |
}
|
441 |
|
442 |
+
/**
|
443 |
+
* Centralized video element creation utility.
|
444 |
+
* @param {string} id - The id for the video element.
|
445 |
+
* @param {string} [className] - Optional class name.
|
446 |
+
* @returns {HTMLVideoElement}
|
447 |
+
*/
|
448 |
static createVideoElement(id, className = "bg-video") {
|
449 |
const video = document.createElement("video");
|
450 |
video.id = id;
|
|
|
459 |
return video;
|
460 |
}
|
461 |
|
462 |
+
/**
|
463 |
+
* Centralized video selection utility.
|
464 |
+
* @param {string} selector - CSS selector or id.
|
465 |
+
* @returns {HTMLVideoElement|null}
|
466 |
+
*/
|
467 |
static getVideoElement(selector) {
|
468 |
if (typeof selector === "string") {
|
469 |
if (selector.startsWith("#")) {
|
|
|
991 |
}
|
992 |
|
993 |
// keep only the augmented determineCategory above (with traits)
|
994 |
+
|
995 |
selectOptimalVideo(category, specificVideo = null, traits = null, affection = null, emotion = null) {
|
996 |
const availableVideos = this.videoCategories[category] || this.videoCategories.neutral;
|
997 |
|
|
|
1212 |
this.isEmotionVideoPlaying = false;
|
1213 |
this.currentEmotionContext = null;
|
1214 |
|
1215 |
+
// Correction : si la voix est encore en cours, relancer une vidéo neutre en boucle
|
1216 |
const category = "neutral";
|
1217 |
const currentVideoSrc = this.activeVideo.querySelector("source").getAttribute("src");
|
1218 |
const available = this.videoCategories[category] || [];
|
|
|
1249 |
}
|
1250 |
|
1251 |
// ADVANCED CONTEXTUAL ANALYSIS
|
1252 |
+
// ADVANCED CONTEXTUAL ANALYSIS - SIMPLIFIED
|
1253 |
async analyzeAndSelectVideo(userMessage, kimiResponse, emotionAnalysis, traits = null, affection = null, lang = null) {
|
1254 |
// Do not analyze-switch away while dancing is sticky/playing
|
1255 |
if (this._stickyContext === "dancing" || this.currentContext === "dancing") {
|
kimi-locale/de.json
CHANGED
@@ -143,7 +143,7 @@
|
|
143 |
"character_summary_bella": "Fröhlich, fürsorglich, sieht Menschen als Pflanzen, die Pflege brauchen",
|
144 |
"character_summary_rosa": "Chaotisch, aufmerksamkeitssuchend, gedeiht in kontrolliertem Chaos",
|
145 |
"character_summary_stella": "Launisch, künstlerisch, fantasievoll, verspielt, verwandelt Chaos in Kunst",
|
146 |
-
"fallback_api_missing": "Um wirklich mit mir zu chatten, füge deinen API-Schlüssel in den Einstellungen hinzu! 💕",
|
147 |
"fallback_api_error": "Entschuldigung, der KI-Service ist vorübergehend nicht verfügbar. Bitte versuche es später noch einmal.",
|
148 |
"fallback_model_error": "Entschuldigung, das ausgewählte Modell ist nicht verfügbar. Bitte wähle ein anderes Modell oder überprüfe deine Konfiguration.",
|
149 |
"fallback_network_error": "Entschuldigung, ich kann nicht antworten, da keine Internetverbindung besteht.",
|
|
|
143 |
"character_summary_bella": "Fröhlich, fürsorglich, sieht Menschen als Pflanzen, die Pflege brauchen",
|
144 |
"character_summary_rosa": "Chaotisch, aufmerksamkeitssuchend, gedeiht in kontrolliertem Chaos",
|
145 |
"character_summary_stella": "Launisch, künstlerisch, fantasievoll, verspielt, verwandelt Chaos in Kunst",
|
146 |
+
"fallback_api_missing": "Um wirklich mit mir zu chatten, füge deinen OpenRouter API-Schlüssel in den Einstellungen hinzu! 💕",
|
147 |
"fallback_api_error": "Entschuldigung, der KI-Service ist vorübergehend nicht verfügbar. Bitte versuche es später noch einmal.",
|
148 |
"fallback_model_error": "Entschuldigung, das ausgewählte Modell ist nicht verfügbar. Bitte wähle ein anderes Modell oder überprüfe deine Konfiguration.",
|
149 |
"fallback_network_error": "Entschuldigung, ich kann nicht antworten, da keine Internetverbindung besteht.",
|
kimi-locale/en.json
CHANGED
@@ -143,7 +143,7 @@
|
|
143 |
"character_summary_bella": "Cheerful, nurturing, sees people as plants needing care",
|
144 |
"character_summary_rosa": "Chaotic, attention-seeking, thrives on controlled chaos",
|
145 |
"character_summary_stella": "Whimsical, artistic, imaginative, playful, transforms chaos into art",
|
146 |
-
"fallback_api_missing": "To really chat with me, add your API key in settings! 💕",
|
147 |
"fallback_api_error": "Sorry, the AI service is temporarily unavailable. Please try again later.",
|
148 |
"fallback_model_error": "Sorry, the selected model is not available. Please choose another model or check your configuration.",
|
149 |
"fallback_network_error": "Sorry, I cannot respond because there is no internet connection.",
|
|
|
143 |
"character_summary_bella": "Cheerful, nurturing, sees people as plants needing care",
|
144 |
"character_summary_rosa": "Chaotic, attention-seeking, thrives on controlled chaos",
|
145 |
"character_summary_stella": "Whimsical, artistic, imaginative, playful, transforms chaos into art",
|
146 |
+
"fallback_api_missing": "To really chat with me, add your OpenRouter API key in settings! 💕",
|
147 |
"fallback_api_error": "Sorry, the AI service is temporarily unavailable. Please try again later.",
|
148 |
"fallback_model_error": "Sorry, the selected model is not available. Please choose another model or check your configuration.",
|
149 |
"fallback_network_error": "Sorry, I cannot respond because there is no internet connection.",
|
kimi-locale/es.json
CHANGED
@@ -143,7 +143,7 @@
|
|
143 |
"character_summary_bella": "Alegre, cariñosa, ve a las personas como plantas que necesitan cuidado",
|
144 |
"character_summary_rosa": "Caótica, busca atención, prospera en el caos controlado",
|
145 |
"character_summary_stella": "Caprichosa, artística, imaginativa, juguetona, transforma el caos en arte",
|
146 |
-
"fallback_api_missing": "Para realmente chatear conmigo, ¡agrega tu clave API de en configuración! 💕",
|
147 |
"fallback_api_error": "Lo siento, el servicio de IA no está disponible temporalmente. Por favor, intenta de nuevo más tarde.",
|
148 |
"fallback_model_error": "Lo siento, el modelo seleccionado no está disponible. Por favor, elige otro modelo o verifica tu configuración.",
|
149 |
"fallback_network_error": "Lo siento, no puedo responder porque no hay conexión a internet.",
|
|
|
143 |
"character_summary_bella": "Alegre, cariñosa, ve a las personas como plantas que necesitan cuidado",
|
144 |
"character_summary_rosa": "Caótica, busca atención, prospera en el caos controlado",
|
145 |
"character_summary_stella": "Caprichosa, artística, imaginativa, juguetona, transforma el caos en arte",
|
146 |
+
"fallback_api_missing": "Para realmente chatear conmigo, ¡agrega tu clave API de OpenRouter en configuración! 💕",
|
147 |
"fallback_api_error": "Lo siento, el servicio de IA no está disponible temporalmente. Por favor, intenta de nuevo más tarde.",
|
148 |
"fallback_model_error": "Lo siento, el modelo seleccionado no está disponible. Por favor, elige otro modelo o verifica tu configuración.",
|
149 |
"fallback_network_error": "Lo siento, no puedo responder porque no hay conexión a internet.",
|
kimi-locale/fr.json
CHANGED
@@ -143,7 +143,7 @@
|
|
143 |
"character_summary_bella": "Joyeuse, bienveillante, voit les gens comme des plantes ayant besoin de soins",
|
144 |
"character_summary_rosa": "Chaotique, en quête d'attention, prospère dans le chaos contrôlé",
|
145 |
"character_summary_stella": "Fantaisiste, artistique, imaginative, joueuse, transforme le chaos en art",
|
146 |
-
"fallback_api_missing": "Pour vraiment discuter avec moi, ajoute ta clé API dans les paramètres ! 💕",
|
147 |
"fallback_api_error": "Désolée, le service IA est temporairement indisponible. Veuillez réessayer plus tard.",
|
148 |
"fallback_model_error": "Désolée, le modèle sélectionné n'est pas disponible. Veuillez choisir un autre modèle ou vérifier votre configuration.",
|
149 |
"fallback_network_error": "Désolée, je ne peux pas répondre car il n'y a pas de connexion internet.",
|
|
|
143 |
"character_summary_bella": "Joyeuse, bienveillante, voit les gens comme des plantes ayant besoin de soins",
|
144 |
"character_summary_rosa": "Chaotique, en quête d'attention, prospère dans le chaos contrôlé",
|
145 |
"character_summary_stella": "Fantaisiste, artistique, imaginative, joueuse, transforme le chaos en art",
|
146 |
+
"fallback_api_missing": "Pour vraiment discuter avec moi, ajoute ta clé API OpenRouter dans les paramètres ! 💕",
|
147 |
"fallback_api_error": "Désolée, le service IA est temporairement indisponible. Veuillez réessayer plus tard.",
|
148 |
"fallback_model_error": "Désolée, le modèle sélectionné n'est pas disponible. Veuillez choisir un autre modèle ou vérifier votre configuration.",
|
149 |
"fallback_network_error": "Désolée, je ne peux pas répondre car il n'y a pas de connexion internet.",
|
kimi-locale/it.json
CHANGED
@@ -143,7 +143,7 @@
|
|
143 |
"character_summary_bella": "Allegra, premurosa, vede le persone come piante che hanno bisogno di cure",
|
144 |
"character_summary_rosa": "Caotica, cerca attenzione, prospera nel caos controllato",
|
145 |
"character_summary_stella": "Capricciosa, artistica, fantasiosa, giocosa, trasforma il caos in arte",
|
146 |
-
"fallback_api_missing": "Per chattare davvero con me, aggiungi la tua chiave API nelle impostazioni! 💕",
|
147 |
"fallback_api_error": "Spiacente, il servizio AI è temporaneamente non disponibile. Riprova più tardi.",
|
148 |
"fallback_model_error": "Spiacente, il modello selezionato non è disponibile. Scegli un altro modello o controlla la tua configurazione.",
|
149 |
"fallback_network_error": "Spiacente, non posso rispondere perché non c'è connessione internet.",
|
|
|
143 |
"character_summary_bella": "Allegra, premurosa, vede le persone come piante che hanno bisogno di cure",
|
144 |
"character_summary_rosa": "Caotica, cerca attenzione, prospera nel caos controllato",
|
145 |
"character_summary_stella": "Capricciosa, artistica, fantasiosa, giocosa, trasforma il caos in arte",
|
146 |
+
"fallback_api_missing": "Per chattare davvero con me, aggiungi la tua chiave API OpenRouter nelle impostazioni! 💕",
|
147 |
"fallback_api_error": "Spiacente, il servizio AI è temporaneamente non disponibile. Riprova più tardi.",
|
148 |
"fallback_model_error": "Spiacente, il modello selezionato non è disponibile. Scegli un altro modello o controlla la tua configurazione.",
|
149 |
"fallback_network_error": "Spiacente, non posso rispondere perché non c'è connessione internet.",
|
kimi-locale/ja.json
CHANGED
@@ -143,7 +143,7 @@
|
|
143 |
"character_summary_bella": "陽気で、優しく、人を世話が必要な植物として見る",
|
144 |
"character_summary_rosa": "混沌的で、注目を求め、制御された混沌で繁栄する",
|
145 |
"character_summary_stella": "気まぐれで、芸術的、想像力豊か、遊び心があり、混沌を芸術に変える",
|
146 |
-
"fallback_api_missing": "本当に私とチャットするには、設定で APIキーを追加してください! 💕",
|
147 |
"fallback_api_error": "申し訳ありませんが、AIサービスが一時的に利用できません。後でもう一度試してください。",
|
148 |
"fallback_model_error": "申し訳ありませんが、選択されたモデルは利用できません。別のモデルを選択するか、設定を確認してください。",
|
149 |
"fallback_network_error": "申し訳ありませんが、インターネット接続がないため応答できません。",
|
|
|
143 |
"character_summary_bella": "陽気で、優しく、人を世話が必要な植物として見る",
|
144 |
"character_summary_rosa": "混沌的で、注目を求め、制御された混沌で繁栄する",
|
145 |
"character_summary_stella": "気まぐれで、芸術的、想像力豊か、遊び心があり、混沌を芸術に変える",
|
146 |
+
"fallback_api_missing": "本当に私とチャットするには、設定でOpenRouter APIキーを追加してください! 💕",
|
147 |
"fallback_api_error": "申し訳ありませんが、AIサービスが一時的に利用できません。後でもう一度試してください。",
|
148 |
"fallback_model_error": "申し訳ありませんが、選択されたモデルは利用できません。別のモデルを選択するか、設定を確認してください。",
|
149 |
"fallback_network_error": "申し訳ありませんが、インターネット接続がないため応答できません。",
|
kimi-locale/zh.json
CHANGED
@@ -143,7 +143,7 @@
|
|
143 |
"character_summary_bella": "开朗、体贴,将人视为需要关爱的植物",
|
144 |
"character_summary_rosa": "混乱、寻求关注,在受控混乱中蓬勃发展",
|
145 |
"character_summary_stella": "古怪、艺术、富有想象力、顽皮,将混乱转化为艺术",
|
146 |
-
"fallback_api_missing": "要真正与我聊天,请在设置中添加您的 API密钥! 💕",
|
147 |
"fallback_api_error": "抱歉,AI服务暂时不可用。请稍后再试。",
|
148 |
"fallback_model_error": "抱歉,所选模型不可用。请选择其他模型或检查您的配置。",
|
149 |
"fallback_network_error": "抱歉,由于没有互联网连接,我无法回应。",
|
|
|
143 |
"character_summary_bella": "开朗、体贴,将人视为需要关爱的植物",
|
144 |
"character_summary_rosa": "混乱、寻求关注,在受控混乱中蓬勃发展",
|
145 |
"character_summary_stella": "古怪、艺术、富有想象力、顽皮,将混乱转化为艺术",
|
146 |
+
"fallback_api_missing": "要真正与我聊天,请在设置中添加您的OpenRouter API密钥! 💕",
|
147 |
"fallback_api_error": "抱歉,AI服务暂时不可用。请稍后再试。",
|
148 |
"fallback_model_error": "抱歉,所选模型不可用。请选择其他模型或检查您的配置。",
|
149 |
"fallback_network_error": "抱歉,由于没有互联网连接,我无法回应。",
|