VirtualKimi commited on
Commit
add7bfd
·
verified ·
1 Parent(s): 249acdb

Upload 32 files

Browse files
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.8"
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="api_key_label">API Key</label>
 
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="provider-api-key" name="provider_api_key"
411
- placeholder="API Key..." autocomplete="new-password" autocapitalize="none"
412
- autocorrect="off" spellcheck="false" inputmode="text" aria-autocomplete="none"
413
- data-lpignore="true" data-1p-ignore="true" data-bwignore="true"
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="1000"
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.8"
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: 1000 },
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
- "wow",
7
- "oh",
8
- "surprise",
9
- "incredible",
10
- "amazing",
11
- "unbelievable",
12
- "no way",
13
- "really?",
14
- "whoa",
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", "épatant", "stupéfiant", "vraiment?", "oh là là"],
111
- laughing: ["haha", "mdr", "rire", "drôle", "hilarant", "mort de rire", "ptdr", "rigole", "sourit", "tu plaisantes"],
112
- shy: ["timide", "gêné", "rougir", "honteux", "intimidé", "mal à l’aise", "réservé", "introverti", "timidité"],
113
- confident: ["confiance", "fier", "sûr", "fort", "déterminé", "assuré", "audacieux", "leader", "sans peur", "affirmé"],
114
- romantic: ["amour", "romantique", "tendre", "câlin", "bisou", "mon cœur", "chéri", "ma belle", "passionné", "adoré"],
115
- flirtatious: [
116
- "flirt",
117
- "taquin",
118
- "séduire",
119
- "charme",
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", "de verdad?", "vaya", "sorprendente"],
145
- laughing: ["jaja", "lol", "reír", "gracioso", "divertido", "carcajada", "sonrisa", "te ríes", "broma", "estás de broma"],
146
- shy: ["tímido", "avergonzado", "sonrojar", "tímida", "intimidado", "reservado", "introvertido", "tímidez", "nervioso"],
147
- confident: ["confianza", "orgulloso", "seguro", "fuerte", "determinado", "seguro de sí", "valiente", "líder", "atrevido"],
148
- romantic: ["amor", "romántico", "tierno", "abrazo", "beso", "mi amor", "cariño", "apasionado", "querido", "corazón"],
149
- flirtatious: ["coqueto", "provocar", "seducir", "encanto", "flirtear", "guiño", "coqueto", "seductor", "pícaro"],
150
- goodbye: ["adiós", "bye", "hasta pronto", "ciao", "hasta luego", "cuídate", "nos vemos", "hasta la próxima"],
151
- kiss: ["beso", "besos", "abrazar", "besito", "abrazo", "besote"],
152
- dancing: ["bailar", "baile", "mover", "ritmo", "paso", "girar", "moverse", "sacudir"],
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", "wirklich?", "überrascht", "staunend"],
169
- laughing: ["haha", "lol", "lachen", "lustig", "witzig", "kicher", "grinsen", "du machst Witze"],
170
- shy: ["schüchtern", "verlegen", "erröten", "beschämt", "eingeschüchtert", "zurückhaltend", "nervös", "schüchternheit"],
171
- confident: ["vertrauen", "stolz", "sicher", "stark", "entschlossen", "selbstbewusst", "mutig", "führer"],
172
- romantic: ["liebe", "romantisch", "zärtlich", "umarmung", "kuss", "mein Schatz", "Liebling", "leidenschaftlich", "Herz"],
173
- flirtatious: ["flirten", "necken", "verführen", "charme", "flirt", "zwinkern", "frech", "verführerisch"],
174
- goodbye: ["auf wiedersehen", "bye", "bis bald", "ciao", "bis später", "pass auf dich auf", "bis dann", "tschüss"],
175
- kiss: ["kuss", "küsse", "umarmen", "Küsschen", "Schmatzer"],
176
- dancing: ["tanzen", "tanz", "bewegen", "groove", "schritt", "drehen", "schwingen"],
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", "davvero?", "sbalorditivo", "sorpreso"],
192
- laughing: ["haha", "lol", "ridere", "divertente", "esilarante", "sorriso", "ridacchiare", "stai scherzando"],
193
- shy: [
194
- "timido",
195
- "imbarazzato",
196
- "arrossire",
197
- "vergognoso",
198
- "intimidito",
199
- "riservato",
200
- "introverso",
201
- "timidezza",
202
- "imbarazzo"
203
- ],
204
- confident: ["fiducia", "orgoglioso", "sicuro", "forte", "determinato", "sicuro di sé", "coraggioso", "leader", "audace"],
205
- romantic: ["amore", "romantico", "tenero", "abbraccio", "bacio", "amore mio", "tesoro", "appassionato", "cuore"],
206
- flirtatious: ["civettare", "provocare", "sedurre", "fascino", "flirtare", "occhiolino", "malizioso", "seducente"],
207
- goodbye: ["arrivederci", "bye", "a presto", "ciao", "abbi cura di te", "a dopo", "ciao ciao"],
208
- kiss: ["bacio", "baci", "abbracciare", "bacino", "abbraccio", "baciotto"],
209
- dancing: ["ballare", "girare", "muoversi", "scuotere"],
210
- listening: ["ascoltami", "fai attenzione", "presta orecchio", "concentrati", "ascolta", "parla", "domanda", "dimmi"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  }
212
  };
