Compare commits
6 Commits
61a13d70df
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8951544ad3 | ||
|
|
49f41c1b44 | ||
|
|
46249fb10c | ||
|
|
2579312029 | ||
|
|
2abcc957ef | ||
|
|
8037477104 |
12
README.md
12
README.md
@@ -4,6 +4,8 @@ AI memory system with psychological analysis for Claude via MCP.
|
|||||||
|
|
||||||
**Current: Layers 1-4 Complete** - Memory storage, AI interpretation, personality analysis, integrated profile, and relationship inference.
|
**Current: Layers 1-4 Complete** - Memory storage, AI interpretation, personality analysis, integrated profile, and relationship inference.
|
||||||
|
|
||||||
|
**Planned: Layer 5** - Knowledge sharing platform combining useful insights with author personality.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### Layer 1: Pure Memory Storage
|
### Layer 1: Pure Memory Storage
|
||||||
@@ -34,6 +36,12 @@ AI memory system with psychological analysis for Claude via MCP.
|
|||||||
- 🎮 **Game Ready**: Foundation for companion apps, games, VTubers
|
- 🎮 **Game Ready**: Foundation for companion apps, games, VTubers
|
||||||
- 🔒 **Opt-in**: Enable only when needed with `--enable-layer4` flag
|
- 🔒 **Opt-in**: Enable only when needed with `--enable-layer4` flag
|
||||||
|
|
||||||
|
### Layer 5: Knowledge Sharing (Planned)
|
||||||
|
- 💡 **Information + Personality**: Share AI interactions with context
|
||||||
|
- 🌐 **SNS for AI Era**: Useful insights combined with author's unique perspective
|
||||||
|
- 🔒 **Privacy-First**: Share essence, not raw data
|
||||||
|
- 📊 **Showcase**: Display how AI understands you
|
||||||
|
|
||||||
### General
|
### General
|
||||||
- 🛠️ **MCP Integration**: Works seamlessly with Claude Code
|
- 🛠️ **MCP Integration**: Works seamlessly with Claude Code
|
||||||
- 🧪 **Well-tested**: Comprehensive test coverage
|
- 🧪 **Well-tested**: Comprehensive test coverage
|
||||||
@@ -223,8 +231,8 @@ Multi-layer system design:
|
|||||||
- **Layer 3** ✅ Complete: Big Five personality analysis
|
- **Layer 3** ✅ Complete: Big Five personality analysis
|
||||||
- **Layer 3.5** ✅ Complete: Integrated profile (unified summary)
|
- **Layer 3.5** ✅ Complete: Integrated profile (unified summary)
|
||||||
- **Layer 4** ✅ Complete: Relationship inference (optional, `--enable-layer4`)
|
- **Layer 4** ✅ Complete: Relationship inference (optional, `--enable-layer4`)
|
||||||
- **Layer 4+** 🔵 Future: Extended game/companion features
|
- **Layer 4+** 🔵 Planned: Extended game/companion features
|
||||||
- **Layer 5** 🔵 Future: Distribution and sharing
|
- **Layer 5** 🔵 Planned: Knowledge sharing (information + personality)
|
||||||
|
|
||||||
**Design Philosophy**:
|
**Design Philosophy**:
|
||||||
- **"Internal complexity, external simplicity"**: Simple API, complex internals
|
- **"Internal complexity, external simplicity"**: Simple API, complex internals
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ aigptは、独立したレイヤーを積み重ねる設計です。各レイヤ
|
|||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────┐
|
┌─────────────────────────────────────────┐
|
||||||
│ Layer 5: Distribution & Sharing │ 🔵 Future
|
│ Layer 5: Knowledge Sharing │ 🔵 Planned
|
||||||
│ (Game streaming, public/private) │
|
│ (Information + Personality sharing) │
|
||||||
├─────────────────────────────────────────┤
|
├─────────────────────────────────────────┤
|
||||||
│ Layer 4+: Extended Features │ 🔵 Planned
|
│ Layer 4+: Extended Features │ 🔵 Planned
|
||||||
│ (Advanced game/companion systems) │
|
│ (Advanced game/companion systems) │
|
||||||
@@ -362,10 +362,16 @@ if user.extraversion < 0.5 {
|
|||||||
|
|
||||||
### Design Philosophy
|
### Design Philosophy
|
||||||
|
|
||||||
**推測のみ、保存なし**:
|
**推測ベース + 短期キャッシング**:
|
||||||
- 毎回Layer 1-3.5から計算
|
- 毎回Layer 1-3.5から計算
|
||||||
- キャッシュなし(シンプルさ優先)
|
- 5分間の短期キャッシュで負荷軽減
|
||||||
- 後でキャッシング追加可能
|
- メモリ更新時にキャッシュ無効化
|
||||||
|
|
||||||
|
**キャッシング戦略**:
|
||||||
|
- SQLiteテーブル(`relationship_cache`)に保存
|
||||||
|
- 個別エンティティ: `get_relationship(entity_id)`
|
||||||
|
- 全体リスト: `list_relationships()`
|
||||||
|
- メモリ作成/更新/削除時に自動クリア
|
||||||
|
|
||||||
**独立性**:
|
**独立性**:
|
||||||
- Layer 1-3.5に依存
|
- Layer 1-3.5に依存
|
||||||
@@ -460,18 +466,104 @@ pub struct Companion {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Layer 5: Distribution (Future)
|
## Layer 5: Knowledge Sharing (Planned)
|
||||||
|
|
||||||
**Status**: 🔵 **Future Consideration**
|
**Status**: 🔵 **Planned**
|
||||||
|
|
||||||
### Purpose
|
### Purpose
|
||||||
ゲーム配信や共有機能。
|
AIとのやり取りを「情報 + 個性」として共有する。SNSや配信のように、**有用な知見**と**作者の個性**を両立させたコンテンツプラットフォーム。
|
||||||
|
|
||||||
### Ideas
|
### Design Philosophy
|
||||||
- Share memory rankings
|
|
||||||
- Export as shareable format
|
人々が求めるもの:
|
||||||
- Public/private memory modes
|
1. **情報価値**: 「このプロンプトでこんな結果が得られた」「この問題をAIでこう解決した」
|
||||||
- Integration with streaming platforms
|
2. **個性・共感**: 「この人はこういう人だ」という親近感、信頼
|
||||||
|
|
||||||
|
SNSや配信と同じく、**情報のみは無機質**、**個性のみは空虚**。両方を組み合わせることで価値が生まれる。
|
||||||
|
|
||||||
|
### Data Model
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub struct SharedInteraction {
|
||||||
|
pub id: String,
|
||||||
|
|
||||||
|
// 情報価値
|
||||||
|
pub problem: String, // 何を解決しようとしたか
|
||||||
|
pub approach: String, // AIとどうやり取りしたか
|
||||||
|
pub result: String, // 何を得たか
|
||||||
|
pub usefulness_score: f32, // 有用性 (0.0-1.0, priority_score由来)
|
||||||
|
pub tags: Vec<String>, // 検索用タグ
|
||||||
|
|
||||||
|
// 個性
|
||||||
|
pub author_profile: ShareableProfile, // 作者の本質
|
||||||
|
pub why_this_matters: String, // なぜこの人がこれに取り組んだか
|
||||||
|
|
||||||
|
// メタデータ
|
||||||
|
pub views: u32,
|
||||||
|
pub useful_count: u32, // 「役に立った」カウント
|
||||||
|
pub created_at: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ShareableProfile {
|
||||||
|
// ユーザーの本質(Layer 3.5から抽出)
|
||||||
|
pub personality_essence: Vec<TraitScore>, // Top 3 traits
|
||||||
|
pub core_interests: Vec<String>, // 5個
|
||||||
|
pub core_values: Vec<String>, // 5個
|
||||||
|
|
||||||
|
// AIの解釈
|
||||||
|
pub ai_perspective: String, // AIがこのユーザーをどう理解しているか
|
||||||
|
pub confidence: f32, // データ品質 (0.0-1.0)
|
||||||
|
|
||||||
|
// 関係性スタイル(Layer 4から推測、匿名化)
|
||||||
|
pub relationship_style: String, // 例: "深く狭い繋がりを好む"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Privacy Design
|
||||||
|
|
||||||
|
**共有するもの:**
|
||||||
|
- ✅ 本質(Layer 3.5の統合プロファイル)
|
||||||
|
- ✅ パターン(関係性スタイル、思考パターン)
|
||||||
|
- ✅ 有用な知見(問題解決のアプローチ)
|
||||||
|
|
||||||
|
**共有しないもの:**
|
||||||
|
- ❌ 生の会話内容(Layer 1-2)
|
||||||
|
- ❌ 個人を特定できる情報
|
||||||
|
- ❌ メモリID、タイムスタンプ等の生データ
|
||||||
|
|
||||||
|
### Use Cases
|
||||||
|
|
||||||
|
**1. AI時代のGitHub Gist**
|
||||||
|
- 有用なプロンプトとその結果を共有
|
||||||
|
- 作者の個性とアプローチが見える
|
||||||
|
- 「この人の考え方が参考になる」
|
||||||
|
|
||||||
|
**2. 知見のSNS**
|
||||||
|
- 情報を発信しながら、個性も伝わる
|
||||||
|
- フォロー、「役に立った」機能
|
||||||
|
- 関心領域でフィルタリング
|
||||||
|
|
||||||
|
**3. AIペルソナのショーケース**
|
||||||
|
- 「AIは私をこう理解している」を共有
|
||||||
|
- 性格分析の精度を比較
|
||||||
|
- コミュニティでの自己表現
|
||||||
|
|
||||||
|
### Implementation Ideas
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Layer 5のMCPツール
|
||||||
|
- create_shareable_interaction() - 知見を共有形式で作成
|
||||||
|
- get_shareable_profile() - 共有可能なプロファイルを生成
|
||||||
|
- export_interaction() - JSON/Markdown形式でエクスポート
|
||||||
|
- anonymize_data() - プライバシー保護処理
|
||||||
|
```
|
||||||
|
|
||||||
|
### Future Platforms
|
||||||
|
|
||||||
|
- Web UI: 知見を閲覧・検索・共有
|
||||||
|
- API: 外部サービスと連携
|
||||||
|
- RSS/Atom: フィード配信
|
||||||
|
- Markdown Export: ブログ投稿用
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ pub enum MemoryError {
|
|||||||
|
|
||||||
#[error("Configuration error: {0}")]
|
#[error("Configuration error: {0}")]
|
||||||
Config(String),
|
Config(String),
|
||||||
|
|
||||||
|
#[error("Parse error: {0}")]
|
||||||
|
Parse(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, MemoryError>;
|
pub type Result<T> = std::result::Result<T, MemoryError>;
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ pub use analysis::UserAnalysis;
|
|||||||
pub use error::{MemoryError, Result};
|
pub use error::{MemoryError, Result};
|
||||||
pub use memory::Memory;
|
pub use memory::Memory;
|
||||||
pub use profile::{UserProfile, TraitScore};
|
pub use profile::{UserProfile, TraitScore};
|
||||||
pub use relationship::{RelationshipInference, infer_all_relationships};
|
pub use relationship::{RelationshipInference, infer_all_relationships, get_relationship};
|
||||||
pub use store::MemoryStore;
|
pub use store::MemoryStore;
|
||||||
|
|||||||
@@ -184,6 +184,11 @@ impl RelationshipInference {
|
|||||||
pub fn infer_all_relationships(
|
pub fn infer_all_relationships(
|
||||||
store: &MemoryStore,
|
store: &MemoryStore,
|
||||||
) -> Result<Vec<RelationshipInference>> {
|
) -> Result<Vec<RelationshipInference>> {
|
||||||
|
// Check cache first
|
||||||
|
if let Some(cached) = store.get_cached_all_relationships()? {
|
||||||
|
return Ok(cached);
|
||||||
|
}
|
||||||
|
|
||||||
// Get all memories
|
// Get all memories
|
||||||
let memories = store.list()?;
|
let memories = store.list()?;
|
||||||
|
|
||||||
@@ -219,9 +224,41 @@ pub fn infer_all_relationships(
|
|||||||
.unwrap_or(std::cmp::Ordering::Equal)
|
.unwrap_or(std::cmp::Ordering::Equal)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Cache the result
|
||||||
|
store.save_all_relationships_cache(&relationships)?;
|
||||||
|
|
||||||
Ok(relationships)
|
Ok(relationships)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get relationship inference for a specific entity (with caching)
|
||||||
|
pub fn get_relationship(
|
||||||
|
store: &MemoryStore,
|
||||||
|
entity_id: &str,
|
||||||
|
) -> Result<RelationshipInference> {
|
||||||
|
// Check cache first
|
||||||
|
if let Some(cached) = store.get_cached_relationship(entity_id)? {
|
||||||
|
return Ok(cached);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all memories
|
||||||
|
let memories = store.list()?;
|
||||||
|
|
||||||
|
// Get user profile
|
||||||
|
let user_profile = store.get_profile()?;
|
||||||
|
|
||||||
|
// Infer relationship
|
||||||
|
let relationship = RelationshipInference::infer(
|
||||||
|
entity_id.to_string(),
|
||||||
|
&memories,
|
||||||
|
&user_profile,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cache it
|
||||||
|
store.save_relationship_cache(entity_id, &relationship)?;
|
||||||
|
|
||||||
|
Ok(relationship)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -102,6 +102,17 @@ impl MemoryStore {
|
|||||||
[],
|
[],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Create relationship_cache table (Layer 4 - relationship inference cache)
|
||||||
|
// entity_id = "" for all_relationships cache
|
||||||
|
conn.execute(
|
||||||
|
"CREATE TABLE IF NOT EXISTS relationship_cache (
|
||||||
|
entity_id TEXT PRIMARY KEY,
|
||||||
|
data TEXT NOT NULL,
|
||||||
|
cached_at TEXT NOT NULL
|
||||||
|
)",
|
||||||
|
[],
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(Self { conn })
|
Ok(Self { conn })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,6 +148,10 @@ impl MemoryStore {
|
|||||||
memory.updated_at.to_rfc3339(),
|
memory.updated_at.to_rfc3339(),
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Clear relationship cache since memory data changed
|
||||||
|
self.clear_relationship_cache()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +219,9 @@ impl MemoryStore {
|
|||||||
return Err(MemoryError::NotFound(memory.id.clone()));
|
return Err(MemoryError::NotFound(memory.id.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear relationship cache since memory data changed
|
||||||
|
self.clear_relationship_cache()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,6 +235,9 @@ impl MemoryStore {
|
|||||||
return Err(MemoryError::NotFound(id.to_string()));
|
return Err(MemoryError::NotFound(id.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear relationship cache since memory data changed
|
||||||
|
self.clear_relationship_cache()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,6 +485,123 @@ impl MemoryStore {
|
|||||||
|
|
||||||
Ok(profile)
|
Ok(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== Layer 4: Relationship Cache Methods ==========
|
||||||
|
|
||||||
|
/// Cache duration in minutes
|
||||||
|
const RELATIONSHIP_CACHE_DURATION_MINUTES: i64 = 5;
|
||||||
|
|
||||||
|
/// Save relationship inference to cache
|
||||||
|
pub fn save_relationship_cache(
|
||||||
|
&self,
|
||||||
|
entity_id: &str,
|
||||||
|
relationship: &super::relationship::RelationshipInference,
|
||||||
|
) -> Result<()> {
|
||||||
|
let data = serde_json::to_string(relationship)?;
|
||||||
|
let cached_at = Utc::now().to_rfc3339();
|
||||||
|
|
||||||
|
self.conn.execute(
|
||||||
|
"INSERT OR REPLACE INTO relationship_cache (entity_id, data, cached_at) VALUES (?1, ?2, ?3)",
|
||||||
|
params![entity_id, data, cached_at],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get cached relationship inference
|
||||||
|
pub fn get_cached_relationship(
|
||||||
|
&self,
|
||||||
|
entity_id: &str,
|
||||||
|
) -> Result<Option<super::relationship::RelationshipInference>> {
|
||||||
|
let mut stmt = self
|
||||||
|
.conn
|
||||||
|
.prepare("SELECT data, cached_at FROM relationship_cache WHERE entity_id = ?1")?;
|
||||||
|
|
||||||
|
let result = stmt.query_row([entity_id], |row| {
|
||||||
|
let data: String = row.get(0)?;
|
||||||
|
let cached_at: String = row.get(1)?;
|
||||||
|
Ok((data, cached_at))
|
||||||
|
});
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok((data, cached_at_str)) => {
|
||||||
|
// Check if cache is still valid (within 5 minutes)
|
||||||
|
let cached_at = DateTime::parse_from_rfc3339(&cached_at_str)
|
||||||
|
.map_err(|e| MemoryError::Parse(e.to_string()))?
|
||||||
|
.with_timezone(&Utc);
|
||||||
|
|
||||||
|
let age_minutes = (Utc::now() - cached_at).num_seconds() / 60;
|
||||||
|
|
||||||
|
if age_minutes < Self::RELATIONSHIP_CACHE_DURATION_MINUTES {
|
||||||
|
let relationship: super::relationship::RelationshipInference =
|
||||||
|
serde_json::from_str(&data)?;
|
||||||
|
Ok(Some(relationship))
|
||||||
|
} else {
|
||||||
|
// Cache expired
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Save all relationships list to cache (use empty string as entity_id)
|
||||||
|
pub fn save_all_relationships_cache(
|
||||||
|
&self,
|
||||||
|
relationships: &[super::relationship::RelationshipInference],
|
||||||
|
) -> Result<()> {
|
||||||
|
let data = serde_json::to_string(relationships)?;
|
||||||
|
let cached_at = Utc::now().to_rfc3339();
|
||||||
|
|
||||||
|
self.conn.execute(
|
||||||
|
"INSERT OR REPLACE INTO relationship_cache (entity_id, data, cached_at) VALUES ('', ?1, ?2)",
|
||||||
|
params![data, cached_at],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get cached all relationships list
|
||||||
|
pub fn get_cached_all_relationships(
|
||||||
|
&self,
|
||||||
|
) -> Result<Option<Vec<super::relationship::RelationshipInference>>> {
|
||||||
|
let mut stmt = self
|
||||||
|
.conn
|
||||||
|
.prepare("SELECT data, cached_at FROM relationship_cache WHERE entity_id = ''")?;
|
||||||
|
|
||||||
|
let result = stmt.query_row([], |row| {
|
||||||
|
let data: String = row.get(0)?;
|
||||||
|
let cached_at: String = row.get(1)?;
|
||||||
|
Ok((data, cached_at))
|
||||||
|
});
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok((data, cached_at_str)) => {
|
||||||
|
let cached_at = DateTime::parse_from_rfc3339(&cached_at_str)
|
||||||
|
.map_err(|e| MemoryError::Parse(e.to_string()))?
|
||||||
|
.with_timezone(&Utc);
|
||||||
|
|
||||||
|
let age_minutes = (Utc::now() - cached_at).num_seconds() / 60;
|
||||||
|
|
||||||
|
if age_minutes < Self::RELATIONSHIP_CACHE_DURATION_MINUTES {
|
||||||
|
let relationships: Vec<super::relationship::RelationshipInference> =
|
||||||
|
serde_json::from_str(&data)?;
|
||||||
|
Ok(Some(relationships))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear all relationship caches (call when memories are modified)
|
||||||
|
pub fn clear_relationship_cache(&self) -> Result<()> {
|
||||||
|
self.conn.execute("DELETE FROM relationship_cache", [])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use anyhow::Result;
|
|||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use std::io::{self, BufRead, Write};
|
use std::io::{self, BufRead, Write};
|
||||||
|
|
||||||
use crate::core::{Memory, MemoryStore, UserAnalysis, RelationshipInference, infer_all_relationships};
|
use crate::core::{Memory, MemoryStore, UserAnalysis, infer_all_relationships, get_relationship};
|
||||||
|
|
||||||
pub struct BaseMCPServer {
|
pub struct BaseMCPServer {
|
||||||
store: MemoryStore,
|
store: MemoryStore,
|
||||||
@@ -581,43 +581,26 @@ impl BaseMCPServer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get memories and user profile
|
// Get relationship (with caching)
|
||||||
let memories = match self.store.list() {
|
match get_relationship(&self.store, entity_id) {
|
||||||
Ok(m) => m,
|
Ok(relationship) => json!({
|
||||||
Err(e) => return json!({
|
"success": true,
|
||||||
"success": false,
|
"relationship": {
|
||||||
"error": format!("Failed to get memories: {}", e)
|
"entity_id": relationship.entity_id,
|
||||||
|
"interaction_count": relationship.interaction_count,
|
||||||
|
"avg_priority": relationship.avg_priority,
|
||||||
|
"days_since_last": relationship.days_since_last,
|
||||||
|
"bond_strength": relationship.bond_strength,
|
||||||
|
"relationship_type": relationship.relationship_type,
|
||||||
|
"confidence": relationship.confidence,
|
||||||
|
"inferred_at": relationship.inferred_at
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
};
|
Err(e) => json!({
|
||||||
|
|
||||||
let user_profile = match self.store.get_profile() {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => return json!({
|
|
||||||
"success": false,
|
"success": false,
|
||||||
"error": format!("Failed to get profile: {}", e)
|
"error": format!("Failed to get relationship: {}", e)
|
||||||
}),
|
}),
|
||||||
};
|
}
|
||||||
|
|
||||||
// Infer relationship
|
|
||||||
let relationship = RelationshipInference::infer(
|
|
||||||
entity_id.to_string(),
|
|
||||||
&memories,
|
|
||||||
&user_profile,
|
|
||||||
);
|
|
||||||
|
|
||||||
json!({
|
|
||||||
"success": true,
|
|
||||||
"relationship": {
|
|
||||||
"entity_id": relationship.entity_id,
|
|
||||||
"interaction_count": relationship.interaction_count,
|
|
||||||
"avg_priority": relationship.avg_priority,
|
|
||||||
"days_since_last": relationship.days_since_last,
|
|
||||||
"bond_strength": relationship.bond_strength,
|
|
||||||
"relationship_type": relationship.relationship_type,
|
|
||||||
"confidence": relationship.confidence,
|
|
||||||
"inferred_at": relationship.inferred_at
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tool_list_relationships(&self, arguments: &Value) -> Value {
|
fn tool_list_relationships(&self, arguments: &Value) -> Value {
|
||||||
|
|||||||
Reference in New Issue
Block a user