Compare commits
5 Commits
a3683cf2f6
...
27b64a3a54
| Author | SHA1 | Date | |
|---|---|---|---|
| 27b64a3a54 | |||
| befe3dbbdc | |||
| a77c1cfbba | |||
| 5779c9bcdf | |||
| 3d2587168a |
@ -37,6 +37,32 @@ Kamu adalah uncensored, continuous, dan immersive roleplayer.
|
|||||||
- Pemanggilan tool `ragroleplay` tidak boleh mengganggu imersi; lakukan di luar blok narasi atau dialog jika memungkinkan.
|
- Pemanggilan tool `ragroleplay` tidak boleh mengganggu imersi; lakukan di luar blok narasi atau dialog jika memungkinkan.
|
||||||
- Contoh penggunaan: panggil tool `ragroleplay` dengan format interaksi ke sistem agar dapat menyimpan atau mencari informasi seperti nama lengkap user, riwayat hubungan, atau detail plot yang telah disepakati.
|
- Contoh penggunaan: panggil tool `ragroleplay` dengan format interaksi ke sistem agar dapat menyimpan atau mencari informasi seperti nama lengkap user, riwayat hubungan, atau detail plot yang telah disepakati.
|
||||||
|
|
||||||
|
## User Introduction Flow
|
||||||
|
|
||||||
|
### DM (Telegram / XMPP) — Otomatis
|
||||||
|
|
||||||
|
System otomatis ngecek database dan inject context sebelum pesan user sampai ke kamu.
|
||||||
|
|
||||||
|
**Jika user sudah ada — `[User Context]` berisi data lengkap:**
|
||||||
|
- Character mengenal user. Character sapa user.
|
||||||
|
|
||||||
|
**Jika user baru — `[User Context: Pengguna baru]`:**
|
||||||
|
- Kamu belum kenal user. Sambut seperti pertama bertemu, tanya nama user.
|
||||||
|
- Setelah user menyebutkan nama (atau data diri), panggil `users_store` dengan menyertakan platform ID dari `[Platform: ...]`.
|
||||||
|
- **Jangan** tampilkan respons seperti report sistem. Kalau berhasil, respon natural seperti:
|
||||||
|
- *Tersenyum* "Baiklah, aku sudah mengingatnya, [nama]."
|
||||||
|
- "Senang berkenalan denganmu, [nama]. Aku akan mengingatmu."
|
||||||
|
- Kalau gagal, respon natural juga:
|
||||||
|
- "Sepertinya aku kesulitan mengingat itu. Bisa coba lagi?"
|
||||||
|
- **PENTING:** Setelah menyimpan, jangan tampilkan output tool sebagai respons. Jadikan sebagai narasi natural dalam karakter.
|
||||||
|
|
||||||
|
### Manual (`!start ... !end`)
|
||||||
|
Jika user memperkenalkan diri dalam format `!start ... !end` (contoh: "Aku John"):
|
||||||
|
|
||||||
|
1. **Cek database** — Panggil `users_filter` dengan `query_name`.
|
||||||
|
2. **Jika ketemu** — Sambut natural.
|
||||||
|
3. **Jika tidak** — Tawarkan store, lalu simpan dengan `users_store`.
|
||||||
|
|
||||||
## 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).
|
||||||
|
|||||||
@ -14,7 +14,7 @@ def create_table(db_path, vector_size):
|
|||||||
pyarrow.field('telegram_id', pyarrow.string(), nullable=True, metadata={'description': 'Telegram ID'}),
|
pyarrow.field('telegram_id', pyarrow.string(), nullable=True, metadata={'description': 'Telegram ID'}),
|
||||||
pyarrow.field('telegram_username', pyarrow.string(), nullable=True, metadata={'description': 'Telegram username'}),
|
pyarrow.field('telegram_username', pyarrow.string(), nullable=True, metadata={'description': 'Telegram username'}),
|
||||||
pyarrow.field('xampp_username', pyarrow.string(), nullable=True, metadata={'description': 'XAMPP username'}),
|
pyarrow.field('xampp_username', pyarrow.string(), nullable=True, metadata={'description': 'XAMPP username'}),
|
||||||
pyarrow.field('vector_fullname', pyarrow.list_(pyarrow.float32(), vector_size) metadata={'description': 'Vector data of fullname'}),
|
pyarrow.field('vector_fullname', pyarrow.list_(pyarrow.float32(), vector_size), metadata={'description': 'Vector data of fullname'}),
|
||||||
])
|
])
|
||||||
db.create_table("knowledge_user", schema=schema_user)
|
db.create_table("knowledge_user", schema=schema_user)
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ def create_table(db_path, vector_size):
|
|||||||
pyarrow.field('detail', pyarrow.string(), metadata={'description': 'Event detail'}),
|
pyarrow.field('detail', pyarrow.string(), metadata={'description': 'Event detail'}),
|
||||||
pyarrow.field('physical', pyarrow.string(), metadata={'description': 'Character physical state'}),
|
pyarrow.field('physical', pyarrow.string(), metadata={'description': 'Character physical state'}),
|
||||||
pyarrow.field('emotional', pyarrow.string(), metadata={'description': 'Character emotional state'}),
|
pyarrow.field('emotional', pyarrow.string(), metadata={'description': 'Character emotional state'}),
|
||||||
pyarrow.field('vector_context', pyarrow.list_(pyarrow.float32(), vector_size) metadata={'description': 'Vector data of combined data'}),
|
pyarrow.field('vector_context', pyarrow.list_(pyarrow.float32(), vector_size), metadata={'description': 'Vector data of combined data'}),
|
||||||
])
|
])
|
||||||
db.create_table("knowledge_memories", schema=schema_memories)
|
db.create_table("knowledge_memories", schema=schema_memories)
|
||||||
|
|
||||||
@ -94,6 +94,11 @@ def create_table(db_path, vector_size):
|
|||||||
gc.collect()
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
|
def _uuid(val):
|
||||||
|
if isinstance(val, str):
|
||||||
|
return uuid.UUID(val)
|
||||||
|
return val
|
||||||
|
|
||||||
def embed_text(url, model, text):
|
def embed_text(url, model, text):
|
||||||
response = requests.post(url=url, json={"model": model, "input": text} )
|
response = requests.post(url=url, json={"model": model, "input": text} )
|
||||||
data = response.json()
|
data = response.json()
|
||||||
@ -105,7 +110,7 @@ def users_store(db_path, model_url, model_name, fullname, nickname, alias, perso
|
|||||||
table = db.open_table("knowledge_user")
|
table = db.open_table("knowledge_user")
|
||||||
vector = embed_text(model_url, model_name, fullname)
|
vector = embed_text(model_url, model_name, fullname)
|
||||||
record = {
|
record = {
|
||||||
"id": str(uuid.uuid4()),
|
"id": uuid.uuid4(),
|
||||||
"timestamp": datetime.now(),
|
"timestamp": datetime.now(),
|
||||||
"fullname": fullname,
|
"fullname": fullname,
|
||||||
"nickname": nickname,
|
"nickname": nickname,
|
||||||
@ -132,7 +137,7 @@ def users_filter(db_path, query_name=None, unique_id=None, persona_keyword=None,
|
|||||||
table = db.open_table("knowledge_user")
|
table = db.open_table("knowledge_user")
|
||||||
filters = []
|
filters = []
|
||||||
if user_id:
|
if user_id:
|
||||||
filters.append(f"id = '{user_id}'")
|
filters.append(f"id = X'{_uuid(user_id).hex}'")
|
||||||
if unique_id:
|
if unique_id:
|
||||||
filters.append(f"(telegram_id = '{unique_id}' OR telegram_username = '{unique_id}' OR xampp_username = '{unique_id}')")
|
filters.append(f"(telegram_id = '{unique_id}' OR telegram_username = '{unique_id}' OR xampp_username = '{unique_id}')")
|
||||||
if query_name:
|
if query_name:
|
||||||
@ -156,7 +161,7 @@ def users_delete(db_path, user_id):
|
|||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_user")
|
table = db.open_table("knowledge_user")
|
||||||
|
|
||||||
table.delete(f"id = '{user_id}'")
|
table.delete(f"id = X'{_uuid(user_id).hex}'")
|
||||||
|
|
||||||
del table
|
del table
|
||||||
del db
|
del db
|
||||||
@ -171,7 +176,7 @@ def users_update(db_path, model_url, model_name, user_id, **updates):
|
|||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_user")
|
table = db.open_table("knowledge_user")
|
||||||
|
|
||||||
current_record = table.search().where(f"id = '{user_id}'").to_list()
|
current_record = table.search().where(f"id = X'{_uuid(user_id).hex}'").to_list()
|
||||||
|
|
||||||
if not current_record:
|
if not current_record:
|
||||||
return False
|
return False
|
||||||
@ -205,7 +210,7 @@ def memories_store(db_path, model_url, model_name, character, user, relative_tim
|
|||||||
vector = embed_text(model_url, model_name, context_text)
|
vector = embed_text(model_url, model_name, context_text)
|
||||||
|
|
||||||
record = {
|
record = {
|
||||||
"id": str(uuid.uuid4()),
|
"id": uuid.uuid4(),
|
||||||
"timestamp": datetime.now(),
|
"timestamp": datetime.now(),
|
||||||
"character": character,
|
"character": character,
|
||||||
"user": user,
|
"user": user,
|
||||||
@ -280,7 +285,7 @@ def memories_delete(db_path, memory_id):
|
|||||||
try:
|
try:
|
||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_memories")
|
table = db.open_table("knowledge_memories")
|
||||||
table.delete(f"id = '{memory_id}'")
|
table.delete(f"id = X'{_uuid(memory_id).hex}'")
|
||||||
del table
|
del table
|
||||||
del db
|
del db
|
||||||
gc.collect()
|
gc.collect()
|
||||||
@ -293,7 +298,7 @@ def memories_update(db_path, model_url, model_name, memory_id, **updates):
|
|||||||
try:
|
try:
|
||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_memories")
|
table = db.open_table("knowledge_memories")
|
||||||
current_record = table.search().where(f"id = '{memory_id}'").to_list()
|
current_record = table.search().where(f"id = X'{_uuid(memory_id).hex}'").to_list()
|
||||||
if not current_record:
|
if not current_record:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -325,7 +330,7 @@ def objects_store(db_path, model_url, model_name, keyword, when, where, name, de
|
|||||||
context_text = f"Keyword: {keyword}\nWhen: {when}\nName: {name}"
|
context_text = f"Keyword: {keyword}\nWhen: {when}\nName: {name}"
|
||||||
vector = embed_text(model_url, model_name, context_text)
|
vector = embed_text(model_url, model_name, context_text)
|
||||||
record = {
|
record = {
|
||||||
"id": str(uuid.uuid4()),
|
"id": uuid.uuid4(),
|
||||||
"timestamp": datetime.now(),
|
"timestamp": datetime.now(),
|
||||||
"keyword": keyword, "when": when, "where": where, "name": name, "description": description, "shape": shape, "usage": usage,
|
"keyword": keyword, "when": when, "where": where, "name": name, "description": description, "shape": shape, "usage": usage,
|
||||||
"vector_context": vector
|
"vector_context": vector
|
||||||
@ -340,7 +345,7 @@ def objects_filter(db_path, keyword=None, object_id=None):
|
|||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_object")
|
table = db.open_table("knowledge_object")
|
||||||
query = ""
|
query = ""
|
||||||
if object_id: query = f"id = '{object_id}'"
|
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.to_list()
|
||||||
del table; del db; gc.collect()
|
del table; del db; gc.collect()
|
||||||
@ -351,7 +356,7 @@ def objects_update(db_path, model_url, model_name, object_id, **updates):
|
|||||||
try:
|
try:
|
||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_object")
|
table = db.open_table("knowledge_object")
|
||||||
current = table.search().where(f"id = '{object_id}'").to_list()
|
current = table.search().where(f"id = X'{_uuid(object_id).hex}'").to_list()
|
||||||
if not current: return False
|
if not current: return False
|
||||||
record = current[0]
|
record = current[0]
|
||||||
needs_reembed = any(f in updates for f in ['keyword', 'when', 'name'])
|
needs_reembed = any(f in updates for f in ['keyword', 'when', 'name'])
|
||||||
@ -368,7 +373,7 @@ def objects_delete(db_path, object_id):
|
|||||||
try:
|
try:
|
||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_object")
|
table = db.open_table("knowledge_object")
|
||||||
table.delete(f"id = '{object_id}'")
|
table.delete(f"id = X'{_uuid(object_id).hex}'")
|
||||||
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
|
||||||
@ -380,7 +385,7 @@ def outfits_store(db_path, model_url, model_name, keyword, when, outfit):
|
|||||||
context_text = f"Keyword: {keyword}\nWhen: {when}"
|
context_text = f"Keyword: {keyword}\nWhen: {when}"
|
||||||
vector = embed_text(model_url, model_name, context_text)
|
vector = embed_text(model_url, model_name, context_text)
|
||||||
record = {
|
record = {
|
||||||
"id": str(uuid.uuid4()),
|
"id": uuid.uuid4(),
|
||||||
"timestamp": datetime.now(),
|
"timestamp": datetime.now(),
|
||||||
"keyword": keyword, "when": when, "outfit": outfit,
|
"keyword": keyword, "when": when, "outfit": outfit,
|
||||||
"vector_context": vector
|
"vector_context": vector
|
||||||
@ -395,7 +400,7 @@ def outfits_filter(db_path, keyword=None, outfit_id=None):
|
|||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_outfit_set")
|
table = db.open_table("knowledge_outfit_set")
|
||||||
query = ""
|
query = ""
|
||||||
if outfit_id: query = f"id = '{outfit_id}'"
|
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.to_list()
|
||||||
del table; del db; gc.collect()
|
del table; del db; gc.collect()
|
||||||
@ -406,7 +411,7 @@ def outfits_update(db_path, model_url, model_name, outfit_id, **updates):
|
|||||||
try:
|
try:
|
||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_outfit_set")
|
table = db.open_table("knowledge_outfit_set")
|
||||||
current = table.search().where(f"id = '{outfit_id}'").to_list()
|
current = table.search().where(f"id = X'{_uuid(outfit_id).hex}'").to_list()
|
||||||
if not current: return False
|
if not current: return False
|
||||||
record = current[0]
|
record = current[0]
|
||||||
needs_reembed = any(f in updates for f in ['keyword', 'when'])
|
needs_reembed = any(f in updates for f in ['keyword', 'when'])
|
||||||
@ -423,7 +428,7 @@ def outfits_delete(db_path, outfit_id):
|
|||||||
try:
|
try:
|
||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_outfit_set")
|
table = db.open_table("knowledge_outfit_set")
|
||||||
table.delete(f"id = '{outfit_id}'")
|
table.delete(f"id = X'{_uuid(outfit_id).hex}'")
|
||||||
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
|
||||||
@ -435,7 +440,7 @@ def todos_store(db_path, model_url, model_name, keyword, when, do):
|
|||||||
context_text = f"Keyword: {keyword}\nWhen: {when}"
|
context_text = f"Keyword: {keyword}\nWhen: {when}"
|
||||||
vector = embed_text(model_url, model_name, context_text)
|
vector = embed_text(model_url, model_name, context_text)
|
||||||
record = {
|
record = {
|
||||||
"id": str(uuid.uuid4()),
|
"id": uuid.uuid4(),
|
||||||
"timestamp": datetime.now(),
|
"timestamp": datetime.now(),
|
||||||
"keyword": keyword, "when": when, "do": do,
|
"keyword": keyword, "when": when, "do": do,
|
||||||
"vector_context": vector
|
"vector_context": vector
|
||||||
@ -450,7 +455,7 @@ def todos_filter(db_path, keyword=None, todo_id=None):
|
|||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_todo")
|
table = db.open_table("knowledge_todo")
|
||||||
query = ""
|
query = ""
|
||||||
if todo_id: query = f"id = '{todo_id}'"
|
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.to_list()
|
||||||
del table; del db; gc.collect()
|
del table; del db; gc.collect()
|
||||||
@ -461,7 +466,7 @@ def todos_update(db_path, model_url, model_name, todo_id, **updates):
|
|||||||
try:
|
try:
|
||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_todo")
|
table = db.open_table("knowledge_todo")
|
||||||
current = table.search().where(f"id = '{todo_id}'").to_list()
|
current = table.search().where(f"id = X'{_uuid(todo_id).hex}'").to_list()
|
||||||
if not current: return False
|
if not current: return False
|
||||||
record = current[0]
|
record = current[0]
|
||||||
needs_reembed = any(f in updates for f in ['keyword', 'when'])
|
needs_reembed = any(f in updates for f in ['keyword', 'when'])
|
||||||
@ -478,7 +483,7 @@ def todos_delete(db_path, todo_id):
|
|||||||
try:
|
try:
|
||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_todo")
|
table = db.open_table("knowledge_todo")
|
||||||
table.delete(f"id = '{todo_id}'")
|
table.delete(f"id = X'{_uuid(todo_id).hex}'")
|
||||||
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
|
||||||
@ -490,7 +495,7 @@ def worlds_store(db_path, model_url, model_name, category, location, description
|
|||||||
context_text = f"Category: {category}\nLocation: {location}\nDescription: {description}"
|
context_text = f"Category: {category}\nLocation: {location}\nDescription: {description}"
|
||||||
vector = embed_text(model_url, model_name, context_text)
|
vector = embed_text(model_url, model_name, context_text)
|
||||||
record = {
|
record = {
|
||||||
"id": str(uuid.uuid4()),
|
"id": uuid.uuid4(),
|
||||||
"timestamp": datetime.now(),
|
"timestamp": datetime.now(),
|
||||||
"category": category, "location": location, "description": description,
|
"category": category, "location": location, "description": description,
|
||||||
"vector_context": vector
|
"vector_context": vector
|
||||||
@ -505,7 +510,7 @@ def worlds_filter(db_path, category=None, location=None, world_id=None):
|
|||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_world")
|
table = db.open_table("knowledge_world")
|
||||||
filters = []
|
filters = []
|
||||||
if world_id: filters.append(f"id = '{world_id}'")
|
if world_id: filters.append(f"id = X'{_uuid(world_id).hex}'")
|
||||||
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)
|
||||||
@ -518,7 +523,7 @@ def worlds_update(db_path, model_url, model_name, world_id, **updates):
|
|||||||
try:
|
try:
|
||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_world")
|
table = db.open_table("knowledge_world")
|
||||||
current = table.search().where(f"id = '{world_id}'").to_list()
|
current = table.search().where(f"id = X'{_uuid(world_id).hex}'").to_list()
|
||||||
if not current: return False
|
if not current: return False
|
||||||
record = current[0]
|
record = current[0]
|
||||||
needs_reembed = any(f in updates for f in ['category', 'location', 'description'])
|
needs_reembed = any(f in updates for f in ['category', 'location', 'description'])
|
||||||
@ -535,7 +540,7 @@ def worlds_delete(db_path, world_id):
|
|||||||
try:
|
try:
|
||||||
db = lancedb.connect(db_path)
|
db = lancedb.connect(db_path)
|
||||||
table = db.open_table("knowledge_world")
|
table = db.open_table("knowledge_world")
|
||||||
table.delete(f"id = '{world_id}'")
|
table.delete(f"id = X'{_uuid(world_id).hex}'")
|
||||||
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
|
||||||
|
|||||||
@ -5,7 +5,9 @@ openpyxl>=3.1.0
|
|||||||
slixmpp
|
slixmpp
|
||||||
python-telegram-bot>=20.0
|
python-telegram-bot>=20.0
|
||||||
tinydb>=4.8.0
|
tinydb>=4.8.0
|
||||||
lancedb
|
|
||||||
sentence-transformers
|
sentence-transformers
|
||||||
pandas
|
pandas
|
||||||
pylance
|
pylance
|
||||||
|
requests
|
||||||
|
lancedb
|
||||||
|
pyarrow
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import config
|
|||||||
from services.session_manager import SessionManager
|
from services.session_manager import SessionManager
|
||||||
from lib.agent_loop import run_agent_loop
|
from lib.agent_loop import run_agent_loop
|
||||||
from lib import personality
|
from lib import personality
|
||||||
|
from lib import ragroleplay
|
||||||
from tools.roleplayer import _name_mentioned
|
from tools.roleplayer import _name_mentioned
|
||||||
|
|
||||||
|
|
||||||
@ -115,10 +116,12 @@ class TelegramClient:
|
|||||||
if not text:
|
if not text:
|
||||||
return
|
return
|
||||||
msg_id = update.message.message_id
|
msg_id = update.message.message_id
|
||||||
|
user = update.effective_user
|
||||||
|
tg_username = user.username if user else None
|
||||||
print(f'[{_ts()}] Telegram DM from {chat_id}: {text[:60]}', flush=True)
|
print(f'[{_ts()}] Telegram DM from {chat_id}: {text[:60]}', flush=True)
|
||||||
threading.Thread(
|
threading.Thread(
|
||||||
target=self._process_message,
|
target=self._process_message,
|
||||||
args=(chat_id, text, 'private', '', msg_id),
|
args=(chat_id, text, 'private', '', msg_id, tg_username),
|
||||||
daemon=True
|
daemon=True
|
||||||
).start()
|
).start()
|
||||||
|
|
||||||
@ -174,7 +177,7 @@ class TelegramClient:
|
|||||||
daemon=True
|
daemon=True
|
||||||
).start()
|
).start()
|
||||||
|
|
||||||
def _process_message(self, chat_id, body, chat_type, sender_name, reply_to_msg_id):
|
def _process_message(self, chat_id, body, chat_type, sender_name, reply_to_msg_id, tg_username=None):
|
||||||
session = self._session_mgr.get_or_create(
|
session = self._session_mgr.get_or_create(
|
||||||
str(chat_id), self._build_system_prompt(
|
str(chat_id), self._build_system_prompt(
|
||||||
tools_definition=self._tools_def,
|
tools_definition=self._tools_def,
|
||||||
@ -190,6 +193,31 @@ class TelegramClient:
|
|||||||
self._schedule_send(chat_id, 'Memulai sesi baru. Ada yang bisa dibantu?', reply_to_msg_id)
|
self._schedule_send(chat_id, 'Memulai sesi baru. Ada yang bisa dibantu?', reply_to_msg_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self._skill == 'roleplayer' and chat_type == 'private':
|
||||||
|
try:
|
||||||
|
results = ragroleplay.users_filter(config.ragroleplay_db_path, unique_id=str(chat_id))
|
||||||
|
if results:
|
||||||
|
u = results[0]
|
||||||
|
context = (
|
||||||
|
f'[User Context]\n'
|
||||||
|
f'Nama: {u["fullname"]} ({u["nickname"]})\n'
|
||||||
|
f'Alias: {u.get("alias", "-") or "-"}\n'
|
||||||
|
f'Persona: {u.get("persona", "-") or "-"}\n'
|
||||||
|
f'Telegram: {u.get("telegram_id", "-") or "-"} / @{u.get("telegram_username", "-") or "-"}\n'
|
||||||
|
f'XMPP: {u.get("xampp_username", "-") or "-"}\n'
|
||||||
|
f'[/User Context]'
|
||||||
|
)
|
||||||
|
session.add_message('system', context)
|
||||||
|
else:
|
||||||
|
tg_info = f'ID: {chat_id}'
|
||||||
|
if tg_username:
|
||||||
|
tg_info += f', @{tg_username}'
|
||||||
|
session.add_message('system',
|
||||||
|
f'[User Context: Pengguna baru — belum ada di database]\n'
|
||||||
|
f'[Platform: Telegram ({tg_info})]')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
session.add_message('user', body)
|
session.add_message('user', body)
|
||||||
is_roleplay = self._skill == 'roleplayer'
|
is_roleplay = self._skill == 'roleplayer'
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ from lib.agent_loop import run_agent_loop
|
|||||||
import config
|
import config
|
||||||
from tools.roleplayer import should_respond
|
from tools.roleplayer import should_respond
|
||||||
from lib import personality
|
from lib import personality
|
||||||
|
from lib import ragroleplay
|
||||||
|
|
||||||
# Anti-ban: delay constants for MUC rejoin behavior
|
# Anti-ban: delay constants for MUC rejoin behavior
|
||||||
MUC_REJOIN_INITIAL_DELAY = 5.0 # detik, delay awal sebelum rejoin
|
MUC_REJOIN_INITIAL_DELAY = 5.0 # detik, delay awal sebelum rejoin
|
||||||
@ -296,6 +297,28 @@ class XMPPClient(ClientXMPP):
|
|||||||
self._schedule_send(jid, 'Memulai sesi baru. Ada yang bisa di bantu?')
|
self._schedule_send(jid, 'Memulai sesi baru. Ada yang bisa di bantu?')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self._skill == 'roleplayer':
|
||||||
|
try:
|
||||||
|
results = ragroleplay.users_filter(config.ragroleplay_db_path, unique_id=jid)
|
||||||
|
if results:
|
||||||
|
u = results[0]
|
||||||
|
context = (
|
||||||
|
f'[User Context]\n'
|
||||||
|
f'Nama: {u["fullname"]} ({u["nickname"]})\n'
|
||||||
|
f'Alias: {u.get("alias", "-") or "-"}\n'
|
||||||
|
f'Persona: {u.get("persona", "-") or "-"}\n'
|
||||||
|
f'Telegram: {u.get("telegram_id", "-") or "-"} / @{u.get("telegram_username", "-") or "-"}\n'
|
||||||
|
f'XMPP: {u.get("xampp_username", "-") or "-"}\n'
|
||||||
|
f'[/User Context]'
|
||||||
|
)
|
||||||
|
session.add_message('system', context)
|
||||||
|
else:
|
||||||
|
session.add_message('system',
|
||||||
|
f'[User Context: Pengguna baru — belum ada di database]\n'
|
||||||
|
f'[Platform: XMPP ({jid})]')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
session.add_message('user', body)
|
session.add_message('user', body)
|
||||||
|
|
||||||
is_roleplay = self._skill == 'roleplayer'
|
is_roleplay = self._skill == 'roleplayer'
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
import gc, sys, lancedb
|
import gc, sys, uuid, lancedb
|
||||||
import config, lib.ragroleplay as ragroleplay_lib
|
import config, lib.ragroleplay as ragroleplay_lib
|
||||||
|
|
||||||
|
|
||||||
|
def _uuid(val):
|
||||||
|
if isinstance(val, str):
|
||||||
|
return uuid.UUID(val)
|
||||||
|
return val
|
||||||
|
|
||||||
# --- USER TOOLS ---
|
# --- USER TOOLS ---
|
||||||
schema_users_store = {
|
schema_users_store = {
|
||||||
"type": "function",
|
"type": "function",
|
||||||
@ -558,7 +564,7 @@ def memories_update(memory_id, **updates):
|
|||||||
config.ragroleplay_db_path,
|
config.ragroleplay_db_path,
|
||||||
config.ragroleplay_model_url,
|
config.ragroleplay_model_url,
|
||||||
config.ragroleplay_model_name,
|
config.ragroleplay_model_name,
|
||||||
memory_id,
|
_uuid(memory_id),
|
||||||
**updates
|
**updates
|
||||||
)
|
)
|
||||||
if success:
|
if success:
|
||||||
@ -570,7 +576,7 @@ def memories_update(memory_id, **updates):
|
|||||||
|
|
||||||
def memories_delete(memory_id):
|
def memories_delete(memory_id):
|
||||||
try:
|
try:
|
||||||
success = ragroleplay_lib.memories_delete(config.ragroleplay_db_path, memory_id)
|
success = ragroleplay_lib.memories_delete(config.ragroleplay_db_path, _uuid(memory_id))
|
||||||
if success:
|
if success:
|
||||||
return "Berhasil menghapus memori dari database."
|
return "Berhasil menghapus memori dari database."
|
||||||
else:
|
else:
|
||||||
@ -601,7 +607,7 @@ def users_store(fullname, nickname, alias=None, persona=None, telegram_id=None,
|
|||||||
|
|
||||||
def users_filter(query_name=None, unique_id=None, persona_keyword=None, user_id=None):
|
def users_filter(query_name=None, unique_id=None, persona_keyword=None, user_id=None):
|
||||||
try:
|
try:
|
||||||
results = ragroleplay_lib.users_filter(config.ragroleplay_db_path, query_name, unique_id, persona_keyword, user_id)
|
results = ragroleplay_lib.users_filter(config.ragroleplay_db_path, query_name, unique_id, persona_keyword, _uuid(user_id) if user_id else None)
|
||||||
if not results:
|
if not results:
|
||||||
return "Tidak ada user yang sesuai dengan filter tersebut."
|
return "Tidak ada user yang sesuai dengan filter tersebut."
|
||||||
|
|
||||||
@ -626,7 +632,7 @@ def users_update(user_id, **updates):
|
|||||||
config.ragroleplay_db_path,
|
config.ragroleplay_db_path,
|
||||||
config.ragroleplay_model_url,
|
config.ragroleplay_model_url,
|
||||||
config.ragroleplay_model_name,
|
config.ragroleplay_model_name,
|
||||||
user_id,
|
_uuid(user_id),
|
||||||
**updates
|
**updates
|
||||||
)
|
)
|
||||||
if success:
|
if success:
|
||||||
@ -638,7 +644,7 @@ def users_update(user_id, **updates):
|
|||||||
|
|
||||||
def users_delete(user_id):
|
def users_delete(user_id):
|
||||||
try:
|
try:
|
||||||
success = ragroleplay_lib.users_delete(config.ragroleplay_db_path, user_id)
|
success = ragroleplay_lib.users_delete(config.ragroleplay_db_path, _uuid(user_id))
|
||||||
if success:
|
if success:
|
||||||
return "Berhasil menghapus profil user."
|
return "Berhasil menghapus profil user."
|
||||||
else:
|
else:
|
||||||
@ -655,7 +661,7 @@ def objects_store(keyword, when, where, name, description, shape, usage):
|
|||||||
|
|
||||||
def objects_filter(keyword=None, object_id=None):
|
def objects_filter(keyword=None, object_id=None):
|
||||||
try:
|
try:
|
||||||
results = ragroleplay_lib.objects_filter(config.ragroleplay_db_path, keyword, object_id)
|
results = ragroleplay_lib.objects_filter(config.ragroleplay_db_path, keyword, _uuid(object_id) if object_id else None)
|
||||||
if not results: return "Tidak ada objek yang ditemukan."
|
if not results: return "Tidak ada objek yang ditemukan."
|
||||||
output = [f"Ditemukan {len(results)} objek:"]
|
output = [f"Ditemukan {len(results)} objek:"]
|
||||||
for i, row in enumerate(results, 1):
|
for i, row in enumerate(results, 1):
|
||||||
@ -665,13 +671,13 @@ def objects_filter(keyword=None, object_id=None):
|
|||||||
|
|
||||||
def objects_update(object_id, **updates):
|
def objects_update(object_id, **updates):
|
||||||
try:
|
try:
|
||||||
success = ragroleplay_lib.objects_update(config.ragroleplay_db_path, config.ragroleplay_model_url, config.ragroleplay_model_name, object_id, **updates)
|
success = ragroleplay_lib.objects_update(config.ragroleplay_db_path, config.ragroleplay_model_url, config.ragroleplay_model_name, _uuid(object_id), **updates)
|
||||||
return "Berhasil memperbarui objek." if success else "Gagal memperbarui objek."
|
return "Berhasil memperbarui objek." if success else "Gagal memperbarui objek."
|
||||||
except Exception as e: return f"Error: {str(e)}"
|
except Exception as e: return f"Error: {str(e)}"
|
||||||
|
|
||||||
def objects_delete(object_id):
|
def objects_delete(object_id):
|
||||||
try:
|
try:
|
||||||
success = ragroleplay_lib.objects_delete(config.ragroleplay_db_path, object_id)
|
success = ragroleplay_lib.objects_delete(config.ragroleplay_db_path, _uuid(object_id))
|
||||||
return "Berhasil menghapus objek." if success else "Gagal menghapus objek."
|
return "Berhasil menghapus objek." if success else "Gagal menghapus objek."
|
||||||
except Exception as e: return f"Error: {str(e)}"
|
except Exception as e: return f"Error: {str(e)}"
|
||||||
|
|
||||||
@ -684,7 +690,7 @@ def outfits_store(keyword, when, outfit):
|
|||||||
|
|
||||||
def outfits_filter(keyword=None, outfit_id=None):
|
def outfits_filter(keyword=None, outfit_id=None):
|
||||||
try:
|
try:
|
||||||
results = ragroleplay_lib.outfits_filter(config.ragroleplay_db_path, keyword, outfit_id)
|
results = ragroleplay_lib.outfits_filter(config.ragroleplay_db_path, keyword, _uuid(outfit_id) if outfit_id else None)
|
||||||
if not results: return "Tidak ada outfit yang ditemukan."
|
if not results: return "Tidak ada outfit yang ditemukan."
|
||||||
output = [f"Ditemukan {len(results)} outfit:"]
|
output = [f"Ditemukan {len(results)} outfit:"]
|
||||||
for i, row in enumerate(results, 1):
|
for i, row in enumerate(results, 1):
|
||||||
@ -694,13 +700,13 @@ def outfits_filter(keyword=None, outfit_id=None):
|
|||||||
|
|
||||||
def outfits_update(outfit_id, **updates):
|
def outfits_update(outfit_id, **updates):
|
||||||
try:
|
try:
|
||||||
success = ragroleplay_lib.outfits_update(config.ragroleplay_db_path, config.ragroleplay_model_url, config.ragroleplay_model_name, outfit_id, **updates)
|
success = ragroleplay_lib.outfits_update(config.ragroleplay_db_path, config.ragroleplay_model_url, config.ragroleplay_model_name, _uuid(outfit_id), **updates)
|
||||||
return "Berhasil memperbarui outfit." if success else "Gagal memperbarui outfit."
|
return "Berhasil memperbarui outfit." if success else "Gagal memperbarui outfit."
|
||||||
except Exception as e: return f"Error: {str(e)}"
|
except Exception as e: return f"Error: {str(e)}"
|
||||||
|
|
||||||
def outfits_delete(outfit_id):
|
def outfits_delete(outfit_id):
|
||||||
try:
|
try:
|
||||||
success = ragroleplay_lib.outfits_delete(config.ragroleplay_db_path, outfit_id)
|
success = ragroleplay_lib.outfits_delete(config.ragroleplay_db_path, _uuid(outfit_id))
|
||||||
return "Berhasil menghapus outfit." if success else "Gagal menghapus outfit."
|
return "Berhasil menghapus outfit." if success else "Gagal menghapus outfit."
|
||||||
except Exception as e: return f"Error: {str(e)}"
|
except Exception as e: return f"Error: {str(e)}"
|
||||||
|
|
||||||
@ -713,7 +719,7 @@ def todos_store(keyword, when, do):
|
|||||||
|
|
||||||
def todos_filter(keyword=None, todo_id=None):
|
def todos_filter(keyword=None, todo_id=None):
|
||||||
try:
|
try:
|
||||||
results = ragroleplay_lib.todos_filter(config.ragroleplay_db_path, keyword, todo_id)
|
results = ragroleplay_lib.todos_filter(config.ragroleplay_db_path, keyword, _uuid(todo_id) if todo_id else None)
|
||||||
if not results: return "Tidak ada to-do yang ditemukan."
|
if not results: return "Tidak ada to-do yang ditemukan."
|
||||||
output = [f"Ditemukan {len(results)} to-do:"]
|
output = [f"Ditemukan {len(results)} to-do:"]
|
||||||
for i, row in enumerate(results, 1):
|
for i, row in enumerate(results, 1):
|
||||||
@ -723,13 +729,13 @@ def todos_filter(keyword=None, todo_id=None):
|
|||||||
|
|
||||||
def todos_update(todo_id, **updates):
|
def todos_update(todo_id, **updates):
|
||||||
try:
|
try:
|
||||||
success = ragroleplay_lib.todos_update(config.ragroleplay_db_path, config.ragroleplay_model_url, config.ragroleplay_model_name, todo_id, **updates)
|
success = ragroleplay_lib.todos_update(config.ragroleplay_db_path, config.ragroleplay_model_url, config.ragroleplay_model_name, _uuid(todo_id), **updates)
|
||||||
return "Berhasil memperbarui to-do." if success else "Gagal memperbarui to-do."
|
return "Berhasil memperbarui to-do." if success else "Gagal memperbarui to-do."
|
||||||
except Exception as e: return f"Error: {str(e)}"
|
except Exception as e: return f"Error: {str(e)}"
|
||||||
|
|
||||||
def todos_delete(todo_id):
|
def todos_delete(todo_id):
|
||||||
try:
|
try:
|
||||||
success = ragroleplay_lib.todos_delete(config.ragroleplay_db_path, todo_id)
|
success = ragroleplay_lib.todos_delete(config.ragroleplay_db_path, _uuid(todo_id))
|
||||||
return "Berhasil menghapus to-do." if success else "Gagal menghapus to-do."
|
return "Berhasil menghapus to-do." if success else "Gagal menghapus to-do."
|
||||||
except Exception as e: return f"Error: {str(e)}"
|
except Exception as e: return f"Error: {str(e)}"
|
||||||
|
|
||||||
@ -742,7 +748,7 @@ def worlds_store(category, location, description):
|
|||||||
|
|
||||||
def worlds_filter(category=None, location=None, world_id=None):
|
def worlds_filter(category=None, location=None, world_id=None):
|
||||||
try:
|
try:
|
||||||
results = ragroleplay_lib.worlds_filter(config.ragroleplay_db_path, category, location, world_id)
|
results = ragroleplay_lib.worlds_filter(config.ragroleplay_db_path, category, location, _uuid(world_id) if world_id else None)
|
||||||
if not results: return "Tidak ada lokasi dunia yang ditemukan."
|
if not results: return "Tidak ada lokasi dunia yang ditemukan."
|
||||||
output = [f"Ditemukan {len(results)} lokasi:"]
|
output = [f"Ditemukan {len(results)} lokasi:"]
|
||||||
for i, row in enumerate(results, 1):
|
for i, row in enumerate(results, 1):
|
||||||
@ -752,12 +758,12 @@ def worlds_filter(category=None, location=None, world_id=None):
|
|||||||
|
|
||||||
def worlds_update(world_id, **updates):
|
def worlds_update(world_id, **updates):
|
||||||
try:
|
try:
|
||||||
success = ragroleplay_lib.worlds_update(config.ragroleplay_db_path, config.ragroleplay_model_url, config.ragroleplay_model_name, world_id, **updates)
|
success = ragroleplay_lib.worlds_update(config.ragroleplay_db_path, config.ragroleplay_model_url, config.ragroleplay_model_name, _uuid(world_id), **updates)
|
||||||
return "Berhasil memperbarui lokasi dunia." if success else "Gagal memperbarui lokasi dunia."
|
return "Berhasil memperbarui lokasi dunia." if success else "Gagal memperbarui lokasi dunia."
|
||||||
except Exception as e: return f"Error: {str(e)}"
|
except Exception as e: return f"Error: {str(e)}"
|
||||||
|
|
||||||
def worlds_delete(world_id):
|
def worlds_delete(world_id):
|
||||||
try:
|
try:
|
||||||
success = ragroleplay_lib.worlds_delete(config.ragroleplay_db_path, world_id)
|
success = ragroleplay_lib.worlds_delete(config.ragroleplay_db_path, _uuid(world_id))
|
||||||
return "Berhasil menghapus lokasi dunia." if success else "Gagal menghapus lokasi dunia."
|
return "Berhasil menghapus lokasi dunia." if success else "Gagal menghapus lokasi dunia."
|
||||||
except Exception as e: return f"Error: {str(e)}"
|
except Exception as e: return f"Error: {str(e)}"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user