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": "抱歉,由于没有互联网连接,我无法回应。",
|