Spaces:
Running
Running
Upload 32 files
Browse files- CHANGELOG.md +10 -0
- index.html +10 -11
- kimi-js/kimi-constants.js +251 -118
- kimi-js/kimi-database.js +2 -9
- kimi-js/kimi-llm-manager.js +36 -40
- kimi-js/kimi-memory-system.js +2 -1
- kimi-js/kimi-module.js +12 -7
- kimi-js/kimi-script.js +90 -10
- kimi-js/kimi-utils.js +7 -39
- kimi-locale/de.json +1 -1
- kimi-locale/en.json +1 -1
- kimi-locale/es.json +1 -1
- kimi-locale/fr.json +1 -1
- kimi-locale/it.json +1 -1
- kimi-locale/ja.json +1 -1
- kimi-locale/zh.json +1 -1
CHANGELOG.md
CHANGED
@@ -1,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.
|
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="
|
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="
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
data-
|
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="
|
|
|
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.
|
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: [
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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", "
|
22 |
-
flirtatious: [
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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: [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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", "
|
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", "
|
49 |
-
dancing: ["tanzen", "tanz", "bewegen", "groove", "schritt"],
|
50 |
-
listening: [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
},
|
52 |
it: {
|
53 |
-
surprise: ["wow", "oh", "sorpresa", "incredibile", "stupefacente"],
|
54 |
-
laughing: ["haha", "lol", "ridere", "divertente", "esilarante"],
|
55 |
-
shy: [
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
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 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
592 |
},
|
593 |
zh: {
|
594 |
-
|
595 |
-
|
596 |
-
|
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
|
720 |
-
return
|
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 |
-
|
745 |
-
de: {},
|
746 |
-
|
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.
|
755 |
-
globalLoss:
|
756 |
// Per-emotion gain scaling (keys must match KimiEmotionSystem.EMOTIONS values)
|
757 |
emotionGain: {
|
758 |
-
positive: 1.
|
759 |
-
negative:
|
760 |
-
romantic: 1.
|
761 |
-
laughing: 1.
|
762 |
-
dancing: 1.
|
763 |
-
shy:
|
764 |
-
confident: 1.
|
765 |
-
flirtatious: 1.
|
766 |
},
|
767 |
// Per-trait scaling
|
768 |
traitGain: {
|
769 |
-
affection: 1.
|
770 |
-
romance: 1.
|
771 |
-
empathy: 1.
|
772 |
-
playfulness: 1.
|
773 |
-
humor: 1.
|
774 |
-
intelligence: 1.
|
775 |
},
|
776 |
traitLoss: {
|
777 |
-
affection:
|
778 |
-
romance:
|
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
|
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: "
|
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 === "
|
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("
|
478 |
const modelId = await this.db.getPreference("llmModelId", this.currentModel || "gpt-4o-mini");
|
479 |
if (!apiKey) {
|
480 |
throw new Error("API key not configured for selected provider");
|
@@ -576,7 +576,7 @@ class KimiLLMManager {
|
|
576 |
}
|
577 |
|
578 |
async chatWithOpenRouter(userMessage, options = {}) {
|
579 |
-
const apiKey = await this.db.getPreference("
|
580 |
if (!apiKey) {
|
581 |
throw new Error("OpenRouter API key not configured");
|
582 |
}
|
@@ -975,45 +975,15 @@ class KimiLLMManager {
|
|
975 |
}
|
976 |
|
977 |
async testModel(modelId, testMessage = "Test API ok?") {
|
978 |
-
|
979 |
-
|
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 |
-
*
|
1014 |
-
*
|
1015 |
-
*
|
1016 |
-
* @param {string}
|
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("
|
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 |
-
|
|
|
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 |
-
"
|
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.
|
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",
|
1346 |
if (window.voiceManager && !message.startsWith("Vous:")) {
|
1347 |
-
window.voiceManager.speak(
|
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 |
-
|
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("
|
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
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
257 |
-
|
258 |
-
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
562 |
const modelIdInput = ApiUi.modelIdInput();
|
563 |
-
const
|
|
|
|
|
|
|
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
|
|
|
|
|
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 |
-
|
|
|
58 |
},
|
59 |
async getApiKey(db, provider) {
|
60 |
if (!db) return null;
|
61 |
if (provider === "ollama") return "__local__";
|
62 |
-
|
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 |
-
//
|
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
|
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
|
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
|
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
|
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
|
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": "本当に私とチャットするには、設定で
|
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": "要真正与我聊天,请在设置中添加您的
|
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": "抱歉,由于没有互联网连接,我无法回应。",
|