Spaces:
Running
Running
Upload 22 files
Browse files- CHANGELOG.md +12 -0
- index.html +3 -3
- kimi-js/kimi-constants.js +116 -11
- kimi-js/kimi-database.js +31 -1
- kimi-js/kimi-emotion-system.js +3 -2
- kimi-js/kimi-module.js +49 -1
- kimi-js/kimi-script.js +112 -47
CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
| 1 |
# Virtual Kimi Changelog
|
| 2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
# [1.1.2] - 2025-08-30
|
| 4 |
|
| 5 |
### Improvements
|
|
|
|
| 1 |
# Virtual Kimi Changelog
|
| 2 |
|
| 3 |
+
# [1.1.3] - 2025-08-30
|
| 4 |
+
|
| 5 |
+
### Improvements
|
| 6 |
+
|
| 7 |
+
- Refined personality trait handling and synchronization per character for more consistent and predictable behavior across modules (emotion, video, voice, memory).
|
| 8 |
+
|
| 9 |
+
### Fixed / Improved
|
| 10 |
+
|
| 11 |
+
- Improved conversation deletion logic: deleting chat now only affects the selected character and prevents stale asynchronous saves from re-adding removed messages.
|
| 12 |
+
- Added a lightweight BroadcastChannel announcement so clears propagate to other open tabs, avoiding cross-tab resurrection of deleted conversations.
|
| 13 |
+
- Centralized conversation clearing logic in the database layer to ensure consistent behavior (use `clearConversations()` across imports and operations).
|
| 14 |
+
|
| 15 |
# [1.1.2] - 2025-08-30
|
| 16 |
|
| 17 |
### Improvements
|
index.html
CHANGED
|
@@ -57,7 +57,7 @@
|
|
| 57 |
},
|
| 58 |
"dateCreated": "2025-07-16",
|
| 59 |
"dateModified": "2025-08-30",
|
| 60 |
-
"version": "v1.1.
|
| 61 |
}
|
| 62 |
</script>
|
| 63 |
|
|
@@ -1072,7 +1072,7 @@
|
|
| 1072 |
<h3><i class="fas fa-code"></i> Technical Information</h3>
|
| 1073 |
<div class="tech-info">
|
| 1074 |
<p><strong>Created date :</strong> July 16, 2025</p>
|
| 1075 |
-
<p><strong>Version :</strong> v1.1.
|
| 1076 |
<p><strong>Last update :</strong> August 30, 2025</p>
|
| 1077 |
<p><strong>Technologies :</strong> HTML5, CSS3, JavaScript ES6+, IndexedDB, Web Speech
|
| 1078 |
API</p>
|
|
@@ -1127,7 +1127,7 @@
|
|
| 1127 |
"name": "Jean & Kimi"
|
| 1128 |
},
|
| 1129 |
"dateCreated": "2025-07-16",
|
| 1130 |
-
"version": "v1.1.
|
| 1131 |
}
|
| 1132 |
}
|
| 1133 |
</script>
|
|
|
|
| 57 |
},
|
| 58 |
"dateCreated": "2025-07-16",
|
| 59 |
"dateModified": "2025-08-30",
|
| 60 |
+
"version": "v1.1.3"
|
| 61 |
}
|
| 62 |
</script>
|
| 63 |
|
|
|
|
| 1072 |
<h3><i class="fas fa-code"></i> Technical Information</h3>
|
| 1073 |
<div class="tech-info">
|
| 1074 |
<p><strong>Created date :</strong> July 16, 2025</p>
|
| 1075 |
+
<p><strong>Version :</strong> v1.1.3</p>
|
| 1076 |
<p><strong>Last update :</strong> August 30, 2025</p>
|
| 1077 |
<p><strong>Technologies :</strong> HTML5, CSS3, JavaScript ES6+, IndexedDB, Web Speech
|
| 1078 |
API</p>
|
|
|
|
| 1127 |
"name": "Jean & Kimi"
|
| 1128 |
},
|
| 1129 |
"dateCreated": "2025-07-16",
|
| 1130 |
+
"version": "v1.1.3"
|
| 1131 |
}
|
| 1132 |
}
|
| 1133 |
</script>
|
kimi-js/kimi-constants.js
CHANGED
|
@@ -344,11 +344,36 @@ window.KIMI_PERSONALITY_KEYWORDS = {
|
|
| 344 |
negative: ["stupid", "dumb", "foolish", "slow", "naive", "ignorant", "simple"]
|
| 345 |
},
|
| 346 |
romance: {
|
| 347 |
-
positive: [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 348 |
negative: ["cold", "distant", "indifferent", "rejection", "loneliness", "breakup", "sad"]
|
| 349 |
},
|
| 350 |
affection: {
|
| 351 |
-
positive: [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
negative: [
|
| 353 |
"mean",
|
| 354 |
"cold",
|
|
@@ -410,7 +435,21 @@ window.KIMI_PERSONALITY_KEYWORDS = {
|
|
| 410 |
]
|
| 411 |
},
|
| 412 |
romance: {
|
| 413 |
-
positive: [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 414 |
negative: [
|
| 415 |
"froid",
|
| 416 |
"froide",
|
|
@@ -434,6 +473,11 @@ window.KIMI_PERSONALITY_KEYWORDS = {
|
|
| 434 |
"attentionné",
|
| 435 |
"câlin",
|
| 436 |
"aimer",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 437 |
"adorer",
|
| 438 |
"adorable"
|
| 439 |
],
|
|
@@ -521,11 +565,35 @@ window.KIMI_PERSONALITY_KEYWORDS = {
|
|
| 521 |
]
|
| 522 |
},
|
| 523 |
romance: {
|
| 524 |
-
positive: [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 525 |
negative: ["frío", "fría", "distante", "indiferente", "rechazo", "soledad", "ruptura", "triste"]
|
| 526 |
},
|
| 527 |
affection: {
|
| 528 |
-
positive: [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 529 |
negative: [
|
| 530 |
"malo",
|
| 531 |
"mala",
|
|
@@ -595,7 +663,8 @@ window.KIMI_PERSONALITY_KEYWORDS = {
|
|
| 595 |
"leidenschaft",
|
| 596 |
"charmant",
|
| 597 |
"liebenswert",
|
| 598 |
-
"süß"
|
|
|
|
| 599 |
],
|
| 600 |
negative: [
|
| 601 |
"kalt",
|
|
@@ -612,7 +681,18 @@ window.KIMI_PERSONALITY_KEYWORDS = {
|
|
| 612 |
]
|
| 613 |
},
|
| 614 |
affection: {
|
| 615 |
-
positive: [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 616 |
negative: [
|
| 617 |
"gemein",
|
| 618 |
"gemeine",
|
|
@@ -674,11 +754,33 @@ window.KIMI_PERSONALITY_KEYWORDS = {
|
|
| 674 |
negative: ["stupido", "stupida", "sciocco", "sciocca", "lento", "lenta", "ingenuo", "ingenua", "ignorante"]
|
| 675 |
},
|
| 676 |
romance: {
|
| 677 |
-
positive: [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 678 |
negative: ["freddo", "fredda", "distante", "indifferente", "rifiuto", "solitudine", "rottura", "triste"]
|
| 679 |
},
|
| 680 |
affection: {
|
| 681 |
-
positive: [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 682 |
negative: [
|
| 683 |
"cattivo",
|
| 684 |
"cattiva",
|
|
@@ -722,7 +824,7 @@ window.KIMI_PERSONALITY_KEYWORDS = {
|
|
| 722 |
laughing: ["はは", "笑", "笑う", "面白い", "愉快"],
|
| 723 |
shy: ["恥ずかしい", "照れる", "赤面", "内気", "遠慮"],
|
| 724 |
confident: ["自信", "誇り", "確信", "強い", "決意"],
|
| 725 |
-
romantic: ["愛", "ロマンチック", "優しい", "抱擁", "キス", "愛しい"],
|
| 726 |
flirtatious: ["いちゃつく", "からかう", "誘惑", "魅力", "フリート"],
|
| 727 |
goodbye: ["さようなら", "バイバイ", "また今度", "チャオ", "またね"],
|
| 728 |
kiss: ["キス", "抱擁", "チュー"],
|
|
@@ -734,7 +836,7 @@ window.KIMI_PERSONALITY_KEYWORDS = {
|
|
| 734 |
laughing: ["哈哈", "笑", "大笑", "有趣", "搞笑"],
|
| 735 |
shy: ["害羞", "尴尬", "脸红", "羞涩", "胆怯"],
|
| 736 |
confident: ["自信", "骄傲", "确信", "强壮", "坚定"],
|
| 737 |
-
romantic: ["爱", "浪漫", "温柔", "拥抱", "吻", "亲爱的"],
|
| 738 |
flirtatious: ["调情", "挑逗", "诱惑", "魅力", "撒娇"],
|
| 739 |
goodbye: ["再见", "拜拜", "回头见", "拜", "下次见"],
|
| 740 |
kiss: ["吻", "亲吻", "拥抱", "亲"],
|
|
@@ -808,6 +910,9 @@ window.KIMI_NEGATORS = window.KIMI_NEGATORS || {
|
|
| 808 |
window.KIMI_NEGATION_WINDOW = window.KIMI_NEGATION_WINDOW || 3; // tokens to look back for negation
|
| 809 |
window.KIMI_SMOOTHING_ALPHA = window.KIMI_SMOOTHING_ALPHA || 0.3;
|
| 810 |
window.KIMI_PERSIST_THRESHOLD = window.KIMI_PERSIST_THRESHOLD || 0.1; // absolute percent (slightly higher to slow small visible jumps)
|
|
|
|
|
|
|
|
|
|
| 811 |
|
| 812 |
// Memory system knobs
|
| 813 |
window.KIMI_MAX_MEMORIES = window.KIMI_MAX_MEMORIES || 100; // default max memory entries per character
|
|
|
|
| 344 |
negative: ["stupid", "dumb", "foolish", "slow", "naive", "ignorant", "simple"]
|
| 345 |
},
|
| 346 |
romance: {
|
| 347 |
+
positive: [
|
| 348 |
+
"cuddle",
|
| 349 |
+
"love",
|
| 350 |
+
"romantic",
|
| 351 |
+
"kiss",
|
| 352 |
+
"tenderness",
|
| 353 |
+
"passion",
|
| 354 |
+
"charming",
|
| 355 |
+
"adorable",
|
| 356 |
+
"sweet",
|
| 357 |
+
"i love you",
|
| 358 |
+
"love you"
|
| 359 |
+
],
|
| 360 |
negative: ["cold", "distant", "indifferent", "rejection", "loneliness", "breakup", "sad"]
|
| 361 |
},
|
| 362 |
affection: {
|
| 363 |
+
positive: [
|
| 364 |
+
"affection",
|
| 365 |
+
"tenderness",
|
| 366 |
+
"close",
|
| 367 |
+
"warmth",
|
| 368 |
+
"kind",
|
| 369 |
+
"caring",
|
| 370 |
+
"cuddle",
|
| 371 |
+
"love",
|
| 372 |
+
"adore",
|
| 373 |
+
"lovely",
|
| 374 |
+
"i love you",
|
| 375 |
+
"love you"
|
| 376 |
+
],
|
| 377 |
negative: [
|
| 378 |
"mean",
|
| 379 |
"cold",
|
|
|
|
| 435 |
]
|
| 436 |
},
|
| 437 |
romance: {
|
| 438 |
+
positive: [
|
| 439 |
+
"câlin",
|
| 440 |
+
"amour",
|
| 441 |
+
"romantique",
|
| 442 |
+
"bisou",
|
| 443 |
+
"tendresse",
|
| 444 |
+
"passion",
|
| 445 |
+
"séduisant",
|
| 446 |
+
"charmant",
|
| 447 |
+
"adorable",
|
| 448 |
+
"je t'aime",
|
| 449 |
+
"je taime",
|
| 450 |
+
"t'aime",
|
| 451 |
+
"taime"
|
| 452 |
+
],
|
| 453 |
negative: [
|
| 454 |
"froid",
|
| 455 |
"froide",
|
|
|
|
| 473 |
"attentionné",
|
| 474 |
"câlin",
|
| 475 |
"aimer",
|
| 476 |
+
"aime",
|
| 477 |
+
"je t'aime",
|
| 478 |
+
"je taime",
|
| 479 |
+
"t'aime",
|
| 480 |
+
"taime",
|
| 481 |
"adorer",
|
| 482 |
"adorable"
|
| 483 |
],
|
|
|
|
| 565 |
]
|
| 566 |
},
|
| 567 |
romance: {
|
| 568 |
+
positive: [
|
| 569 |
+
"abrazo",
|
| 570 |
+
"amor",
|
| 571 |
+
"romántico",
|
| 572 |
+
"beso",
|
| 573 |
+
"ternura",
|
| 574 |
+
"pasión",
|
| 575 |
+
"encantador",
|
| 576 |
+
"adorable",
|
| 577 |
+
"dulce",
|
| 578 |
+
"te quiero",
|
| 579 |
+
"te amo"
|
| 580 |
+
],
|
| 581 |
negative: ["frío", "fría", "distante", "indiferente", "rechazo", "soledad", "ruptura", "triste"]
|
| 582 |
},
|
| 583 |
affection: {
|
| 584 |
+
positive: [
|
| 585 |
+
"afecto",
|
| 586 |
+
"ternura",
|
| 587 |
+
"cerca",
|
| 588 |
+
"calidez",
|
| 589 |
+
"amable",
|
| 590 |
+
"cariño",
|
| 591 |
+
"abrazar",
|
| 592 |
+
"amor",
|
| 593 |
+
"adorar",
|
| 594 |
+
"te quiero",
|
| 595 |
+
"te amo"
|
| 596 |
+
],
|
| 597 |
negative: [
|
| 598 |
"malo",
|
| 599 |
"mala",
|
|
|
|
| 663 |
"leidenschaft",
|
| 664 |
"charmant",
|
| 665 |
"liebenswert",
|
| 666 |
+
"süß",
|
| 667 |
+
"ich liebe dich"
|
| 668 |
],
|
| 669 |
negative: [
|
| 670 |
"kalt",
|
|
|
|
| 681 |
]
|
| 682 |
},
|
| 683 |
affection: {
|
| 684 |
+
positive: [
|
| 685 |
+
"zuneigung",
|
| 686 |
+
"zärtlichkeit",
|
| 687 |
+
"nah",
|
| 688 |
+
"wärme",
|
| 689 |
+
"freundlich",
|
| 690 |
+
"fürsorglich",
|
| 691 |
+
"umarmen",
|
| 692 |
+
"liebe",
|
| 693 |
+
"anbeten",
|
| 694 |
+
"ich liebe dich"
|
| 695 |
+
],
|
| 696 |
negative: [
|
| 697 |
"gemein",
|
| 698 |
"gemeine",
|
|
|
|
| 754 |
negative: ["stupido", "stupida", "sciocco", "sciocca", "lento", "lenta", "ingenuo", "ingenua", "ignorante"]
|
| 755 |
},
|
| 756 |
romance: {
|
| 757 |
+
positive: [
|
| 758 |
+
"abbraccio",
|
| 759 |
+
"amore",
|
| 760 |
+
"romantico",
|
| 761 |
+
"bacio",
|
| 762 |
+
"tenerezza",
|
| 763 |
+
"passione",
|
| 764 |
+
"affascinante",
|
| 765 |
+
"adorabile",
|
| 766 |
+
"dolce",
|
| 767 |
+
"ti amo"
|
| 768 |
+
],
|
| 769 |
negative: ["freddo", "fredda", "distante", "indifferente", "rifiuto", "solitudine", "rottura", "triste"]
|
| 770 |
},
|
| 771 |
affection: {
|
| 772 |
+
positive: [
|
| 773 |
+
"affetto",
|
| 774 |
+
"tenerezza",
|
| 775 |
+
"vicino",
|
| 776 |
+
"calore",
|
| 777 |
+
"gentile",
|
| 778 |
+
"premuroso",
|
| 779 |
+
"abbraccio",
|
| 780 |
+
"amore",
|
| 781 |
+
"adorare",
|
| 782 |
+
"ti amo"
|
| 783 |
+
],
|
| 784 |
negative: [
|
| 785 |
"cattivo",
|
| 786 |
"cattiva",
|
|
|
|
| 824 |
laughing: ["はは", "笑", "笑う", "面白い", "愉快"],
|
| 825 |
shy: ["恥ずかしい", "照れる", "赤面", "内気", "遠慮"],
|
| 826 |
confident: ["自信", "誇り", "確信", "強い", "決意"],
|
| 827 |
+
romantic: ["愛", "ロマンチック", "優しい", "抱擁", "キス", "愛しい", "愛してる", "好き"],
|
| 828 |
flirtatious: ["いちゃつく", "からかう", "誘惑", "魅力", "フリート"],
|
| 829 |
goodbye: ["さようなら", "バイバイ", "また今度", "チャオ", "またね"],
|
| 830 |
kiss: ["キス", "抱擁", "チュー"],
|
|
|
|
| 836 |
laughing: ["哈哈", "笑", "大笑", "有趣", "搞笑"],
|
| 837 |
shy: ["害羞", "尴尬", "脸红", "羞涩", "胆怯"],
|
| 838 |
confident: ["自信", "骄傲", "确信", "强壮", "坚定"],
|
| 839 |
+
romantic: ["爱", "浪漫", "温柔", "拥抱", "吻", "亲爱的", "我爱你"],
|
| 840 |
flirtatious: ["调情", "挑逗", "诱惑", "魅力", "撒娇"],
|
| 841 |
goodbye: ["再见", "拜拜", "回头见", "拜", "下次见"],
|
| 842 |
kiss: ["吻", "亲吻", "拥抱", "亲"],
|
|
|
|
| 910 |
window.KIMI_NEGATION_WINDOW = window.KIMI_NEGATION_WINDOW || 3; // tokens to look back for negation
|
| 911 |
window.KIMI_SMOOTHING_ALPHA = window.KIMI_SMOOTHING_ALPHA || 0.3;
|
| 912 |
window.KIMI_PERSIST_THRESHOLD = window.KIMI_PERSIST_THRESHOLD || 0.1; // absolute percent (slightly higher to slow small visible jumps)
|
| 913 |
+
// Weight applied to counts found in the LLM/Kimi response when updating personality traits.
|
| 914 |
+
// You can override at runtime by setting window.KIMI_LLM_RESPONSE_WEIGHT (e.g. 0.2)
|
| 915 |
+
window.KIMI_LLM_RESPONSE_WEIGHT = window.KIMI_LLM_RESPONSE_WEIGHT || 0.2;
|
| 916 |
|
| 917 |
// Memory system knobs
|
| 918 |
window.KIMI_MAX_MEMORIES = window.KIMI_MAX_MEMORIES || 100; // default max memory entries per character
|
kimi-js/kimi-database.js
CHANGED
|
@@ -98,7 +98,7 @@ class KimiDatabase {
|
|
| 98 |
async setConversationsBatch(conversationsArray) {
|
| 99 |
if (!Array.isArray(conversationsArray)) return;
|
| 100 |
try {
|
| 101 |
-
await this.
|
| 102 |
if (conversationsArray.length) {
|
| 103 |
await this.db.conversations.bulkPut(conversationsArray);
|
| 104 |
}
|
|
@@ -107,6 +107,18 @@ class KimiDatabase {
|
|
| 107 |
}
|
| 108 |
}
|
| 109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
async setLLMModelsBatch(modelsArray) {
|
| 111 |
if (!Array.isArray(modelsArray)) return;
|
| 112 |
try {
|
|
@@ -465,6 +477,24 @@ class KimiDatabase {
|
|
| 465 |
|
| 466 |
async saveConversation(userText, kimiResponse, favorability, timestamp = new Date(), character = null) {
|
| 467 |
if (!character) character = await this.getSelectedCharacter();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 468 |
const conversation = {
|
| 469 |
user: userText,
|
| 470 |
kimi: kimiResponse,
|
|
|
|
| 98 |
async setConversationsBatch(conversationsArray) {
|
| 99 |
if (!Array.isArray(conversationsArray)) return;
|
| 100 |
try {
|
| 101 |
+
await this.clearConversations(null);
|
| 102 |
if (conversationsArray.length) {
|
| 103 |
await this.db.conversations.bulkPut(conversationsArray);
|
| 104 |
}
|
|
|
|
| 107 |
}
|
| 108 |
}
|
| 109 |
|
| 110 |
+
// Clear conversations helper - centralizes clearing logic
|
| 111 |
+
async clearConversations(character = null) {
|
| 112 |
+
if (character) {
|
| 113 |
+
// Delete only conversations for a given character
|
| 114 |
+
const all = await this.db.conversations.where("character").equals(character).toArray();
|
| 115 |
+
const ids = all.map(item => item.id);
|
| 116 |
+
if (ids.length) return this.db.conversations.bulkDelete(ids);
|
| 117 |
+
return Promise.resolve();
|
| 118 |
+
}
|
| 119 |
+
return this.db.conversations.clear();
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
async setLLMModelsBatch(modelsArray) {
|
| 123 |
if (!Array.isArray(modelsArray)) return;
|
| 124 |
try {
|
|
|
|
| 477 |
|
| 478 |
async saveConversation(userText, kimiResponse, favorability, timestamp = new Date(), character = null) {
|
| 479 |
if (!character) character = await this.getSelectedCharacter();
|
| 480 |
+
// If a global cleared timestamp exists and this conversation is older or equal, skip saving
|
| 481 |
+
try {
|
| 482 |
+
const clearedMap = window.kimiConversationsClearedAt;
|
| 483 |
+
if (clearedMap && typeof clearedMap === "object") {
|
| 484 |
+
const clearedAtForChar = clearedMap[character];
|
| 485 |
+
if (clearedAtForChar) {
|
| 486 |
+
const clearedTime = new Date(clearedAtForChar).getTime();
|
| 487 |
+
const convTime = new Date(timestamp).getTime();
|
| 488 |
+
if (!isNaN(clearedTime) && convTime <= clearedTime) {
|
| 489 |
+
// Skip saving to avoid resurrecting cleared messages for this character
|
| 490 |
+
return null;
|
| 491 |
+
}
|
| 492 |
+
}
|
| 493 |
+
}
|
| 494 |
+
} catch (e) {
|
| 495 |
+
// ignore parsing errors and fall through to save
|
| 496 |
+
}
|
| 497 |
+
|
| 498 |
const conversation = {
|
| 499 |
user: userText,
|
| 500 |
kimi: kimiResponse,
|
kimi-js/kimi-emotion-system.js
CHANGED
|
@@ -357,13 +357,14 @@ class KimiEmotionSystem {
|
|
| 357 |
let posCount = 0;
|
| 358 |
let negCount = 0;
|
| 359 |
|
|
|
|
| 360 |
for (const w of posWords) {
|
| 361 |
posCount += this.countTokenMatches(lowerUser, String(w)) * 1.0;
|
| 362 |
-
posCount += this.countTokenMatches(lowerKimi, String(w)) *
|
| 363 |
}
|
| 364 |
for (const w of negWords) {
|
| 365 |
negCount += this.countTokenMatches(lowerUser, String(w)) * 1.0;
|
| 366 |
-
negCount += this.countTokenMatches(lowerKimi, String(w)) *
|
| 367 |
}
|
| 368 |
|
| 369 |
const delta = (posCount - negCount) * 0.8; // softened multiplier to 0.8 for gentler progression
|
|
|
|
| 357 |
let posCount = 0;
|
| 358 |
let negCount = 0;
|
| 359 |
|
| 360 |
+
const llmWeight = Number(window.KIMI_LLM_RESPONSE_WEIGHT) || 0.5;
|
| 361 |
for (const w of posWords) {
|
| 362 |
posCount += this.countTokenMatches(lowerUser, String(w)) * 1.0;
|
| 363 |
+
posCount += this.countTokenMatches(lowerKimi, String(w)) * llmWeight;
|
| 364 |
}
|
| 365 |
for (const w of negWords) {
|
| 366 |
negCount += this.countTokenMatches(lowerUser, String(w)) * 1.0;
|
| 367 |
+
negCount += this.countTokenMatches(lowerKimi, String(w)) * llmWeight;
|
| 368 |
}
|
| 369 |
|
| 370 |
const delta = (posCount - negCount) * 0.8; // softened multiplier to 0.8 for gentler progression
|
kimi-js/kimi-module.js
CHANGED
|
@@ -41,7 +41,7 @@ class KimiDataManager extends KimiBaseManager {
|
|
| 41 |
|
| 42 |
try {
|
| 43 |
// Clear all conversations directly
|
| 44 |
-
await this.db.
|
| 45 |
|
| 46 |
// Clear chat UI
|
| 47 |
const chatMessages = document.getElementById("chat-messages");
|
|
@@ -822,6 +822,23 @@ async function loadChatHistory() {
|
|
| 822 |
chatMessages.removeChild(chatMessages.firstChild);
|
| 823 |
}
|
| 824 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 825 |
if (kimiDB) {
|
| 826 |
try {
|
| 827 |
const recent = await kimiDB.getRecentConversations(10);
|
|
@@ -1545,6 +1562,37 @@ async function sendMessage() {
|
|
| 1545 |
message = validation.sanitized || message.trim();
|
| 1546 |
if (!message) return;
|
| 1547 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1548 |
addMessageToChat("user", message);
|
| 1549 |
chatInput.value = "";
|
| 1550 |
if (waitingIndicator) waitingIndicator.style.display = "inline-block";
|
|
|
|
| 41 |
|
| 42 |
try {
|
| 43 |
// Clear all conversations directly
|
| 44 |
+
await this.db.clearConversations();
|
| 45 |
|
| 46 |
// Clear chat UI
|
| 47 |
const chatMessages = document.getElementById("chat-messages");
|
|
|
|
| 822 |
chatMessages.removeChild(chatMessages.firstChild);
|
| 823 |
}
|
| 824 |
|
| 825 |
+
// Ensure i18n manager has loaded translations to avoid raw keys appearing (e.g., "greeting_high")
|
| 826 |
+
if (window.kimiI18nManager && typeof window.kimiI18nManager.applyTranslations === "function") {
|
| 827 |
+
// give i18n a short moment to apply if still loading
|
| 828 |
+
const start = Date.now();
|
| 829 |
+
while (
|
| 830 |
+
(window.kimiI18nManager.translations == null || Object.keys(window.kimiI18nManager.translations).length === 0) &&
|
| 831 |
+
Date.now() - start < 500
|
| 832 |
+
) {
|
| 833 |
+
// small delay
|
| 834 |
+
// eslint-disable-next-line no-await-in-loop
|
| 835 |
+
await new Promise(r => setTimeout(r, 50));
|
| 836 |
+
}
|
| 837 |
+
try {
|
| 838 |
+
window.kimiI18nManager.applyTranslations();
|
| 839 |
+
} catch (e) {}
|
| 840 |
+
}
|
| 841 |
+
|
| 842 |
if (kimiDB) {
|
| 843 |
try {
|
| 844 |
const recent = await kimiDB.getRecentConversations(10);
|
|
|
|
| 1562 |
message = validation.sanitized || message.trim();
|
| 1563 |
if (!message) return;
|
| 1564 |
|
| 1565 |
+
// Force persist current personality sliders to avoid UI/DB desync
|
| 1566 |
+
try {
|
| 1567 |
+
const kimiDB = window.kimiDB;
|
| 1568 |
+
if (kimiDB && typeof kimiDB.setPersonalityBatch === "function") {
|
| 1569 |
+
const traitIds = [
|
| 1570 |
+
"trait-affection",
|
| 1571 |
+
"trait-playfulness",
|
| 1572 |
+
"trait-intelligence",
|
| 1573 |
+
"trait-empathy",
|
| 1574 |
+
"trait-humor",
|
| 1575 |
+
"trait-romance"
|
| 1576 |
+
];
|
| 1577 |
+
const pending = {};
|
| 1578 |
+
traitIds.forEach(id => {
|
| 1579 |
+
const el = document.getElementById(id);
|
| 1580 |
+
if (el && el.value !== undefined) {
|
| 1581 |
+
const trait = id.replace(/^trait-/, "");
|
| 1582 |
+
const v = Number(el.value);
|
| 1583 |
+
if (isFinite(v)) pending[trait] = v;
|
| 1584 |
+
}
|
| 1585 |
+
});
|
| 1586 |
+
// Only write when there is at least one trait
|
| 1587 |
+
if (Object.keys(pending).length > 0) {
|
| 1588 |
+
// small timeout to ensure UI changes settled (rare) - use direct write
|
| 1589 |
+
await kimiDB.setPersonalityBatch(pending);
|
| 1590 |
+
}
|
| 1591 |
+
}
|
| 1592 |
+
} catch (e) {
|
| 1593 |
+
console.warn("Failed to persist personality sliders before send:", e);
|
| 1594 |
+
}
|
| 1595 |
+
|
| 1596 |
addMessageToChat("user", message);
|
| 1597 |
chatInput.value = "";
|
| 1598 |
if (waitingIndicator) waitingIndicator.style.display = "inline-block";
|
kimi-js/kimi-script.js
CHANGED
|
@@ -58,6 +58,24 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
| 58 |
await kimiMemory.init();
|
| 59 |
window.kimiMemory = kimiMemory;
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
// Expose globally (already set before init)
|
| 62 |
|
| 63 |
// Load available models now that LLM is ready
|
|
@@ -409,57 +427,82 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
| 409 |
async function attachCharacterSection() {
|
| 410 |
let saveCharacterBtn = window.KimiDOMUtils.get("#save-character-btn");
|
| 411 |
if (saveCharacterBtn) {
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
if (window.
|
| 432 |
-
window.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 433 |
}
|
| 434 |
-
}
|
| 435 |
-
if (window.voiceManager && window.voiceManager.updateSelectedCharacter) {
|
| 436 |
-
await window.voiceManager.updateSelectedCharacter();
|
| 437 |
-
}
|
| 438 |
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 459 |
}
|
| 460 |
let settingsButton2 = window.KimiDOMUtils.get("#settings-button");
|
| 461 |
if (settingsButton2) {
|
| 462 |
-
settingsButton2.
|
|
|
|
|
|
|
|
|
|
| 463 |
}
|
| 464 |
}
|
| 465 |
await attachCharacterSection();
|
|
@@ -901,7 +944,29 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
| 901 |
}
|
| 902 |
if (window.kimiDB && window.kimiDB.db) {
|
| 903 |
try {
|
| 904 |
-
await window.kimiDB.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 905 |
} catch (error) {
|
| 906 |
console.error("Error deleting conversations:", error);
|
| 907 |
}
|
|
|
|
| 58 |
await kimiMemory.init();
|
| 59 |
window.kimiMemory = kimiMemory;
|
| 60 |
|
| 61 |
+
// Setup BroadcastChannel to announce conversation clears across tabs (very small, optional)
|
| 62 |
+
try {
|
| 63 |
+
if (typeof BroadcastChannel !== "undefined") {
|
| 64 |
+
window.kimiBroadcast = new BroadcastChannel("kimi-db-events");
|
| 65 |
+
window.kimiBroadcast.addEventListener("message", ev => {
|
| 66 |
+
try {
|
| 67 |
+
const data = ev.data || {};
|
| 68 |
+
if (data && data.type === "conversations:cleared" && data.character) {
|
| 69 |
+
window.kimiConversationsClearedAt = window.kimiConversationsClearedAt || {};
|
| 70 |
+
window.kimiConversationsClearedAt[data.character] = data.timestamp || new Date().toISOString();
|
| 71 |
+
}
|
| 72 |
+
} catch (e) {
|
| 73 |
+
// ignore
|
| 74 |
+
}
|
| 75 |
+
});
|
| 76 |
+
}
|
| 77 |
+
} catch (e) {}
|
| 78 |
+
|
| 79 |
// Expose globally (already set before init)
|
| 80 |
|
| 81 |
// Load available models now that LLM is ready
|
|
|
|
| 427 |
async function attachCharacterSection() {
|
| 428 |
let saveCharacterBtn = window.KimiDOMUtils.get("#save-character-btn");
|
| 429 |
if (saveCharacterBtn) {
|
| 430 |
+
// Prevent attaching the same listener multiple times
|
| 431 |
+
if (saveCharacterBtn.dataset.kimiSaveListenerAttached === "1") {
|
| 432 |
+
// already attached
|
| 433 |
+
} else {
|
| 434 |
+
saveCharacterBtn.dataset.kimiSaveListenerAttached = "1";
|
| 435 |
+
saveCharacterBtn.addEventListener("click", async e => {
|
| 436 |
+
const settingsPanel = window.KimiDOMUtils.get(".settings-panel");
|
| 437 |
+
let scrollTop = settingsPanel ? settingsPanel.scrollTop : null;
|
| 438 |
+
const characterGrid = window.KimiDOMUtils.get("#character-grid");
|
| 439 |
+
const selectedCard = characterGrid ? characterGrid.querySelector(".character-card.selected") : null;
|
| 440 |
+
if (!selectedCard) return;
|
| 441 |
+
const charKey = selectedCard.dataset.character;
|
| 442 |
+
// Character save should not toggle the API key saved indicator.
|
| 443 |
+
const promptInput = window.KimiDOMUtils.get(`#prompt-${charKey}`);
|
| 444 |
+
const prompt = promptInput ? promptInput.value : "";
|
| 445 |
+
|
| 446 |
+
await window.kimiDB.setSelectedCharacter(charKey);
|
| 447 |
+
await window.kimiDB.setSystemPromptForCharacter(charKey, prompt);
|
| 448 |
+
// Ensure memory system uses the correct character
|
| 449 |
+
if (window.kimiMemorySystem) {
|
| 450 |
+
window.kimiMemorySystem.selectedCharacter = charKey;
|
| 451 |
+
}
|
| 452 |
+
if (window.kimiVideo && window.kimiVideo.setCharacter) {
|
| 453 |
+
window.kimiVideo.setCharacter(charKey);
|
| 454 |
+
if (window.kimiVideo.switchToContext) {
|
| 455 |
+
window.kimiVideo.switchToContext("neutral");
|
| 456 |
+
}
|
| 457 |
+
}
|
| 458 |
+
if (window.voiceManager && window.voiceManager.updateSelectedCharacter) {
|
| 459 |
+
await window.voiceManager.updateSelectedCharacter();
|
| 460 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 461 |
|
| 462 |
+
await window.loadCharacterSection();
|
| 463 |
+
if (settingsPanel && scrollTop !== null) {
|
| 464 |
+
requestAnimationFrame(() => {
|
| 465 |
+
settingsPanel.scrollTop = scrollTop;
|
| 466 |
+
});
|
| 467 |
+
}
|
| 468 |
+
// Refresh memory tab after character selection
|
| 469 |
+
if (window.kimiMemoryUI && typeof window.kimiMemoryUI.updateMemoryStats === "function") {
|
| 470 |
+
await window.kimiMemoryUI.updateMemoryStats();
|
| 471 |
+
}
|
| 472 |
+
// Refresh chat history immediately so the chat-messages container shows the selected character's history
|
| 473 |
+
if (typeof window.loadChatHistory === "function") {
|
| 474 |
+
try {
|
| 475 |
+
await window.loadChatHistory();
|
| 476 |
+
} catch (e) {
|
| 477 |
+
console.warn("Failed to refresh chat history after character change:", e);
|
| 478 |
+
}
|
| 479 |
+
}
|
| 480 |
+
saveCharacterBtn.setAttribute("data-i18n", "saved");
|
| 481 |
+
saveCharacterBtn.classList.add("success");
|
| 482 |
+
saveCharacterBtn.disabled = true;
|
| 483 |
+
|
| 484 |
+
setTimeout(() => {
|
| 485 |
+
saveCharacterBtn.setAttribute("data-i18n", "save");
|
| 486 |
+
saveCharacterBtn.classList.remove("success");
|
| 487 |
+
saveCharacterBtn.disabled = false;
|
| 488 |
+
}, 900);
|
| 489 |
+
// Also reload the page shortly after restoring the button so the user sees feedback
|
| 490 |
+
setTimeout(() => {
|
| 491 |
+
try {
|
| 492 |
+
location.reload();
|
| 493 |
+
} catch (e) {
|
| 494 |
+
console.warn("Failed to reload after save feedback:", e);
|
| 495 |
+
}
|
| 496 |
+
}, 1100);
|
| 497 |
+
});
|
| 498 |
+
}
|
| 499 |
}
|
| 500 |
let settingsButton2 = window.KimiDOMUtils.get("#settings-button");
|
| 501 |
if (settingsButton2) {
|
| 502 |
+
if (!settingsButton2.dataset.kimiSettingsListenerAttached) {
|
| 503 |
+
settingsButton2.dataset.kimiSettingsListenerAttached = "1";
|
| 504 |
+
settingsButton2.addEventListener("click", window.loadCharacterSection);
|
| 505 |
+
}
|
| 506 |
}
|
| 507 |
}
|
| 508 |
await attachCharacterSection();
|
|
|
|
| 944 |
}
|
| 945 |
if (window.kimiDB && window.kimiDB.db) {
|
| 946 |
try {
|
| 947 |
+
const selectedCharacter = await window.kimiDB.getSelectedCharacter();
|
| 948 |
+
// Mark cleared timestamp per-character to prevent pending async saves from re-adding old messages
|
| 949 |
+
window.kimiConversationsClearedAt = window.kimiConversationsClearedAt || {};
|
| 950 |
+
const ts = new Date().toISOString();
|
| 951 |
+
window.kimiConversationsClearedAt[selectedCharacter] = ts;
|
| 952 |
+
await window.kimiDB.clearConversations(selectedCharacter);
|
| 953 |
+
// Broadcast the clear to other tabs so they set their cleared markers too
|
| 954 |
+
try {
|
| 955 |
+
if (window.kimiBroadcast && typeof window.kimiBroadcast.postMessage === "function") {
|
| 956 |
+
window.kimiBroadcast.postMessage({
|
| 957 |
+
type: "conversations:cleared",
|
| 958 |
+
character: selectedCharacter,
|
| 959 |
+
timestamp: ts
|
| 960 |
+
});
|
| 961 |
+
}
|
| 962 |
+
} catch (e) {}
|
| 963 |
+
// Clear the cleared marker for this character shortly after to allow new conversations again
|
| 964 |
+
setTimeout(() => {
|
| 965 |
+
try {
|
| 966 |
+
if (window.kimiConversationsClearedAt)
|
| 967 |
+
delete window.kimiConversationsClearedAt[selectedCharacter];
|
| 968 |
+
} catch (e) {}
|
| 969 |
+
}, 5000);
|
| 970 |
} catch (error) {
|
| 971 |
console.error("Error deleting conversations:", error);
|
| 972 |
}
|