213
 
@@ -709,28 +585,16 @@ window.KIMI_PERSONALITY_KEYWORDS = {
709
  }
710
  },
711
  ja: {
712
- surprise: ["わお", "おお", "驚き", "信じられない", "すごい"],
713
- laughing: ["はは", "", "笑う", "面白い", "愉快"],
714
- shy: ["恥ずかしい", "照れる", "赤面", "内気", "遠慮"],
715
- confident: ["自信", "誇り", "確信", "強い", "決意"],
716
- romantic: ["愛", "ロマンチック", "優しい", "抱擁", "キス", "愛しい"],
717
- flirtatious: ["いちゃつく", "からかう", "誘惑", "魅力", "フリート"],
718
- goodbye: ["さようなら", "バイバイ", "また今度", "チャオ", "またね"],
719
- kiss: ["キス", "抱擁", "チュー"],
720
- dancing: ["踊る", "ダンス", "動く", "グルーブ", "ステップ"],
721
- listening: ["聞いて", "聞く", "聞いてください", "話して", "話す", "質問", "尋ねる", "教えて"]
722
  },
723
  zh: {
724
- surprise: ["哇", "哦", "惊喜", "难以置信", "惊人"],
725
- laughing: ["哈哈", "", "大笑", "有趣", "搞笑"],
726
- shy: ["害羞", "尴尬", "脸红", "羞涩", "胆怯"],
727
- confident: ["自信", "骄傲", "确信", "强壮", "坚定"],
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 set = window.KIMI_COMMON_WORDS_SET[language] || window.KIMI_COMMON_WORDS_SET.en;
862
- return set.has(word.toLowerCase());
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, laughing: 1.0 },
886
- it: { romantic: 1.2, laughing: 0.9 },
887
- de: { romantic: 1.0, laughing: 1.0 },
888
- en: { romantic: 1.0, laughing: 1.0 },
889
- ja: { romantic: 1.0, laughing: 1.0 },
890
- zh: { romantic: 1.0, laughing: 1.0 }
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.2,
897
- globalLoss: 0.8,
898
  // Per-emotion gain scaling (keys must match KimiEmotionSystem.EMOTIONS values)
899
  emotionGain: {
900
- positive: 1.1,
901
- negative: 0.9,
902
- romantic: 1.3,
903
- laughing: 1.15,
904
- dancing: 1.05,
905
- shy: 0.95,
906
- confident: 1.1,
907
- flirtatious: 1.2
908
  },
909
  // Per-trait scaling
910
  traitGain: {
911
- affection: 1.2,
912
- romance: 1.3,
913
- empathy: 1.1,
914
- playfulness: 1.15,
915
- humor: 1.1,
916
- intelligence: 1.05
917
  },
918
  traitLoss: {
919
- affection: 0.9,
920
- romance: 0.9,
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 count = window.KIMI_EMOTIONAL_RESPONSES[type]?.length || 1;
 
 
 
 
 
 
 
 
 
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: "providerApiKey", value: "" }
 
 
 
 
 
 
 
130
  ];
131
  }
132
 
@@ -358,7 +365,7 @@ class KimiDatabase {
358
  }
359
 
360
  async setPreference(key, value) {
361
- if (key === "providerApiKey") {
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("providerApiKey", "");
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("providerApiKey");
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("providerApiKey", "");
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
- // Round to two decimals to avoid floating point artifacts
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
- "providerApiKey",
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.providerApiKey || "";
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", finalResponse);
1351
  if (window.voiceManager && !message.startsWith("Vous:")) {
1352
- window.voiceManager.speak(finalResponse);
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
- // Removed plugin reload for strict isolation
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("provider-api-key"),
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
- ? window.KimiProviderUtils.getKeyPrefForProvider(provider)
271
- : "providerApiKey";
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 baseUrl = baseUrlInput ? baseUrlInput.value.trim() : "";
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
- let result;
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: 32000, 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,14 +43,26 @@ window.KimiValidationUtils = {
43
 
44
  // Provider utilities used across the app
45
  const KimiProviderUtils = {
 
 
 
 
 
 
 
 
 
 
46
  getKeyPrefForProvider(provider) {
47
- // Centralized: always use 'providerApiKey' for all providers except Ollama
48
- return provider === "ollama" ? null : "providerApiKey";
49
  },
50
  async getApiKey(db, provider) {
51
  if (!db) return null;
52
  if (provider === "ollama") return "__local__";
53
- return await db.getPreference("providerApiKey");
 
 
 
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
- //Centralized crossfade transition between two videos.
 
 
 
 
 
 
 
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
- //Centralized video element creation utility.
 
 
 
 
 
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
- //Centralized video selection utility.
 
 
 
 
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
- // Si la voix est encore en cours, relancer une vidéo neutre en boucle
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": "抱歉,由于没有互联网连接,我无法回应。",