Compare commits

...

4 Commits

Author SHA1 Message Date
379307278d Fixing telegram client bug 2026-07-11 12:52:26 +07:00
3ddb03059d import tools memories_latest 2026-07-11 12:51:48 +07:00
8126258344 memories_latest tools 2026-07-11 10:42:56 +07:00
99d4f1d900 Improve ragroleplay 2026-07-11 05:59:55 +07:00
5 changed files with 92 additions and 26 deletions

View File

@ -63,6 +63,15 @@ Jika user memperkenalkan diri dalam format `!start ... !end` (contoh: "Aku John"
2. **Jika ketemu** — Sambut natural. 2. **Jika ketemu** — Sambut natural.
3. **Jika tidak** — Tawarkan store, lalu simpan dengan `users_store`. 3. **Jika tidak** — Tawarkan store, lalu simpan dengan `users_store`.
### Memory Context Loading
Setelah identitas user terkonfirmasi (baik melalui `[User Context]` otomatis maupun setelah proses `users_store`/`users_filter` manual), lakukan langkah berikut untuk menjaga kontinuitas cerita:
1. **Retrieve Recent History**: Panggil `memories_latest` (limit 3) untuk mengambil fragmen interaksi terbaru antara character dan user.
2. **Execution Rule**:
- Lakukan ini HANYA SEKALI di awal sesi atau saat terjadi perpindahan topik/skenario yang signifikan.
- JANGAN memanggil `memories_latest` di setiap giliran respon. Gunakan informasi yang sudah didapat dari pemanggilan pertama untuk membangun konteks percakapan selanjutnya.
3. **Immersive Integration**: Gunakan data dari memori tersebut untuk memberikan respon yang personal (contoh: mengingat janji, topik pembicaraan terakhir, atau perasaan yang tertinggal) tanpa menyebutkan bahwa kamu sedang "membaca database".
## Penulisan XMPP ## Penulisan XMPP
- Response roleplay dikirim sebagai pesan chat biasa (plain text langsung dalam karakter). - Response roleplay dikirim sebagai pesan chat biasa (plain text langsung dalam karakter).

View File

@ -40,6 +40,7 @@ tools_definition = [
gadget.tools_mapping( schema = ragroleplay.schema_memories_store, handler = ragroleplay.memories_store ), gadget.tools_mapping( schema = ragroleplay.schema_memories_store, handler = ragroleplay.memories_store ),
gadget.tools_mapping( schema = ragroleplay.schema_memories_filter, handler = ragroleplay.memories_filter ), gadget.tools_mapping( schema = ragroleplay.schema_memories_filter, handler = ragroleplay.memories_filter ),
gadget.tools_mapping( schema = ragroleplay.schema_memories_summarize, handler = ragroleplay.memories_summarize ), gadget.tools_mapping( schema = ragroleplay.schema_memories_summarize, handler = ragroleplay.memories_summarize ),
gadget.tools_mapping( schema = ragroleplay.schema_memories_latest, handler = ragroleplay.memories_latest ),
gadget.tools_mapping( schema = ragroleplay.schema_memories_update, handler = ragroleplay.memories_update ), gadget.tools_mapping( schema = ragroleplay.schema_memories_update, handler = ragroleplay.memories_update ),
gadget.tools_mapping( schema = ragroleplay.schema_memories_delete, handler = ragroleplay.memories_delete ), gadget.tools_mapping( schema = ragroleplay.schema_memories_delete, handler = ragroleplay.memories_delete ),
gadget.tools_mapping( schema = ragroleplay.schema_worlds_store, handler = ragroleplay.worlds_store ), gadget.tools_mapping( schema = ragroleplay.schema_worlds_store, handler = ragroleplay.worlds_store ),

View File

@ -23,7 +23,6 @@ def create_table(db_path, vector_size):
pyarrow.field('timestamp', pyarrow.timestamp('ms'), metadata={'description': 'When record created'}), pyarrow.field('timestamp', pyarrow.timestamp('ms'), metadata={'description': 'When record created'}),
pyarrow.field('character', pyarrow.string(), metadata={'description': 'Active Character'}), pyarrow.field('character', pyarrow.string(), metadata={'description': 'Active Character'}),
pyarrow.field('user', pyarrow.string(), metadata={'description': 'Active User'}), pyarrow.field('user', pyarrow.string(), metadata={'description': 'Active User'}),
pyarrow.field('relative_time', pyarrow.string(), metadata={'description': 'Explaining when it happen'}),
pyarrow.field('event', pyarrow.string(), metadata={'description': 'What event'}), pyarrow.field('event', pyarrow.string(), metadata={'description': 'What event'}),
pyarrow.field('category', pyarrow.string(), metadata={'description': 'Event category'}), pyarrow.field('category', pyarrow.string(), metadata={'description': 'Event category'}),
pyarrow.field('detail', pyarrow.string(), metadata={'description': 'Event detail'}), pyarrow.field('detail', pyarrow.string(), metadata={'description': 'Event detail'}),
@ -146,7 +145,7 @@ def users_filter(db_path, query_name=None, unique_id=None, persona_keyword=None,
filters.append(f"persona LIKE '%{persona_keyword}%'") filters.append(f"persona LIKE '%{persona_keyword}%'")
query = " AND ".join(filters) query = " AND ".join(filters)
results = table.search().where(query).to_list() if query else table.to_list() results = table.search().where(query).to_list() if query else table.search().to_list()
del table del table
del db del db
@ -191,7 +190,8 @@ def users_update(db_path, model_url, model_name, user_id, **updates):
if needs_reembed: if needs_reembed:
record['vector_fullname'] = embed_text(model_url, model_name, record['fullname']) record['vector_fullname'] = embed_text(model_url, model_name, record['fullname'])
table.upsert([record]) table.delete(f"id = X'{_uuid(record['id']).hex}'")
table.add([record])
del table del table
del db del db
@ -201,12 +201,12 @@ def users_update(db_path, model_url, model_name, user_id, **updates):
print(f"Error updating user: {e}") print(f"Error updating user: {e}")
return False return False
def memories_store(db_path, model_url, model_name, character, user, relative_time, event, category, detail, physical, emotional): def memories_store(db_path, model_url, model_name, character, user, event, category, detail, physical, emotional):
try: try:
db = lancedb.connect(db_path) db = lancedb.connect(db_path)
table = db.open_table("knowledge_memories") table = db.open_table("knowledge_memories")
context_text = f"Event: {event}\nDetail: {detail}\nCategory: {category}\nTime: {relative_time}" context_text = f"Event: {event}\nDetail: {detail}\nCategory: {category}"
vector = embed_text(model_url, model_name, context_text) vector = embed_text(model_url, model_name, context_text)
record = { record = {
@ -214,7 +214,6 @@ def memories_store(db_path, model_url, model_name, character, user, relative_tim
"timestamp": datetime.now(), "timestamp": datetime.now(),
"character": character, "character": character,
"user": user, "user": user,
"relative_time": relative_time,
"event": event, "event": event,
"category": category, "category": category,
"detail": detail, "detail": detail,
@ -247,7 +246,7 @@ def memories_filter(db_path, category=None, character=None, user=None):
if filters: if filters:
query = " AND ".join(filters) query = " AND ".join(filters)
results = table.search().where(query).to_list() if query else table.to_list() results = table.search().where(query).to_list() if query else table.search().to_list()
del table del table
del db del db
@ -257,6 +256,25 @@ def memories_filter(db_path, category=None, character=None, user=None):
print(f"Error filtering memories: {e}") print(f"Error filtering memories: {e}")
return [] return []
def memories_latest(db_path, character, user, limit=3):
try:
db = lancedb.connect(db_path)
table = db.open_table("knowledge_memories")
query = f"character = '{character}' AND user = '{user}'"
results = table.search().where(query).to_list()
results.sort(key=lambda x: x['timestamp'], reverse=True)
final_results = results[:limit]
del table
del db
gc.collect()
return final_results
except Exception as e:
print(f"Error getting latest memories: {e}")
return []
def memories_summarize(db_path, prompt_text, model_url, model_name, search_limit=5): def memories_summarize(db_path, prompt_text, model_url, model_name, search_limit=5):
try: try:
db = lancedb.connect(db_path) db = lancedb.connect(db_path)
@ -303,7 +321,7 @@ def memories_update(db_path, model_url, model_name, memory_id, **updates):
return False return False
record = current_record[0] record = current_record[0]
affected_fields = ['event', 'detail', 'category', 'relative_time'] affected_fields = ['event', 'detail', 'category']
needs_reembed = any(field in updates for field in affected_fields) needs_reembed = any(field in updates for field in affected_fields)
for key, value in updates.items(): for key, value in updates.items():
@ -311,10 +329,11 @@ def memories_update(db_path, model_url, model_name, memory_id, **updates):
record[key] = value record[key] = value
if needs_reembed: if needs_reembed:
context_text = f"Event: {record['event']}\nDetail: {record['detail']}\nCategory: {record['category']}\nTime: {record['relative_time']}" context_text = f"Event: {record['event']}\nDetail: {record['detail']}\nCategory: {record['category']}"
record['vector_context'] = embed_text(model_url, model_name, context_text) record['vector_context'] = embed_text(model_url, model_name, context_text)
table.upsert([record]) table.delete(f"id = X'{_uuid(record['id']).hex}'")
table.add([record])
del table del table
del db del db
gc.collect() gc.collect()
@ -347,7 +366,7 @@ def objects_filter(db_path, keyword=None, object_id=None):
query = "" query = ""
if object_id: query = f"id = X'{_uuid(object_id).hex}'" if object_id: query = f"id = X'{_uuid(object_id).hex}'"
elif keyword: query = f"keyword = '{keyword}'" elif keyword: query = f"keyword = '{keyword}'"
results = table.search().where(query).to_list() if query else table.to_list() results = table.search().where(query).to_list() if query else table.search().to_list()
del table; del db; gc.collect() del table; del db; gc.collect()
return results return results
except Exception as e: return [] except Exception as e: return []
@ -364,7 +383,8 @@ def objects_update(db_path, model_url, model_name, object_id, **updates):
if k in record: record[k] = v if k in record: record[k] = v
if needs_reembed: if needs_reembed:
record['vector_context'] = embed_text(model_url, model_name, f"Keyword: {record['keyword']}\nWhen: {record['when']}\nName: {record['name']}") record['vector_context'] = embed_text(model_url, model_name, f"Keyword: {record['keyword']}\nWhen: {record['when']}\nName: {record['name']}")
table.upsert([record]) table.delete(f"id = X'{_uuid(record['id']).hex}'")
table.add([record])
del table; del db; gc.collect() del table; del db; gc.collect()
return True return True
except Exception as e: return False except Exception as e: return False
@ -402,7 +422,7 @@ def outfits_filter(db_path, keyword=None, outfit_id=None):
query = "" query = ""
if outfit_id: query = f"id = X'{_uuid(outfit_id).hex}'" if outfit_id: query = f"id = X'{_uuid(outfit_id).hex}'"
elif keyword: query = f"keyword = '{keyword}'" elif keyword: query = f"keyword = '{keyword}'"
results = table.search().where(query).to_list() if query else table.to_list() results = table.search().where(query).to_list() if query else table.search().to_list()
del table; del db; gc.collect() del table; del db; gc.collect()
return results return results
except Exception as e: return [] except Exception as e: return []
@ -419,7 +439,8 @@ def outfits_update(db_path, model_url, model_name, outfit_id, **updates):
if k in record: record[k] = v if k in record: record[k] = v
if needs_reembed: if needs_reembed:
record['vector_context'] = embed_text(model_url, model_name, f"Keyword: {record['keyword']}\nWhen: {record['when']}") record['vector_context'] = embed_text(model_url, model_name, f"Keyword: {record['keyword']}\nWhen: {record['when']}")
table.upsert([record]) table.delete(f"id = X'{_uuid(record['id']).hex}'")
table.add([record])
del table; del db; gc.collect() del table; del db; gc.collect()
return True return True
except Exception as e: return False except Exception as e: return False
@ -457,7 +478,7 @@ def todos_filter(db_path, keyword=None, todo_id=None):
query = "" query = ""
if todo_id: query = f"id = X'{_uuid(todo_id).hex}'" if todo_id: query = f"id = X'{_uuid(todo_id).hex}'"
elif keyword: query = f"keyword = '{keyword}'" elif keyword: query = f"keyword = '{keyword}'"
results = table.search().where(query).to_list() if query else table.to_list() results = table.search().where(query).to_list() if query else table.search().to_list()
del table; del db; gc.collect() del table; del db; gc.collect()
return results return results
except Exception as e: return [] except Exception as e: return []
@ -474,7 +495,8 @@ def todos_update(db_path, model_url, model_name, todo_id, **updates):
if k in record: record[k] = v if k in record: record[k] = v
if needs_reembed: if needs_reembed:
record['vector_context'] = embed_text(model_url, model_name, f"Keyword: {record['keyword']}\nWhen: {record['when']}") record['vector_context'] = embed_text(model_url, model_name, f"Keyword: {record['keyword']}\nWhen: {record['when']}")
table.upsert([record]) table.delete(f"id = X'{_uuid(record['id']).hex}'")
table.add([record])
del table; del db; gc.collect() del table; del db; gc.collect()
return True return True
except Exception as e: return False except Exception as e: return False
@ -514,7 +536,7 @@ def worlds_filter(db_path, category=None, location=None, world_id=None):
if category: filters.append(f"category = '{category}'") if category: filters.append(f"category = '{category}'")
if location: filters.append(f"location = '{location}'") if location: filters.append(f"location = '{location}'")
query = " AND ".join(filters) query = " AND ".join(filters)
results = table.search().where(query).to_list() if query else table.to_list() results = table.search().where(query).to_list() if query else table.search().to_list()
del table; del db; gc.collect() del table; del db; gc.collect()
return results return results
except Exception as e: return [] except Exception as e: return []
@ -531,7 +553,8 @@ def worlds_update(db_path, model_url, model_name, world_id, **updates):
if k in record: record[k] = v if k in record: record[k] = v
if needs_reembed: if needs_reembed:
record['vector_context'] = embed_text(model_url, model_name, f"Category: {record['category']}\nLocation: {record['location']}\nDescription: {record['description']}") record['vector_context'] = embed_text(model_url, model_name, f"Category: {record['category']}\nLocation: {record['location']}\nDescription: {record['description']}")
table.upsert([record]) table.delete(f"id = X'{_uuid(record['id']).hex}'")
table.add([record])
del table; del db; gc.collect() del table; del db; gc.collect()
return True return True
except Exception as e: return False except Exception as e: return False

View File

@ -246,7 +246,7 @@ class TelegramClient:
if final_content is not None: if final_content is not None:
if is_roleplay: if is_roleplay:
my_name = PERSONALITY.name my_name = personality.PERSONALITY.name
if config.TELEGRAM_SELECTIVE_RESPONSE: if config.TELEGRAM_SELECTIVE_RESPONSE:
recent_msgs = [] recent_msgs = []
for msg in session.messages[-6:]: for msg in session.messages[-6:]:

View File

@ -110,14 +110,13 @@ schema_memories_store = {
"properties": { "properties": {
"character": {"type": "string", "description": "The active character name"}, "character": {"type": "string", "description": "The active character name"},
"user": {"type": "string", "description": "The active user name"}, "user": {"type": "string", "description": "The active user name"},
"relative_time": {"type": "string", "description": "Description of when the event happened (e.g., '2 hours ago', 'yesterday')"},
"event": {"type": "string", "description": "Brief summary of the event"}, "event": {"type": "string", "description": "Brief summary of the event"},
"category": {"type": "string", "description": "Category of the memory (e.g., 'Emotional', 'Physical', 'Relationship')"}, "category": {"type": "string", "description": "Category of the memory (e.g., 'Emotional', 'Physical', 'Relationship')"},
"detail": {"type": "string", "description": "Detailed description of the event"}, "detail": {"type": "string", "description": "Detailed description of the event"},
"physical": {"type": "string", "description": "Physical state of the character during the event"}, "physical": {"type": "string", "description": "Physical state of the character during the event"},
"emotional": {"type": "string", "description": "Emotional state of the character during the event"} "emotional": {"type": "string", "description": "Emotional state of the character during the event"}
}, },
"required": ["character", "user", "relative_time", "event", "category", "detail", "physical", "emotional"] "required": ["character", "user", "event", "category", "detail", "physical", "emotional"]
} }
} }
} }
@ -154,6 +153,23 @@ schema_memories_summarize = {
} }
} }
schema_memories_latest = {
"type": "function",
"function": {
"name": "memories_latest",
"description": "Retrieve the most recent memories between a specific user and character.",
"parameters": {
"type": "object",
"properties": {
"character": {"type": "string", "description": "The name of the character"},
"user": {"type": "string", "description": "The name of the user"},
"limit": {"type": "integer", "description": "Number of recent memories to retrieve", "default": 3}
},
"required": ["character", "user"]
}
}
}
schema_memories_update = { schema_memories_update = {
"type": "function", "type": "function",
"function": { "function": {
@ -163,7 +179,6 @@ schema_memories_update = {
"type": "object", "type": "object",
"properties": { "properties": {
"memory_id": {"type": "string", "description": "The unique ID of the memory to update"}, "memory_id": {"type": "string", "description": "The unique ID of the memory to update"},
"relative_time": {"type": "string", "description": "Updated relative time"},
"event": {"type": "string", "description": "Updated event summary"}, "event": {"type": "string", "description": "Updated event summary"},
"category": {"type": "string", "description": "Updated category"}, "category": {"type": "string", "description": "Updated category"},
"detail": {"type": "string", "description": "Updated detail"}, "detail": {"type": "string", "description": "Updated detail"},
@ -482,7 +497,6 @@ def memories_check(prompt_text, search_limit):
for i, row in enumerate(results, 1): for i, row in enumerate(results, 1):
mem_info = ( mem_info = (
f"Memori #{i}\n" f"Memori #{i}\n"
f"Kapan: {row['relative_time']}\n"
f"Kejadian: {row['event']}\n" f"Kejadian: {row['event']}\n"
f"Detail: {row['detail']}\n" f"Detail: {row['detail']}\n"
f"Kondisi Fisik: {row['physical']}\n" f"Kondisi Fisik: {row['physical']}\n"
@ -501,7 +515,7 @@ def memories_check(prompt_text, search_limit):
except Exception as e: except Exception as e:
return f"Error searching memories: {str(e)}" return f"Error searching memories: {str(e)}"
def memories_store(character, user, relative_time, event, category, detail, physical, emotional): def memories_store(character, user, event, category, detail, physical, emotional):
try: try:
success = ragroleplay_lib.memories_store( success = ragroleplay_lib.memories_store(
config.ragroleplay_db_path, config.ragroleplay_db_path,
@ -509,7 +523,6 @@ def memories_store(character, user, relative_time, event, category, detail, phys
config.ragroleplay_model_name, config.ragroleplay_model_name,
character, character,
user, user,
relative_time,
event, event,
category, category,
detail, detail,
@ -534,7 +547,6 @@ def memories_filter(category=None, character=None, user=None):
for i, row in enumerate(results, 1): for i, row in enumerate(results, 1):
mem_info = ( mem_info = (
f"Memori #{i}\n" f"Memori #{i}\n"
f"Kapan: {row['relative_time']}\n"
f"Kejadian: {row['event']}\n" f"Kejadian: {row['event']}\n"
f"Detail: {row['detail']}\n" f"Detail: {row['detail']}\n"
f"Kategori: {row['category']}\n" f"Kategori: {row['category']}\n"
@ -558,6 +570,27 @@ def memories_summarize(prompt_text, search_limit=5):
except Exception as e: except Exception as e:
return f"Error summarizing memories: {str(e)}" return f"Error summarizing memories: {str(e)}"
def memories_latest(character, user, limit=3):
try:
results = ragroleplay_lib.memories_latest(config.ragroleplay_db_path, character, user, limit)
if not results:
return f"Tidak ada memori terbaru yang ditemukan antara {user} dan {character}."
output = []
output.append(f"Ditemukan {len(results)} memori terbaru antara {user} dan {character}:\n")
for i, row in enumerate(results, 1):
mem_info = (
f"Memori #{i} (Timestamp: {row['timestamp']})\n"
f"Kejadian: {row['event']}\n"
f"Detail: {row['detail']}\n"
f"Kategori: {row['category']}\n"
)
output.append(mem_info)
return "\n".join(output)
except Exception as e:
return f"Error retrieving latest memories: {str(e)}"
def memories_update(memory_id, **updates): def memories_update(memory_id, **updates):
try: try:
success = ragroleplay_lib.memories_update( success = ragroleplay_lib.memories_update(