ocerebro 0.4.3 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/dashboard/api.py +6 -1
- package/src/dashboard/standalone_server.py +47 -0
- package/src/mcp/server.py +81 -14
package/package.json
CHANGED
package/pyproject.toml
CHANGED
package/src/dashboard/api.py
CHANGED
|
@@ -6,6 +6,7 @@ from typing import Any, Dict, List, Optional
|
|
|
6
6
|
import sqlite3
|
|
7
7
|
import json
|
|
8
8
|
from datetime import datetime
|
|
9
|
+
from src.forgetting.gc import calculate_rfms_score
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def create_router(
|
|
@@ -24,6 +25,11 @@ def create_router(
|
|
|
24
25
|
router.entities_db = entities_db
|
|
25
26
|
router.cerebro_path = cerebro_path
|
|
26
27
|
|
|
28
|
+
@router.get("/ping")
|
|
29
|
+
async def get_ping():
|
|
30
|
+
"""Retorna o cerebro_path atual do servidor"""
|
|
31
|
+
return {"cerebro_path": str(router.cerebro_path)}
|
|
32
|
+
|
|
27
33
|
@router.get("/status")
|
|
28
34
|
async def get_status():
|
|
29
35
|
"""Retorna status geral do sistema"""
|
|
@@ -242,7 +248,6 @@ def create_router(
|
|
|
242
248
|
memories = []
|
|
243
249
|
for row in rows:
|
|
244
250
|
# Calcula GC risk
|
|
245
|
-
from src.forgetting.gc import calculate_rfms_score
|
|
246
251
|
memory_dict = dict(row)
|
|
247
252
|
gc_risk = 1.0 - calculate_rfms_score(memory_dict)
|
|
248
253
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Servidor standalone do Dashboard do OCerebro - roda como processo separado"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
# Adiciona project root ao path
|
|
8
|
+
project_root = Path(__file__).parent.parent.parent
|
|
9
|
+
sys.path.insert(0, str(project_root))
|
|
10
|
+
|
|
11
|
+
from src.dashboard.server import DashboardServer
|
|
12
|
+
from src.mcp.server import CerebroMCP
|
|
13
|
+
|
|
14
|
+
def main():
|
|
15
|
+
"""Inicia o servidor standalone"""
|
|
16
|
+
port = int(sys.argv[1]) if len(sys.argv) > 1 else 7999
|
|
17
|
+
cerebro_path = Path(sys.argv[2]) if len(sys.argv) > 2 else None
|
|
18
|
+
|
|
19
|
+
# Inicializa componentes com o path correto
|
|
20
|
+
mcp = CerebroMCP(cerebro_path=cerebro_path)
|
|
21
|
+
|
|
22
|
+
dashboard = DashboardServer(
|
|
23
|
+
cerebro_path=mcp.cerebro_path,
|
|
24
|
+
metadata_db=mcp.metadata_db,
|
|
25
|
+
embeddings_db=mcp.embeddings_db,
|
|
26
|
+
entities_db=mcp.entities_db
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
if dashboard.start(port):
|
|
30
|
+
# Salva PID para poder ser morto quando precisar trocar de contexto
|
|
31
|
+
pid_file = Path.home() / ".ocerebro_dashboard.pid"
|
|
32
|
+
pid_file.write_text(str(os.getpid()), encoding="utf-8")
|
|
33
|
+
|
|
34
|
+
print(f"Dashboard rodando em http://localhost:{port}")
|
|
35
|
+
print(f"Cerebro path: {mcp.cerebro_path}")
|
|
36
|
+
print(f"PID: {os.getpid()}")
|
|
37
|
+
|
|
38
|
+
# Mantém vivo
|
|
39
|
+
import time
|
|
40
|
+
while True:
|
|
41
|
+
time.sleep(3600) # Dorme por 1 hora, loop infinito
|
|
42
|
+
else:
|
|
43
|
+
print(f"Falha ao iniciar dashboard na porta {port}")
|
|
44
|
+
sys.exit(1)
|
|
45
|
+
|
|
46
|
+
if __name__ == "__main__":
|
|
47
|
+
main()
|
package/src/mcp/server.py
CHANGED
|
@@ -791,6 +791,22 @@ Uma chamada por memória. O sistema salva e indexa automaticamente.
|
|
|
791
791
|
except Exception:
|
|
792
792
|
pass # Falha silenciosa se frontmatter inválido
|
|
793
793
|
|
|
794
|
+
# Indexar no metadata_db para aparecer no dashboard
|
|
795
|
+
if self.metadata_db:
|
|
796
|
+
tags_str = tags if isinstance(tags, str) else ",".join(tags) if isinstance(tags, list) else ""
|
|
797
|
+
self.metadata_db.insert({
|
|
798
|
+
"id": mem_name,
|
|
799
|
+
"type": m_type,
|
|
800
|
+
"project": project,
|
|
801
|
+
"title": frontmatter.get("title", mem_name) if frontmatter else mem_name,
|
|
802
|
+
"content": body_content,
|
|
803
|
+
"tags": tags_str,
|
|
804
|
+
"created_at": datetime.now().isoformat(),
|
|
805
|
+
"updated_at": datetime.now().isoformat(),
|
|
806
|
+
"layer": "auto",
|
|
807
|
+
"path": str(file_path),
|
|
808
|
+
})
|
|
809
|
+
|
|
794
810
|
return f"✅ Memória '{mem_name}' salva em {file_path}"
|
|
795
811
|
|
|
796
812
|
def _remember(self, args: Dict[str, Any]) -> str:
|
|
@@ -898,26 +914,77 @@ Uma chamada por memória. O sistema salva e indexa automaticamente.
|
|
|
898
914
|
def _cerebro_dashboard(self, args: Dict[str, Any]) -> str:
|
|
899
915
|
"""Abre o dashboard visual do OCerebro no browser"""
|
|
900
916
|
try:
|
|
901
|
-
|
|
917
|
+
import subprocess
|
|
918
|
+
import sys
|
|
919
|
+
import requests
|
|
902
920
|
|
|
903
921
|
port = args.get("port", 7999)
|
|
904
922
|
|
|
905
|
-
#
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
923
|
+
# Verifica se já está rodando
|
|
924
|
+
import socket
|
|
925
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
926
|
+
sock.settimeout(1)
|
|
927
|
+
result = sock.connect_ex(('127.0.0.1', port))
|
|
928
|
+
sock.close()
|
|
929
|
+
is_running = result == 0
|
|
930
|
+
|
|
931
|
+
if is_running:
|
|
932
|
+
# Verifica se o servidor está usando o cerebro_path correto
|
|
933
|
+
try:
|
|
934
|
+
resp = requests.get(f"http://127.0.0.1:{port}/api/ping", timeout=2)
|
|
935
|
+
if resp.status_code == 200:
|
|
936
|
+
data = resp.json()
|
|
937
|
+
running_path = data.get("cerebro_path", "")
|
|
938
|
+
current_path = str(self.cerebro_path.absolute())
|
|
939
|
+
|
|
940
|
+
if running_path != current_path:
|
|
941
|
+
# Path diferente - precisa reiniciar o servidor
|
|
942
|
+
# Lê o PID e mata o processo antigo
|
|
943
|
+
pid_file = Path.home() / ".ocerebro_dashboard.pid"
|
|
944
|
+
if pid_file.exists():
|
|
945
|
+
try:
|
|
946
|
+
old_pid = int(pid_file.read_text(encoding="utf-8").strip())
|
|
947
|
+
import os
|
|
948
|
+
os.kill(old_pid, 9) # SIGKILL
|
|
949
|
+
pid_file.unlink() # Remove o arquivo PID
|
|
950
|
+
except Exception:
|
|
951
|
+
pass # Processo já morreu ou erro ao matar
|
|
952
|
+
|
|
953
|
+
# Agora is_running será False e vamos reiniciar abaixo
|
|
954
|
+
is_running = False
|
|
955
|
+
except Exception:
|
|
956
|
+
pass # Se falhar o ping, tenta reiniciar mesmo assim
|
|
957
|
+
|
|
958
|
+
if not is_running:
|
|
959
|
+
# Inicia como processo separado (persiste após o MCP terminar)
|
|
960
|
+
server_script = Path(__file__).parent.parent / "dashboard" / "standalone_server.py"
|
|
961
|
+
if not server_script.exists():
|
|
962
|
+
return "⚠️ Erro: standalone_server.py não encontrado."
|
|
963
|
+
|
|
964
|
+
# Inicia processo em background
|
|
965
|
+
subprocess.Popen(
|
|
966
|
+
[sys.executable, str(server_script), str(port), str(self.cerebro_path.absolute())],
|
|
967
|
+
stdout=subprocess.DEVNULL,
|
|
968
|
+
stderr=subprocess.DEVNULL,
|
|
969
|
+
start_new_session=True # Desacoplado do processo pai
|
|
970
|
+
)
|
|
912
971
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
972
|
+
# Aguarda servidor estar pronto (até 5s)
|
|
973
|
+
import time
|
|
974
|
+
for _ in range(50):
|
|
975
|
+
time.sleep(0.1)
|
|
976
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
977
|
+
sock.settimeout(1)
|
|
978
|
+
result = sock.connect_ex(('127.0.0.1', port))
|
|
979
|
+
sock.close()
|
|
980
|
+
if result == 0:
|
|
981
|
+
break
|
|
982
|
+
else:
|
|
983
|
+
return "⚠️ Servidor não iniciou em 5 segundos."
|
|
918
984
|
|
|
919
985
|
# Abre browser
|
|
920
|
-
|
|
986
|
+
import webbrowser
|
|
987
|
+
webbrowser.open(f"http://localhost:{port}")
|
|
921
988
|
|
|
922
989
|
return f"✅ Dashboard aberto em http://localhost:{port}"
|
|
923
990
|
except ImportError as e:
|