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