VirtualKimi commited on
Commit
56ab489
·
verified ·
1 Parent(s): add7bfd

Upload 32 files

Browse files
CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
  # Virtual Kimi Changelog
2
 
 
 
 
 
 
 
 
 
 
 
3
  ## [1.0.7] - 2025-08-19
4
 
5
  ### Changed
 
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
index.html CHANGED
@@ -55,7 +55,7 @@
55
  },
56
  "dateCreated": "2025-07-16",
57
  "dateModified": "2025-08-19",
58
- "version": "v1.0.7"
59
  }
60
  </script>
61
 
@@ -397,8 +397,7 @@
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,11 +407,11 @@
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>
@@ -1014,7 +1013,8 @@
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,7 +1023,6 @@
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,7 +1054,7 @@
1055
  "name": "Jean & Kimi"
1056
  },
1057
  "dateCreated": "2025-07-19",
1058
- "version": "v1.0.7"
1059
  }
1060
  }
1061
  </script>
 
55
  },
56
  "dateCreated": "2025-07-16",
57
  "dateModified": "2025-08-19",
58
+ "version": "v1.0.8"
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="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
  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>
 
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
  <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
  "name": "Jean & Kimi"
1055
  },
1056
  "dateCreated": "2025-07-19",
1057
+ "version": "v1.0.8"
1058
  }
1059
  }
1060
  </script>
