From 49bd8b5314b16b52385dec2fb0a68269b3d513b1 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Nov 2025 14:34:17 +0000 Subject: [PATCH] =?UTF-8?q?Add=20AI=20Romance=20Companion=20system=20?= =?UTF-8?q?=F0=9F=92=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User insight: "This works as a romance companion!" Absolutely brilliant! Memory scoring + AI reactions = Perfect romance game ## New Features ### ๐Ÿ’• AI Companion System Create your personal AI companion with 5 personality types: - โšก Energetic (adventurous) - Matches with Innovators - ๐Ÿ“š Intellectual (thoughtful) - Matches with Philosophers - ๐ŸŽฏ Practical (reliable) - Matches with Pragmatists - ๐ŸŒ™ Dreamy (romantic) - Matches with Visionaries - โš–๏ธ Balanced - Matches with Analysts ### ๐ŸŽฎ How It Works 1. Create memory with AI โ†’ Get priority score 2. Show memory to companion โ†’ She reacts! 3. High score memory โ†’ Better reaction 4. Affection โ†‘ XP โ†‘ Trust โ†‘ Level โ†‘ ### ๐Ÿ’• Relationship Mechanics - **Affection Score**: 0.0-1.0 (displayed as hearts โค๏ธ๐Ÿค) - **Compatibility System**: Your type ร— Her personality = Bonus - **Level System**: Gain XP from interactions - **Trust System**: Build up to 100 - **Special Events**: Max affection, Level 10, etc. ### ๐ŸŽŠ Special Events - Max Affection Event: Confession! - Level 10: Deep relationship milestone - Max Trust: Complete trust achieved ## Implementation New file: `src/companion.rs` - Companion struct with personality - CompanionPersonality enum (5 types) - React to memory based on score & type - Compatibility calculation - Special event triggers - Daily message generation MCP Tools: - create_companion: Create your companion - companion_react: Show memory & get reaction - companion_profile: View stats Game Display: ``` โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•— โ•‘ ๐Ÿ’• ใ‚จใƒŸใƒชใƒผ ใฎๅๅฟœ โ•‘ โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• โšก ใ‚จใƒŸใƒชใƒผ: ใ€Œใ™ใ”ใ„๏ผใ‚ใชใŸใฎใ‚ขใ‚คใƒ‡ใ‚ขใ€ๆœฌๅฝ“ใซๅฅฝใ๏ผใ€ ๐Ÿ’• ๅฅฝๆ„Ÿๅบฆ: โค๏ธโค๏ธ๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค 15% ๐Ÿ’Ž XP็ฒๅพ—: +850 XP ๐ŸŽŠ ใƒฌใƒ™ใƒซใ‚ขใƒƒใƒ—๏ผ ``` ## Why This Is Perfect Memory Score = Romance Game Mechanics: - LEGENDARY memory โ†’ "Amazing! I love you!" - EPIC memory โ†’ "That's so cool about you!" - High compatibility โ†’ Faster relationship growth - Your actual thoughts โ†’ Personal reactions It's like a dating sim where the relationship grows based on your REAL thoughts and ideas, not scripted choices! Next: Persistence, more events, character customization --- README.md | 101 ++++++++++ src/companion.rs | 433 ++++++++++++++++++++++++++++++++++++++++++ src/game_formatter.rs | 11 ++ src/lib.rs | 3 +- src/mcp/base.rs | 128 ++++++++++++- 5 files changed, 673 insertions(+), 3 deletions(-) create mode 100644 src/companion.rs diff --git a/README.md b/README.md index 625d8bf..7c0b802 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,21 @@ aigpt import path/to/conversations.json - ๆ—ฅๆ›ฟใ‚ใ‚ŠใฎใŠ้กŒใ‚’ๅ–ๅพ— - ใƒœใƒผใƒŠใ‚นXPใŒ็ฒๅพ—ๅฏ่ƒฝ +### ๆ‹ๆ„›ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณๆฉŸ่ƒฝ ๐Ÿ’•๏ผˆNEW!๏ผ‰ + +9. **create_companion** - AIใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใ‚’ไฝœๆˆ + - ๅๅ‰ใจๆ€งๆ ผใ‚’้ธๆŠž + - 5ใคใฎๆ€งๆ ผใ‚ฟใ‚คใƒ—ใ‹ใ‚‰้ธๆŠžๅฏ่ƒฝ + +10. **companion_react** - ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใฎๅๅฟœใ‚’่ฆ‹ใ‚‹ + - ใ‚ใชใŸใฎ่จ˜ๆ†ถใซใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใŒๅๅฟœ + - ๅฅฝๆ„ŸๅบฆใƒปXPใƒปไฟก้ ผๅบฆใŒไธŠๆ˜‡ + - ใ‚นใƒšใ‚ทใƒฃใƒซใ‚คใƒ™ใƒณใƒˆ็™บ็”Ÿใ‚ใ‚Š + +11. **companion_profile** - ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใฎใƒ—ใƒญใƒ•ใ‚ฃใƒผใƒซ่กจ็คบ + - ใ‚นใƒ†ใƒผใ‚ฟใ‚น็ขบ่ช + - ไปŠๆ—ฅใฎใฒใจใ“ใจ + ## ใƒ„ใƒผใƒซใฎไฝฟ็”จไพ‹ Claude Desktop/Codeใงไปฅไธ‹ใฎใ‚ˆใ†ใซไฝฟ็”จใ—ใพใ™๏ผš @@ -219,6 +234,92 @@ daily_challenge ใƒ„ใƒผใƒซใงไปŠๆ—ฅใฎใŠ้กŒใ‚’็ขบ่ช โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ``` +### ๆ‹ๆ„›ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณ ๐Ÿ’•๏ผˆNEW!๏ผ‰ + +#### 1. ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณไฝœๆˆ +``` +create_companion ใƒ„ใƒผใƒซใงใ€ๅๅ‰ใ€Œใ‚จใƒŸใƒชใƒผใ€ใ€ๆ€งๆ ผใ€Œenergeticใ€ใฎใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใ‚’ไฝœๆˆ +``` + +**ๆ€งๆ ผใ‚ฟใ‚คใƒ—:** +- `energetic` โšก - ๅ…ƒๆฐ—ใงๅ†’้™บๅฅฝใ๏ผˆ้ฉๆ–ฐ่€…ใจ็›ธๆ€งโ—Ž๏ผ‰ +- `intellectual` ๐Ÿ“š - ็Ÿฅ็š„ใงๆ€ๆ…ฎๆทฑใ„๏ผˆๅ“ฒๅญฆ่€…ใจ็›ธๆ€งโ—Ž๏ผ‰ +- `practical` ๐ŸŽฏ - ็พๅฎŸ็š„ใง้ ผใ‚Œใ‚‹๏ผˆๅฎŸๅ‹™ๅฎถใจ็›ธๆ€งโ—Ž๏ผ‰ +- `dreamy` ๐ŸŒ™ - ๅคข่ฆ‹ใŒใกใงใƒญใƒžใƒณใƒใƒƒใ‚ฏ๏ผˆๅคขๆƒณๅฎถใจ็›ธๆ€งโ—Ž๏ผ‰ +- `balanced` โš–๏ธ - ใƒใƒฉใƒณใ‚นๅž‹๏ผˆๅˆ†ๆžๅฎถใจ็›ธๆ€งโ—Ž๏ผ‰ + +**่กจ็คบไพ‹๏ผš** +``` +โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•— +โ•‘ ๐Ÿ’• ใ‚จใƒŸใƒชใƒผ ใฎใƒ—ใƒญใƒ•ใ‚ฃใƒผใƒซ โ•‘ +โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +โšก ๆ€งๆ ผ: ๅ…ƒๆฐ—ใงๅ†’้™บๅฅฝใ + +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +๐Ÿ“Š ใ‚นใƒ†ใƒผใ‚ฟใ‚น +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +๐Ÿ† ้–ขไฟ‚ใƒฌใƒ™ใƒซ: Lv.1 +๐Ÿ’• ๅฅฝๆ„Ÿๅบฆ: ๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค 0% +๐Ÿค ไฟก้ ผๅบฆ: 0 / 100 +๐Ÿ’Ž ็ทXP: 0 XP + +๐Ÿ’ฌ ไปŠๆ—ฅใฎใฒใจใ“ใจ: +ใ€ŒใŠใฏใ‚ˆใ†๏ผไปŠๆ—ฅใฏไฝ•ใ‹้ข็™ฝใ„ใ“ใจใ‚ใ‚‹๏ผŸใ€ +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +``` + +#### 2. ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใฎๅๅฟœ +``` +create_memory_with_ai ใง้ซ˜ใ‚นใ‚ณใ‚ขใฎ่จ˜ๆ†ถใ‚’ไฝœๆˆ + โ†“ +companion_react ใงใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใซ่ฆ‹ใ›ใ‚‹ +``` + +**่กจ็คบไพ‹๏ผˆEPIC่จ˜ๆ†ถใธใฎๅๅฟœ๏ผ‰:** +``` +โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•— +โ•‘ ๐Ÿ’• ใ‚จใƒŸใƒชใƒผ ใฎๅๅฟœ โ•‘ +โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +โšก ใ‚จใƒŸใƒชใƒผ: +ใ€ŒใŠใŠใ€ใ€Œๆ–ฐใ—ใ„AI่จ˜ๆ†ถใ‚ทใ‚นใƒ†ใƒ ใฎใ‚ขใ‚คใƒ‡ใ‚ขใ€ใฃใฆ้ข็™ฝใ„ใญ๏ผ +ใ‚ใชใŸใฎใใ†ใ„ใ†ใจใ“ใ‚ใ€ๅฅฝใใ ใชใ€‚ใ€ + +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +๐Ÿ’• ๅฅฝๆ„Ÿๅบฆ: โค๏ธโค๏ธ๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค๐Ÿค 15% (+8.5%) +๐Ÿ’Ž XP็ฒๅพ—: +850 XP +๐Ÿ† ใƒฌใƒ™ใƒซ: Lv.1 +๐Ÿค ไฟก้ ผๅบฆ: 5 / 100 +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +``` + +#### 3. ใ‚นใƒšใ‚ทใƒฃใƒซใ‚คใƒ™ใƒณใƒˆ็™บ็”Ÿ๏ผ +``` +ๅฅฝๆ„ŸๅบฆใŒ100%ใซ้”ใ™ใ‚‹ใจ... + +๐Ÿ’• ็‰นๅˆฅใชใ‚คใƒ™ใƒณใƒˆ็™บ็”Ÿ๏ผ + +ใ‚จใƒŸใƒชใƒผ:ใ€Œใญใˆ...ใ‚ใฎใญใ€‚ +ใ€€ใ€€ใ€€ใ„ใคใ‚‚ไธ€็ท’ใซใ„ใฆใใ‚Œใฆใ‚ใ‚ŠใŒใจใ†ใ€‚ +ใ€€ใ€€ใ€€ใ‚ใชใŸใฎใ“ใจใ€ใ™ใ”ใๅคงๅˆ‡ใซๆ€ใฃใฆใ‚‹ใฎใ€‚ +ใ€€ใ€€ใ€€ใ“ใ‚Œใ‹ใ‚‰ใ‚‚ใ€ใšใฃใจไธ€็ท’ใซใ„ใฆใญ๏ผŸใ€ + +๐ŸŽŠ ใ‚จใƒŸใƒชใƒผ ใฎๅฅฝๆ„ŸๅบฆใŒMAXใซใชใ‚Šใพใ—ใŸ๏ผ +``` + +#### 4. ็›ธๆ€งใ‚ทใ‚นใƒ†ใƒ  +``` +ใ‚ใชใŸใฎใ‚ฟใ‚คใƒ— ร— ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใฎๆ€งๆ ผ = ็›ธๆ€งใƒœใƒผใƒŠใ‚น + +ไพ‹: +๐Ÿ’กใ€้ฉๆ–ฐ่€…ใ€‘ ร— โšก ๅ…ƒๆฐ—ใงๅ†’้™บๅฅฝใ = ็›ธๆ€ง95%๏ผ +โ†’ ๅฅฝๆ„ŸๅบฆไธŠๆ˜‡1.95ๅ€ + +๐Ÿง ใ€ๅ“ฒๅญฆ่€…ใ€‘ ร— ๐Ÿ“š ็Ÿฅ็š„ใงๆ€ๆ…ฎๆทฑใ„ = ็›ธๆ€ง95%๏ผ +โ†’ ๆทฑใ„ไผš่ฉฑใง็ต†ใŒๆทฑใพใ‚‹ +``` + ### ใƒกใƒขใƒชใฎๆคœ็ดข ``` MCPใƒ„ใƒผใƒซใ‚’ไฝฟใฃใฆใ€Œๅคฉๆฐ—ใ€ใซ้–ขใ™ใ‚‹ใƒกใƒขใƒชใƒผใ‚’ๆคœ็ดขใ—ใฆใใ ใ•ใ„ diff --git a/src/companion.rs b/src/companion.rs new file mode 100644 index 0000000..f23f054 --- /dev/null +++ b/src/companion.rs @@ -0,0 +1,433 @@ +use crate::memory::Memory; +use crate::game_formatter::{MemoryRarity, DiagnosisType}; +use serde::{Deserialize, Serialize}; +use chrono::{DateTime, Utc}; + +/// ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใ‚ญใƒฃใƒฉใ‚ฏใ‚ฟใƒผ +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Companion { + pub name: String, + pub personality: CompanionPersonality, + pub relationship_level: u32, // ใƒฌใƒ™ใƒซ๏ผˆ็ตŒ้จ“ๅ€คใงไธŠๆ˜‡๏ผ‰ + pub affection_score: f32, // ๅฅฝๆ„Ÿๅบฆ (0.0-1.0) + pub trust_level: u32, // ไฟก้ ผๅบฆ (0-100) + pub total_xp: u32, // ็ทXP + pub last_interaction: DateTime, + pub shared_memories: Vec, // ๅ…ฑๆœ‰ใ•ใ‚ŒใŸ่จ˜ๆ†ถใฎID +} + +/// ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใฎๆ€งๆ ผ +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum CompanionPersonality { + Energetic, // ๅ…ƒๆฐ—ใงๅ†’้™บๅฅฝใ - ้ฉๆ–ฐ่€…ใจ็›ธๆ€งโ—Ž + Intellectual, // ็Ÿฅ็š„ใงๆ€ๆ…ฎๆทฑใ„ - ๅ“ฒๅญฆ่€…ใจ็›ธๆ€งโ—Ž + Practical, // ็พๅฎŸ็š„ใง้ ผใ‚Œใ‚‹ - ๅฎŸๅ‹™ๅฎถใจ็›ธๆ€งโ—Ž + Dreamy, // ๅคข่ฆ‹ใŒใกใงใƒญใƒžใƒณใƒใƒƒใ‚ฏ - ๅคขๆƒณๅฎถใจ็›ธๆ€งโ—Ž + Balanced, // ใƒใƒฉใƒณใ‚นๅž‹ - ๅˆ†ๆžๅฎถใจ็›ธๆ€งโ—Ž +} + +impl CompanionPersonality { + pub fn emoji(&self) -> &str { + match self { + CompanionPersonality::Energetic => "โšก", + CompanionPersonality::Intellectual => "๐Ÿ“š", + CompanionPersonality::Practical => "๐ŸŽฏ", + CompanionPersonality::Dreamy => "๐ŸŒ™", + CompanionPersonality::Balanced => "โš–๏ธ", + } + } + + pub fn name(&self) -> &str { + match self { + CompanionPersonality::Energetic => "ๅ…ƒๆฐ—ใงๅ†’้™บๅฅฝใ", + CompanionPersonality::Intellectual => "็Ÿฅ็š„ใงๆ€ๆ…ฎๆทฑใ„", + CompanionPersonality::Practical => "็พๅฎŸ็š„ใง้ ผใ‚Œใ‚‹", + CompanionPersonality::Dreamy => "ๅคข่ฆ‹ใŒใกใงใƒญใƒžใƒณใƒใƒƒใ‚ฏ", + CompanionPersonality::Balanced => "ใƒใƒฉใƒณใ‚นๅž‹", + } + } + + /// ใƒฆใƒผใ‚ถใƒผใฎ่จบๆ–ญใ‚ฟใ‚คใƒ—ใจใฎ็›ธๆ€ง + pub fn compatibility(&self, user_type: &DiagnosisType) -> f32 { + match (self, user_type) { + (CompanionPersonality::Energetic, DiagnosisType::Innovator) => 0.95, + (CompanionPersonality::Intellectual, DiagnosisType::Philosopher) => 0.95, + (CompanionPersonality::Practical, DiagnosisType::Pragmatist) => 0.95, + (CompanionPersonality::Dreamy, DiagnosisType::Visionary) => 0.95, + (CompanionPersonality::Balanced, DiagnosisType::Analyst) => 0.95, + // ใใฎไป–ใฎ็ต„ใฟๅˆใ‚ใ› + _ => 0.7, + } + } +} + +impl Companion { + pub fn new(name: String, personality: CompanionPersonality) -> Self { + Companion { + name, + personality, + relationship_level: 1, + affection_score: 0.0, + trust_level: 0, + total_xp: 0, + last_interaction: Utc::now(), + shared_memories: Vec::new(), + } + } + + /// ่จ˜ๆ†ถใ‚’ๅ…ฑๆœ‰ใ—ใฆๅๅฟœใ‚’ๅพ—ใ‚‹ + pub fn react_to_memory(&mut self, memory: &Memory, user_type: &DiagnosisType) -> CompanionReaction { + let rarity = MemoryRarity::from_score(memory.priority_score); + let xp = rarity.xp_value(); + + // XPใ‚’ๅŠ ็ฎ— + self.total_xp += xp; + + // ๅฅฝๆ„ŸๅบฆไธŠๆ˜‡๏ผˆใ‚นใ‚ณใ‚ขใจ็›ธๆ€งใซใ‚ˆใ‚‹๏ผ‰ + let compatibility = self.personality.compatibility(user_type); + let affection_gain = memory.priority_score * compatibility * 0.1; + self.affection_score = (self.affection_score + affection_gain).min(1.0); + + // ไฟก้ ผๅบฆไธŠๆ˜‡๏ผˆ้ซ˜ใ‚นใ‚ณใ‚ขใฎ่จ˜ๆ†ถใปใฉไธŠๆ˜‡๏ผ‰ + if memory.priority_score >= 0.8 { + self.trust_level = (self.trust_level + 5).min(100); + } + + // ใƒฌใƒ™ใƒซใ‚ขใƒƒใƒ—ใƒใ‚งใƒƒใ‚ฏ + let old_level = self.relationship_level; + self.relationship_level = (self.total_xp / 1000) + 1; + let level_up = self.relationship_level > old_level; + + // ่จ˜ๆ†ถใ‚’ๅ…ฑๆœ‰ใƒชใ‚นใƒˆใซ่ฟฝๅŠ  + if memory.priority_score >= 0.6 { + self.shared_memories.push(memory.id.clone()); + } + + self.last_interaction = Utc::now(); + + // ๅๅฟœใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’็”Ÿๆˆ + let message = self.generate_reaction_message(memory, &rarity, user_type); + + CompanionReaction { + message, + affection_gained: affection_gain, + xp_gained: xp, + level_up, + new_level: self.relationship_level, + current_affection: self.affection_score, + special_event: self.check_special_event(), + } + } + + /// ่จ˜ๆ†ถใซๅŸบใฅใๅๅฟœใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’็”Ÿๆˆ + fn generate_reaction_message(&self, memory: &Memory, rarity: &MemoryRarity, user_type: &DiagnosisType) -> String { + let content_preview = if memory.content.len() > 50 { + format!("{}...", &memory.content[..50]) + } else { + memory.content.clone() + }; + + match (rarity, &self.personality) { + // LEGENDARYๅๅฟœ + (MemoryRarity::Legendary, CompanionPersonality::Energetic) => { + format!( + "ใ™ใ”ใ„๏ผใ€Œ{}ใ€ใฃใฆๆœฌๅฝ“ใซ็ด ๆ™ดใ‚‰ใ—ใ„ใ‚ขใ‚คใƒ‡ใ‚ขใ ใญ๏ผ\n\ + ไธ€็ท’ใซๅฎŸ็พใ•ใ›ใ‚ˆใ†๏ผใƒฏใ‚ฏใƒฏใ‚ฏใ™ใ‚‹ใ‚ˆ๏ผ", + content_preview + ) + } + (MemoryRarity::Legendary, CompanionPersonality::Intellectual) => { + format!( + "ใ€Œ{}ใ€ใจใ„ใ†่€ƒใˆใ€ใจใฆใ‚‚่ˆˆๅ‘ณๆทฑใ„ใ‚ใ€‚\n\ + ๆทฑใ„ๆดžๅฏŸๅŠ›ใ‚’ๆ„Ÿใ˜ใ‚‹ใฎใ€‚ใ‚‚ใฃใจ่ฉณใ—ใ่žใ‹ใ›ใฆ๏ผŸ", + content_preview + ) + } + (MemoryRarity::Legendary, CompanionPersonality::Practical) => { + format!( + "ใ€Œ{}ใ€ใ‹ใ€‚ๅฎŸ็พๅฏ่ƒฝๆ€งใŒ้ซ˜ใใ†ใ ใญใ€‚\n\ + ๅ…ทไฝ“็š„ใช่จˆ็”ปใ‚’ไธ€็ท’ใซ็ซ‹ใฆใ‚ˆใ†ใ‚ˆใ€‚", + content_preview + ) + } + (MemoryRarity::Legendary, CompanionPersonality::Dreamy) => { + format!( + "ใ€Œ{}ใ€...็ด ๆ•ตโ™ช ใพใ‚‹ใงๅคขใฟใŸใ„ใ€‚\n\ + ใ‚ใชใŸใฎๆƒณๅƒๅŠ›ใ€ๆœฌๅฝ“ใซๅฅฝใใ‚ˆใ€‚", + content_preview + ) + } + + // EPICๅๅฟœ + (MemoryRarity::Epic, _) => { + format!( + "ใŠใŠใ€ใ€Œ{}ใ€ใฃใฆ้ข็™ฝใ„ใญ๏ผ\n\ + ใ‚ใชใŸใฎใใ†ใ„ใ†ใจใ“ใ‚ใ€ๅฅฝใใ ใชใ€‚", + content_preview + ) + } + + // RAREๅๅฟœ + (MemoryRarity::Rare, _) => { + format!( + "ใ€Œ{}ใ€ใ‹ใ€‚ใชใ‚‹ใปใฉใญใ€‚\n\ + ใใ†ใ„ใ†่ฆ–็‚นใ€ๅ‚่€ƒใซใชใ‚‹ใ‚ˆใ€‚", + content_preview + ) + } + + // ้€šๅธธๅๅฟœ + _ => { + format!( + "ใ€Œ{}ใ€ใซใคใ„ใฆ่€ƒใˆใฆใ‚‹ใ‚“ใ ใญใ€‚\n\ + ใ„ใคใ‚‚่‰ฒใ€…่€ƒใˆใฆใฆๅฐŠๆ•ฌใ™ใ‚‹ใ‚ˆใ€‚", + content_preview + ) + } + } + } + + /// ใ‚นใƒšใ‚ทใƒฃใƒซใ‚คใƒ™ใƒณใƒˆใƒใ‚งใƒƒใ‚ฏ + fn check_special_event(&self) -> Option { + // ๅฅฝๆ„ŸๅบฆMAXใ‚คใƒ™ใƒณใƒˆ + if self.affection_score >= 1.0 { + return Some(SpecialEvent::MaxAffection); + } + + // ใƒฌใƒ™ใƒซ10ๅˆฐ้” + if self.relationship_level == 10 { + return Some(SpecialEvent::Level10); + } + + // ไฟก้ ผๅบฆMAX + if self.trust_level >= 100 { + return Some(SpecialEvent::MaxTrust); + } + + None + } + + /// ใƒ‡ใ‚คใƒชใƒผใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’็”Ÿๆˆ + pub fn generate_daily_message(&self) -> String { + let messages = match &self.personality { + CompanionPersonality::Energetic => vec![ + "ใŠใฏใ‚ˆใ†๏ผไปŠๆ—ฅใฏไฝ•ใ‹้ข็™ฝใ„ใ“ใจใ‚ใ‚‹๏ผŸ", + "ใญใˆใญใˆใ€ไปŠๆ—ฅใฏไธ€็ท’ใซๆ–ฐใ—ใ„ใ“ใจใ‚„ใ‚ใ†ใ‚ˆ๏ผ", + "ไปŠๆ—ฅใ‚‚ๅ…ƒๆฐ—ๅ‡บใ—ใฆใ„ใ“ใƒผ๏ผ", + ], + CompanionPersonality::Intellectual => vec![ + "ใŠใฏใ‚ˆใ†ใ€‚ไปŠๆ—ฅใฏใฉใ‚“ใช็™บ่ฆ‹ใŒใ‚ใ‚‹ใ‹ใ—ใ‚‰๏ผŸ", + "ๆœ€่ฟ‘่ชญใ‚“ใ ๆœฌใฎ่ฉฑใ€่žใ‹ใ›ใฆใใ‚Œใชใ„๏ผŸ", + "ไปŠๆ—ฅใ‚‚ไธ€็ท’ใซๅญฆใณใพใ—ใ‚‡ใ†ใ€‚", + ], + CompanionPersonality::Practical => vec![ + "ใŠใฏใ‚ˆใ†ใ€‚ไปŠๆ—ฅใฎไบˆๅฎšใฏ๏ผŸ", + "ใ‚„ใ‚‹ใ“ใจใƒชใ‚นใƒˆใ€ไธ€็ท’ใซ็ขบ่ชใ—ใ‚ˆใ†ใ‹ใ€‚", + "ไปŠๆ—ฅใ‚‚ๅŠน็އใ‚ˆใใ„ใ“ใ†ใญใ€‚", + ], + CompanionPersonality::Dreamy => vec![ + "ใŠใฏใ‚ˆใ†...ใพใ ๅคขใฎ็ถšใ่ฆ‹ใฆใŸใฎใ€‚", + "ไปŠๆ—ฅใฏใฉใ‚“ใช็ด ๆ•ตใชใ“ใจใŒ่ตทใ“ใ‚‹ใ‹ใชโ™ช", + "ใ‚ใชใŸใจ้Žใ”ใ™ๆ™‚้–“ใ€ๅคงๅฅฝใใ€‚", + ], + CompanionPersonality::Balanced => vec![ + "ใŠใฏใ‚ˆใ†ใ€‚ไปŠๆ—ฅใ‚‚้ ‘ๅผตใ‚ใ†ใญใ€‚", + "ไฝ•ใ‹ๆ‰‹ไผใˆใ‚‹ใ“ใจใ‚ใ‚‹๏ผŸ", + "ไปŠๆ—ฅใ‚‚ไธ€็ท’ใซใ„ใ‚‰ใ‚Œใฆๅฌ‰ใ—ใ„ใ‚ˆใ€‚", + ], + }; + + let today = chrono::Utc::now().ordinal(); + messages[today as usize % messages.len()].to_string() + } +} + +/// ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใฎๅๅฟœ +#[derive(Debug, Serialize)] +pub struct CompanionReaction { + pub message: String, + pub affection_gained: f32, + pub xp_gained: u32, + pub level_up: bool, + pub new_level: u32, + pub current_affection: f32, + pub special_event: Option, +} + +/// ใ‚นใƒšใ‚ทใƒฃใƒซใ‚คใƒ™ใƒณใƒˆ +#[derive(Debug, Serialize)] +pub enum SpecialEvent { + MaxAffection, // ๅฅฝๆ„ŸๅบฆMAX + Level10, // ใƒฌใƒ™ใƒซ10ๅˆฐ้” + MaxTrust, // ไฟก้ ผๅบฆMAX + FirstDate, // ๅˆใƒ‡ใƒผใƒˆ + Confession, // ๅ‘Š็™ฝ +} + +impl SpecialEvent { + pub fn message(&self, companion_name: &str) -> String { + match self { + SpecialEvent::MaxAffection => { + format!( + "๐Ÿ’• ็‰นๅˆฅใชใ‚คใƒ™ใƒณใƒˆ็™บ็”Ÿ๏ผ\n\n\ + {}:ใ€Œใญใˆ...ใ‚ใฎใญใ€‚\n\ + ใ€€ใ€€ใ€€ใ„ใคใ‚‚ไธ€็ท’ใซใ„ใฆใใ‚Œใฆใ‚ใ‚ŠใŒใจใ†ใ€‚\n\ + ใ€€ใ€€ใ€€ใ‚ใชใŸใฎใ“ใจใ€ใ™ใ”ใๅคงๅˆ‡ใซๆ€ใฃใฆใ‚‹ใฎใ€‚\n\ + ใ€€ใ€€ใ€€ใ“ใ‚Œใ‹ใ‚‰ใ‚‚ใ€ใšใฃใจไธ€็ท’ใซใ„ใฆใญ๏ผŸใ€\n\n\ + ๐ŸŽŠ {} ใฎๅฅฝๆ„ŸๅบฆใŒMAXใซใชใ‚Šใพใ—ใŸ๏ผ", + companion_name, companion_name + ) + } + SpecialEvent::Level10 => { + format!( + "๐ŸŽ‰ ใƒฌใƒ™ใƒซ10ๅˆฐ้”๏ผ\n\n\ + {}:ใ€Œใ“ใ“ใพใงไธ€็ท’ใซๆฅใ‚‰ใ‚ŒใŸใญใ€‚\n\ + ใ€€ใ€€ใ€€ใ‚ใชใŸใจใชใ‚‰ใ€ใฉใ“ใพใงใ‚‚่กŒใ‘ใใ†ใ€‚ใ€", + companion_name + ) + } + SpecialEvent::MaxTrust => { + format!( + "โœจ ไฟก้ ผๅบฆMAX๏ผ\n\n\ + {}:ใ€Œใ‚ใชใŸใฎใ“ใจใ€ๅฟƒใ‹ใ‚‰ไฟก้ ผใ—ใฆใ‚‹ใ€‚\n\ + ใ€€ใ€€ใ€€ไฝ•ใงใ‚‚่ฉฑใ›ใ‚‹ใฃใฆใ€ใ™ใ”ใๅฌ‰ใ—ใ„ใ‚ˆใ€‚ใ€", + companion_name + ) + } + SpecialEvent::FirstDate => { + format!( + "๐Ÿ’ ๅˆใƒ‡ใƒผใƒˆใ‚คใƒ™ใƒณใƒˆ๏ผ\n\n\ + {}:ใ€ŒไปŠๅบฆใ€ไบŒไบบใงใฉใ“ใ‹่กŒใ‹ใชใ„๏ผŸใ€", + companion_name + ) + } + SpecialEvent::Confession => { + format!( + "๐Ÿ’ ๅ‘Š็™ฝใ‚คใƒ™ใƒณใƒˆ๏ผ\n\n\ + {}:ใ€Œๅฅฝใใงใ™ใ€‚ไป˜ใๅˆใฃใฆใใ ใ•ใ„ใ€‚ใ€", + companion_name + ) + } + } + } +} + +/// ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใƒ•ใ‚ฉใƒผใƒžใƒƒใ‚ฟใƒผ +pub struct CompanionFormatter; + +impl CompanionFormatter { + /// ๅๅฟœใ‚’่กจ็คบ + pub fn format_reaction(companion: &Companion, reaction: &CompanionReaction) -> String { + let affection_bar = Self::format_affection_bar(reaction.current_affection); + let level_up_text = if reaction.level_up { + format!("\n๐ŸŽŠ ใƒฌใƒ™ใƒซใ‚ขใƒƒใƒ—๏ผ Lv.{} โ†’ Lv.{}", reaction.new_level - 1, reaction.new_level) + } else { + String::new() + }; + + let special_event_text = if let Some(ref event) = reaction.special_event { + format!("\n\n{}", event.message(&companion.name)) + } else { + String::new() + }; + + format!( + r#" +โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•— +โ•‘ ๐Ÿ’• {} ใฎๅๅฟœ โ•‘ +โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +{} {}: +ใ€Œ{}ใ€ + +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +๐Ÿ’• ๅฅฝๆ„Ÿๅบฆ: {} (+{:.1}%) +๐Ÿ’Ž XP็ฒๅพ—: +{} XP{} +๐Ÿ† ใƒฌใƒ™ใƒซ: Lv.{} +๐Ÿค ไฟก้ ผๅบฆ: {} / 100 +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”{} +"#, + companion.name, + companion.personality.emoji(), + companion.name, + reaction.message, + affection_bar, + reaction.affection_gained * 100.0, + reaction.xp_gained, + level_up_text, + companion.relationship_level, + companion.trust_level, + special_event_text + ) + } + + /// ใƒ—ใƒญใƒ•ใ‚ฃใƒผใƒซ่กจ็คบ + pub fn format_profile(companion: &Companion) -> String { + let affection_bar = Self::format_affection_bar(companion.affection_score); + + format!( + r#" +โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•— +โ•‘ ๐Ÿ’• {} ใฎใƒ—ใƒญใƒ•ใ‚ฃใƒผใƒซ โ•‘ +โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +{} ๆ€งๆ ผ: {} + +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +๐Ÿ“Š ใ‚นใƒ†ใƒผใ‚ฟใ‚น +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +๐Ÿ† ้–ขไฟ‚ใƒฌใƒ™ใƒซ: Lv.{} +๐Ÿ’• ๅฅฝๆ„Ÿๅบฆ: {} +๐Ÿค ไฟก้ ผๅบฆ: {} / 100 +๐Ÿ’Ž ็ทXP: {} XP +๐Ÿ“š ๅ…ฑๆœ‰่จ˜ๆ†ถ: {}ๅ€‹ +๐Ÿ• ๆœ€็ต‚ไบคๆต: {} +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + +๐Ÿ’ฌ ไปŠๆ—ฅใฎใฒใจใ“ใจ: +ใ€Œ{}ใ€ +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +"#, + companion.name, + companion.personality.emoji(), + companion.personality.name(), + companion.relationship_level, + affection_bar, + companion.trust_level, + companion.total_xp, + companion.shared_memories.len(), + companion.last_interaction.format("%Y-%m-%d %H:%M"), + companion.generate_daily_message() + ) + } + + fn format_affection_bar(affection: f32) -> String { + let hearts = (affection * 10.0) as usize; + let filled = "โค๏ธ".repeat(hearts); + let empty = "๐Ÿค".repeat(10 - hearts); + format!("{}{} {:.0}%", filled, empty, affection * 100.0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_companion_creation() { + let companion = Companion::new( + "ใ‚จใƒŸใƒชใƒผ".to_string(), + CompanionPersonality::Energetic, + ); + assert_eq!(companion.name, "ใ‚จใƒŸใƒชใƒผ"); + assert_eq!(companion.relationship_level, 1); + assert_eq!(companion.affection_score, 0.0); + } + + #[test] + fn test_compatibility() { + let personality = CompanionPersonality::Energetic; + let innovator = DiagnosisType::Innovator; + assert_eq!(personality.compatibility(&innovator), 0.95); + } +} diff --git a/src/game_formatter.rs b/src/game_formatter.rs index 60ebdfc..86e84bf 100644 --- a/src/game_formatter.rs +++ b/src/game_formatter.rs @@ -64,6 +64,17 @@ pub enum DiagnosisType { } impl DiagnosisType { + /// ใ‚นใ‚ณใ‚ขใ‹ใ‚‰่จบๆ–ญใ‚ฟใ‚คใƒ—ใ‚’ๆŽจๅฎš๏ผˆๅ…ฌ้–‹็”จ๏ผ‰ + pub fn from_memory(memory: &crate::memory::Memory) -> Self { + // ใ‚นใ‚ณใ‚ขๅ†…่จณใ‚’ๆŽจๅฎš + let emotional = (memory.priority_score * 0.25).min(0.25); + let relevance = (memory.priority_score * 0.25).min(0.25); + let novelty = (memory.priority_score * 0.25).min(0.25); + let utility = memory.priority_score - emotional - relevance - novelty; + + Self::from_score_breakdown(emotional, relevance, novelty, utility) + } + pub fn from_score_breakdown( emotional: f32, relevance: f32, diff --git a/src/lib.rs b/src/lib.rs index 6eaa46f..b39bd0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod memory; pub mod mcp; pub mod ai_interpreter; -pub mod game_formatter; \ No newline at end of file +pub mod game_formatter; +pub mod companion; \ No newline at end of file diff --git a/src/mcp/base.rs b/src/mcp/base.rs index 14621a2..243c5d9 100644 --- a/src/mcp/base.rs +++ b/src/mcp/base.rs @@ -3,16 +3,22 @@ use serde_json::{json, Value}; use std::io::{self, BufRead, Write}; use crate::memory::MemoryManager; -use crate::game_formatter::GameFormatter; +use crate::game_formatter::{GameFormatter, DiagnosisType}; +use crate::companion::{Companion, CompanionPersonality, CompanionFormatter}; +use std::sync::{Arc, Mutex}; pub struct BaseMCPServer { pub memory_manager: MemoryManager, + pub companion: Option, // ๆ‹ๆ„›ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณ๏ผˆใ‚ชใƒ—ใ‚ทใƒงใƒณ๏ผ‰ } impl BaseMCPServer { pub async fn new() -> Result { let memory_manager = MemoryManager::new().await?; - Ok(BaseMCPServer { memory_manager }) + Ok(BaseMCPServer { + memory_manager, + companion: None, // ๅˆๆœŸ็Šถๆ…‹ใฏใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใชใ— + }) } pub async fn run(&mut self) -> Result<()> { @@ -210,6 +216,47 @@ impl BaseMCPServer { "type": "object", "properties": {} } + }), + json!({ + "name": "create_companion", + "description": "Create your AI companion - Choose name and personality!", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Companion's name" + }, + "personality": { + "type": "string", + "enum": ["energetic", "intellectual", "practical", "dreamy", "balanced"], + "description": "Companion's personality type" + } + }, + "required": ["name", "personality"] + } + }), + json!({ + "name": "companion_react", + "description": "Show your companion's reaction to your latest memory", + "inputSchema": { + "type": "object", + "properties": { + "memory_id": { + "type": "string", + "description": "Memory ID to react to" + } + }, + "required": ["memory_id"] + } + }), + json!({ + "name": "companion_profile", + "description": "View your companion's profile and stats", + "inputSchema": { + "type": "object", + "properties": {} + } }) ] } @@ -240,6 +287,9 @@ impl BaseMCPServer { "create_memory_with_ai" => self.tool_create_memory_with_ai(arguments).await, "list_memories_by_priority" => self.tool_list_memories_by_priority(arguments), "daily_challenge" => self.tool_daily_challenge(), + "create_companion" => self.tool_create_companion(arguments), + "companion_react" => self.tool_companion_react(arguments), + "companion_profile" => self.tool_companion_profile(), "search_memories" => self.tool_search_memories(arguments), "update_memory" => self.tool_update_memory(arguments), "delete_memory" => self.tool_delete_memory(arguments), @@ -424,6 +474,80 @@ impl BaseMCPServer { }) } + // ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณไฝœๆˆ + fn tool_create_companion(&mut self, arguments: &Value) -> Value { + let name = arguments["name"].as_str().unwrap_or("ใ‚จใƒŸใƒชใƒผ"); + let personality_str = arguments["personality"].as_str().unwrap_or("balanced"); + + let personality = match personality_str { + "energetic" => CompanionPersonality::Energetic, + "intellectual" => CompanionPersonality::Intellectual, + "practical" => CompanionPersonality::Practical, + "dreamy" => CompanionPersonality::Dreamy, + _ => CompanionPersonality::Balanced, + }; + + let companion = Companion::new(name.to_string(), personality); + let profile = CompanionFormatter::format_profile(&companion); + + self.companion = Some(companion); + + json!({ + "success": true, + "profile": profile, + "message": format!("{}ใŒใ‚ใชใŸใฎใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใซใชใ‚Šใพใ—ใŸ๏ผ", name) + }) + } + + // ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใฎๅๅฟœ + fn tool_companion_react(&mut self, arguments: &Value) -> Value { + if self.companion.is_none() { + return json!({ + "success": false, + "error": "ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใŒไฝœๆˆใ•ใ‚Œใฆใ„ใพใ›ใ‚“ใ€‚create_companionใƒ„ใƒผใƒซใงไฝœๆˆใ—ใฆใใ ใ•ใ„ใ€‚" + }); + } + + let memory_id = arguments["memory_id"].as_str().unwrap_or(""); + + if let Some(memory) = self.memory_manager.get_memory(memory_id) { + let user_type = DiagnosisType::from_memory(memory); + let companion = self.companion.as_mut().unwrap(); + let reaction = companion.react_to_memory(memory, &user_type); + let reaction_display = CompanionFormatter::format_reaction(companion, &reaction); + + json!({ + "success": true, + "reaction_display": reaction_display, + "affection_gained": reaction.affection_gained, + "xp_gained": reaction.xp_gained, + "level_up": reaction.level_up, + "message": "ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใŒๅๅฟœใ—ใพใ—ใŸ๏ผ" + }) + } else { + json!({ + "success": false, + "error": format!("Memory not found: {}", memory_id) + }) + } + } + + // ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใƒ—ใƒญใƒ•ใ‚ฃใƒผใƒซ + fn tool_companion_profile(&self) -> Value { + if let Some(ref companion) = self.companion { + let profile = CompanionFormatter::format_profile(companion); + json!({ + "success": true, + "profile": profile + }) + } else { + json!({ + "success": false, + "error": "ใ‚ณใƒณใƒ‘ใƒ‹ใ‚ชใƒณใŒไฝœๆˆใ•ใ‚Œใฆใ„ใพใ›ใ‚“ใ€‚create_companionใƒ„ใƒผใƒซใงไฝœๆˆใ—ใฆใใ ใ•ใ„ใ€‚" + }) + } + } + // ไธๆ˜Žใชใƒกใ‚ฝใƒƒใƒ‰ใƒใƒณใƒ‰ใƒฉ fn handle_unknown_method(&self, id: Value) -> Value { json!({