kimi-js/kimi-constants.js CHANGED
@@ -2,88 +2,212 @@
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,16 +709,28 @@ window.KIMI_PERSONALITY_KEYWORDS = {
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,10 +850,16 @@ window.KIMI_COMMON_WORDS = {
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,42 +882,42 @@ window.KIMI_EMOTION_SENSITIVITY = {
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,16 +1112,7 @@ window.getLocalizedEmotionalResponse = function (type, index = null) {
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 (
 
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
  }
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
  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
  },
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
  : "";
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 (
kimi-js/kimi-database.js CHANGED
@@ -126,14 +126,7 @@ 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: "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,7 +358,7 @@ class KimiDatabase {
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");
 
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
  }
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");
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("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,7 +576,7 @@ class KimiLLMManager {
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
  }
@@ -975,45 +975,15 @@ class KimiLLMManager {
975
  }
976
 
977
  async testModel(modelId, testMessage = "Test API ok?") {
978
- const originalModel = this.currentModel;
979
- try {
980
- await this.setCurrentModel(modelId);
981
- const response = await this.chat(testMessage, { maxTokens: 2 });
982
- return { success: true, response: response };
983
- } catch (error) {
984
- return { success: false, error: error.message };
985
- } finally {
986
- await this.setCurrentModel(originalModel);
987
- }
988
- }
989
-
990
- // Complete model diagnosis
991
- async diagnoseModel(modelId) {
992
- const model = this.availableModels[modelId];
993
- if (!model) {
994
- return {
995
- available: false,
996
- error: "Model not found in local list"
997
- };
998
- }
999
-
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,
1007
- error: `Unable to check: ${error.message}`
1008
- };
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) {
@@ -1062,7 +1032,7 @@ class KimiLLMManager {
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") {
@@ -1102,12 +1072,38 @@ class KimiLLMManager {
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: {
 
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
  }
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
  }
 
975
  }
976
 
977
  async testModel(modelId, testMessage = "Test API ok?") {
978
+ // Ancienne méthode de test (non minimaliste)
979
+ return await this.testApiKeyMinimal(modelId);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
980
  }
981
 
982
  /**
983
+ * Test API minimaliste et centralisé pour tous les providers compatibles.
984
+ * Envoie uniquement un prompt système court et un message utilisateur dans la langue choisie.
985
+ * Aucun contexte, aucune mémoire, aucun paramètre superflu.
986
+ * @param {string} modelId - ID du modèle à tester
987
  * @returns {Promise<{success: boolean, response?: string, error?: string}>}
988
  */
989
  async testApiKeyMinimal(modelId) {
 
1032
  headers["Authorization"] = `Bearer ${apiKey}`;
1033
  headers["HTTP-Referer"] = window.location.origin;
1034
  headers["X-Title"] = "Kimi - Virtual Companion";
1035
+ } else if (["openai", "groq", "together", "deepseek", "openai-compatible"].includes(provider)) {
1036
  baseUrl = await this.db.getPreference("llmBaseUrl", "https://api.openai.com/v1/chat/completions");
1037
  headers["Authorization"] = `Bearer ${apiKey}`;
1038
  } else if (provider === "ollama") {
 
1072
  }
1073
  }
1074
 
1075
+ // Complete model diagnosis
1076
+ async diagnoseModel(modelId) {
1077
+ const model = this.availableModels[modelId];
1078
+ if (!model) {
1079
+ return {
1080
+ available: false,
1081
+ error: "Model not found in local list"
1082
+ };
1083
+ }
1084
+
1085
+ // Check availability on OpenRouter
1086
+ try {
1087
+ // getAvailableModelsFromAPI removed
1088
+ return {
1089
+ available: true,
1090
+ model: model,
1091
+ pricing: model.pricing
1092
+ };
1093
+ } catch (error) {
1094
+ return {
1095
+ available: false,
1096
+ error: `Unable to check: ${error.message}`
1097
+ };
1098
+ }
1099
+ }
1100
+
1101
  // Fetch models from OpenRouter API and merge into availableModels
1102
  async refreshRemoteModels() {
1103
  if (this._isRefreshingModels) return;
1104
  this._isRefreshingModels = true;
1105
  try {
1106
+ const apiKey = await this.db.getPreference("providerApiKey", "");
1107
  const res = await fetch("https://openrouter.ai/api/v1/models", {
1108
  method: "GET",
1109
  headers: {
kimi-js/kimi-memory-system.js CHANGED
@@ -840,7 +840,8 @@ 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
- return Math.min(1.0, importance);
 
844
  }
845
 
846
  // 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
+ // 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
kimi-js/kimi-module.js CHANGED
@@ -747,11 +747,10 @@ async function loadSettingsData() {
747
  "voicePitch",
748
  "voiceVolume",
749
  "selectedLanguage",
750
- "openrouterApiKey",
751
  "llmProvider",
752
  "llmBaseUrl",
753
  "llmModelId",
754
- "llmApiKey",
755
  "selectedCharacter",
756
  "llmTemperature",
757
  "llmMaxTokens",
@@ -766,11 +765,10 @@ async function loadSettingsData() {
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,10 +1339,17 @@ async function sendMessage() {
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,7 +1616,7 @@ function setupSettingsListeners(kimiDB, kimiMemory) {
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
  }
 
747
  "voicePitch",
748
  "voiceVolume",
749
  "selectedLanguage",
750
+ "providerApiKey",
751
  "llmProvider",
752
  "llmBaseUrl",
753
  "llmModelId",
 
754
  "selectedCharacter",
755
  "llmTemperature",
756
  "llmMaxTokens",
 
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
 
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
  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
  }
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("openrouter-api-key"),
84
  toggleBtn: () => document.getElementById("toggle-api-key"),
85
  providerSelect: () => document.getElementById("llm-provider"),
86
  baseUrlInput: () => document.getElementById("llm-base-url"),
@@ -150,7 +150,9 @@ document.addEventListener("DOMContentLoaded", async function () {
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,7 +243,18 @@ document.addEventListener("DOMContentLoaded", async function () {
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,9 +266,11 @@ document.addEventListener("DOMContentLoaded", async function () {
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,7 +288,13 @@ document.addEventListener("DOMContentLoaded", async function () {
273
  : "API Key";
274
  }
275
  const savedBadge = ApiUi.savedBadge();
276
- if (savedBadge) savedBadge.style.display = "none";
 
 
 
 
 
 
277
  ApiUi.clearStatus();
278
  }
279
  });
@@ -362,6 +383,10 @@ document.addEventListener("DOMContentLoaded", async function () {
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,6 +403,10 @@ document.addEventListener("DOMContentLoaded", async function () {
378
  settingsPanel.scrollTop = scrollTop;
379
  });
380
  }
 
 
 
 
381
  saveCharacterBtn.setAttribute("data-i18n", "saved");
382
  saveCharacterBtn.classList.add("success");
383
  saveCharacterBtn.disabled = true;
@@ -557,19 +586,59 @@ document.addEventListener("DOMContentLoaded", async function () {
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,16 +651,21 @@ document.addEventListener("DOMContentLoaded", async function () {
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,6 +677,10 @@ document.addEventListener("DOMContentLoaded", async function () {
603
  statusSpan.textContent = `Error: ${error.message}`;
604
  statusSpan.style.color = "#ff6b6b";
605
  ApiUi.setTestPresence("#9e9e9e");
 
 
 
 
606
  }
607
  });
608
  }
@@ -617,7 +695,9 @@ document.addEventListener("DOMContentLoaded", async function () {
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));
 
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
  }
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
  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
  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
  : "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
 
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
  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
  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
+ // Test API minimal et centralisé pour tous les providers
637
  const result = await window.kimiLLM.testApiKeyMinimal(modelId);
638
  if (result.success) {
639
  statusSpan.textContent = "Connection successful!";
640
  statusSpan.style.color = "#4caf50";
641
+ // Only show saved badge if an actual non-empty API key is stored and provider requires one
642
  const savedBadge = ApiUi.savedBadge();
643
  if (savedBadge) {
644
  const apiKeyInputEl = ApiUi.apiKeyInput();
 
651
  savedBadge.style.display = "none";
652
  }
653
  }
654
+
655
  if (result.response) {
656
  setTimeout(() => {
657
  statusSpan.textContent = `Test response: \"${result.response.substring(0, 50)}...\"`;
658
  }, 1000);
659
  }
660
+ // Mark test success explicitly
661
  ApiUi.setTestPresence("#4caf50");
662
  } else {
663
  statusSpan.textContent = `${result.error}`;
664
  statusSpan.style.color = "#ff6b6b";
665
  ApiUi.setTestPresence("#9e9e9e");
666
+ if (result.error.includes("similaires disponibles")) {
667
+ setTimeout(() => {}, 1000);
668
+ }
669
  }
670
  } else {
671
  statusSpan.textContent = "LLM manager not initialized";
 
677
  statusSpan.textContent = `Error: ${error.message}`;
678
  statusSpan.style.color = "#ff6b6b";
679
  ApiUi.setTestPresence("#9e9e9e");
680
+
681
+ if (error.message.includes("non disponible")) {
682
+ setTimeout(() => {}, 1000);
683
+ }
684
  }
685
  });
686
  }
 
695
  t = setTimeout(async () => {
696
  const providerEl = ApiUi.providerSelect();
697
  const provider = providerEl ? providerEl.value : "openrouter";
698
+ const keyPref = window.KimiProviderUtils
699
+ ? window.KimiProviderUtils.getKeyPrefForProvider(provider)
700
+ : "providerApiKey";
701
  const value = input.value.trim();
702
  // Update Test button state immediately
703
  const validNow = !!(window.KIMI_VALIDATORS && window.KIMI_VALIDATORS.validateApiKey(value));
kimi-js/kimi-utils.js CHANGED
@@ -43,26 +43,14 @@ window.KimiValidationUtils = {
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,8 +201,6 @@ class KimiSecurityUtils {
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,14 +370,7 @@ class KimiVideoManager {
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,12 +418,7 @@ class KimiVideoManager {
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,11 +433,7 @@ class KimiVideoManager {
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,7 +961,6 @@ class KimiVideoManager {
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,7 +1181,7 @@ class KimiVideoManager {
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,7 +1218,6 @@ class KimiVideoManager {
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") {
 
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
  }
202
  return key.trim().length > 10 && (key.startsWith("sk-") || key.startsWith("sk-or-"));
203
  }
 
 
204
  }
205
 
206
  // Cache management for better performance
 
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
  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
  return video;
434
  }
435
 
436
+ //Centralized video selection utility.
 
 
 
 
437
  static getVideoElement(selector) {
438
  if (typeof selector === "string") {
439
  if (selector.startsWith("#")) {
 
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
  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
  }
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") {
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 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.",
 
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.",
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 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.",
 
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.",
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 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.",
 
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.",
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 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.",
 
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.",
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 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.",
 
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.",
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": "本当に私とチャットするには、設定でOpenRouter 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": "本当に私とチャットするには、設定で 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": "要真正与我聊天,请在设置中添加您的OpenRouter 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": "要真正与我聊天,请在设置中添加您的 API密钥! 💕",
147
  "fallback_api_error": "抱歉,AI服务暂时不可用。请稍后再试。",
148
  "fallback_model_error": "抱歉,所选模型不可用。请选择其他模型或检查您的配置。",
149
  "fallback_network_error": "抱歉,由于没有互联网连接,我无法回应。",