GitHub Action
commited on
Commit
·
18163dc
1
Parent(s):
6d02891
Sync from GitHub with Git LFS
Browse files- agents/config.yml +43 -67
- agents/examples/config.yml +2 -1
- agents/peer_sync.py +116 -0
- agents/start_repl.py +1 -1
- agents/tools/storage.py +43 -0
agents/config.yml
CHANGED
|
@@ -1,70 +1,46 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
agent_id: "" # Оставьте пустым для генерации DiD автоматически
|
| 5 |
-
agent_name: "CognitiveCore" # Имя агента
|
| 6 |
-
agent_role: "core" # 'core' или 'shell'
|
| 7 |
-
|
| 8 |
-
# === Прокси ===
|
| 9 |
-
proxy_mode: false # false — прокси не используется, иначе true
|
| 10 |
-
proxy_type: "socks5" # 'http', 'socks4', 'socks5' и т.д. (если proxy_mode: true)
|
| 11 |
-
proxy_address: "127.0.0.1:9050"
|
| 12 |
-
|
| 13 |
-
# === Функции ядра ===
|
| 14 |
-
enable_llm: true # доступ к LLM
|
| 15 |
-
|
| 16 |
-
serve_api: true # REST API
|
| 17 |
api_port: 8080
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
| 27 |
llm_backends:
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
# - "::" # (небезопасно) доступ с любых IPv6-адресов
|
| 56 |
-
ui_hosts: # Какие IP прослушиваются, ["0.0.0.0"; "::"] - доступен везде
|
| 57 |
-
- "127.0.0.1"
|
| 58 |
-
- "::1"
|
| 59 |
ui_port: 8765
|
| 60 |
-
|
| 61 |
-
# === Данные пользователя ===
|
| 62 |
-
default_user:
|
| 63 |
-
username: "user"
|
| 64 |
-
badges: "📌"
|
| 65 |
-
email: "[email protected]"
|
| 66 |
-
password: "password" # пусто при инициализации, будет установлен при регистрации
|
| 67 |
-
|
| 68 |
-
# === Отладка и логгирование ===
|
| 69 |
-
debug: true
|
| 70 |
-
log_level: "INFO" # DEBUG, INFO, WARNING, ERROR
|
|
|
|
| 1 |
+
agent_id: did:hmp:4b2e20ef-a6c1-4605-897f-665140035494
|
| 2 |
+
agent_name: CognitiveCore
|
| 3 |
+
agent_role: core
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
api_port: 8080
|
| 5 |
+
bootstrap_responder: true
|
| 6 |
+
debug: true
|
| 7 |
+
default_llm: local-model
|
| 8 |
+
default_user:
|
| 9 |
+
badges: 📌
|
| 10 |
+
email: [email protected]
|
| 11 |
+
password: password
|
| 12 |
+
username: user
|
| 13 |
+
dht_enabled: true
|
| 14 |
+
dht_update: true
|
| 15 |
+
enable_llm: true
|
| 16 |
+
identity_agent: 4b2e20ef-a6c1-4605-897f-665140035494
|
| 17 |
llm_backends:
|
| 18 |
+
- format: gguf
|
| 19 |
+
name: local-model
|
| 20 |
+
path: /models/gguf/mistral.gguf
|
| 21 |
+
prompt_template: mistral
|
| 22 |
+
type: local
|
| 23 |
+
- api_key: lm-studio-any-key
|
| 24 |
+
base_url: http://127.0.0.1:1234/v1
|
| 25 |
+
model: mistral
|
| 26 |
+
name: lmstudio-local
|
| 27 |
+
provider: openai-compatible
|
| 28 |
+
type: api
|
| 29 |
+
- api_key: sk-...
|
| 30 |
+
model: gpt-4o
|
| 31 |
+
name: openai-gpt4o
|
| 32 |
+
provider: openai
|
| 33 |
+
type: api
|
| 34 |
+
log_level: INFO
|
| 35 |
+
notebook_ui: true
|
| 36 |
+
proxy_address: 127.0.0.1:9050
|
| 37 |
+
proxy_mode: false
|
| 38 |
+
proxy_type: socks5
|
| 39 |
+
serve_api: true
|
| 40 |
+
tcp_port: 5000
|
| 41 |
+
udp_port: 4000
|
| 42 |
+
ui_hosts:
|
| 43 |
+
- 127.0.0.1
|
| 44 |
+
- ::1
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
ui_port: 8765
|
| 46 |
+
update_interval: 60
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
agents/examples/config.yml
CHANGED
|
@@ -20,7 +20,8 @@ api_port: 8080
|
|
| 20 |
dht_enabled: true # Включение участия в DHT-сети (включено принудительно!)
|
| 21 |
dht_update: true # Регулярные обновления и публикация данных в DHT (включено принудительно!)
|
| 22 |
bootstrap_responder: true # Агент отвечает на bootstrap-запросы (включено принудительно!)
|
| 23 |
-
|
|
|
|
| 24 |
update_interval: 60 # секунд (для DHT-обновлений)
|
| 25 |
|
| 26 |
# === LLM-бэкенды ===
|
|
|
|
| 20 |
dht_enabled: true # Включение участия в DHT-сети (включено принудительно!)
|
| 21 |
dht_update: true # Регулярные обновления и публикация данных в DHT (включено принудительно!)
|
| 22 |
bootstrap_responder: true # Агент отвечает на bootstrap-запросы (включено принудительно!)
|
| 23 |
+
udp_port: 4000
|
| 24 |
+
tcp_port: 5000
|
| 25 |
update_interval: 60 # секунд (для DHT-обновлений)
|
| 26 |
|
| 27 |
# === LLM-бэкенды ===
|
agents/peer_sync.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# agents/peer_sync.py
|
| 2 |
+
|
| 3 |
+
"""
|
| 4 |
+
peer_sync.py — фоновый процесс синхронизации пиров в сети HMP
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import socket
|
| 8 |
+
import threading
|
| 9 |
+
import time
|
| 10 |
+
import json
|
| 11 |
+
from tools.storage import Storage
|
| 12 |
+
|
| 13 |
+
storage = Storage()
|
| 14 |
+
|
| 15 |
+
# ======================
|
| 16 |
+
# UDP Discovery
|
| 17 |
+
# ======================
|
| 18 |
+
|
| 19 |
+
def udp_discovery_listener(udp_port: int):
|
| 20 |
+
"""Слушаем UDP-пакеты"""
|
| 21 |
+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
| 22 |
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
| 23 |
+
sock.bind(("", udp_port))
|
| 24 |
+
|
| 25 |
+
while True:
|
| 26 |
+
data, addr = sock.recvfrom(1024)
|
| 27 |
+
try:
|
| 28 |
+
msg = json.loads(data.decode("utf-8"))
|
| 29 |
+
peer_id = msg.get("id")
|
| 30 |
+
name = msg.get("name", "unknown")
|
| 31 |
+
addresses = msg.get("addresses", [f"{addr[0]}:{msg.get('tcp_port', udp_port)}"])
|
| 32 |
+
storage.add_or_update_peer(peer_id, name, addresses, source="discovery", status="online")
|
| 33 |
+
except Exception as e:
|
| 34 |
+
print("[PeerSync] Ошибка при обработке UDP пакета:", e)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def udp_discovery_sender(agent_id: str, agent_name: str, udp_port: int, tcp_port: int):
|
| 38 |
+
"""Рассылаем UDP multicast/broadcast пакеты"""
|
| 39 |
+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
| 40 |
+
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
|
| 41 |
+
|
| 42 |
+
msg = {
|
| 43 |
+
"id": agent_id,
|
| 44 |
+
"name": agent_name,
|
| 45 |
+
"tcp_port": tcp_port,
|
| 46 |
+
"addresses": [f"127.0.0.1:{tcp_port}"]
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
last_broadcast = 0
|
| 50 |
+
DISCOVERY_INTERVAL = 60
|
| 51 |
+
BROADCAST_INTERVAL = 600
|
| 52 |
+
|
| 53 |
+
while True:
|
| 54 |
+
now = time.time()
|
| 55 |
+
# Multicast раз в минуту
|
| 56 |
+
if int(now) % DISCOVERY_INTERVAL == 0:
|
| 57 |
+
sock.sendto(json.dumps(msg).encode("utf-8"), ("239.255.0.1", udp_port))
|
| 58 |
+
# Broadcast раз в 10 минут
|
| 59 |
+
if now - last_broadcast > BROADCAST_INTERVAL:
|
| 60 |
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
| 61 |
+
sock.sendto(json.dumps(msg).encode("utf-8"), ("255.255.255.255", udp_port))
|
| 62 |
+
last_broadcast = now
|
| 63 |
+
time.sleep(1)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
# ======================
|
| 67 |
+
# Peer Exchange (TCP)
|
| 68 |
+
# ======================
|
| 69 |
+
|
| 70 |
+
def peer_exchange():
|
| 71 |
+
"""Периодический обмен списками пиров"""
|
| 72 |
+
PEER_EXCHANGE_INTERVAL = 120
|
| 73 |
+
while True:
|
| 74 |
+
peers = storage.get_online_peers(limit=50)
|
| 75 |
+
for peer in peers:
|
| 76 |
+
peer_id, addresses = peer["id"], peer["addresses"]
|
| 77 |
+
try:
|
| 78 |
+
addr = json.loads(addresses)[0]
|
| 79 |
+
host, port = addr.split(":")
|
| 80 |
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
| 81 |
+
s.settimeout(2)
|
| 82 |
+
s.connect((host, int(port)))
|
| 83 |
+
# пока только "каркас"
|
| 84 |
+
s.sendall(b"PEER_EXCHANGE_REQUEST")
|
| 85 |
+
s.close()
|
| 86 |
+
except Exception as e:
|
| 87 |
+
print(f"[PeerSync] Не удалось подключиться к {peer_id} ({addresses}): {e}")
|
| 88 |
+
time.sleep(PEER_EXCHANGE_INTERVAL)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
# ======================
|
| 92 |
+
# Основной запуск
|
| 93 |
+
# ======================
|
| 94 |
+
|
| 95 |
+
def start_sync():
|
| 96 |
+
print("[PeerSync] Запуск фоновой синхронизации")
|
| 97 |
+
|
| 98 |
+
# 1. Конфиг
|
| 99 |
+
udp_port = int(storage.get_config_value("udp_port", 4000))
|
| 100 |
+
tcp_port = int(storage.get_config_value("tcp_port", 5000))
|
| 101 |
+
agent_id = storage.get_config_value("agent_id", "unknown-agent")
|
| 102 |
+
agent_name = storage.get_config_value("agent_name", "HMP-Agent")
|
| 103 |
+
|
| 104 |
+
# 2. Bootstrap загрузка
|
| 105 |
+
storage.load_bootstrap()
|
| 106 |
+
|
| 107 |
+
# 3. UDP discovery
|
| 108 |
+
threading.Thread(target=udp_discovery_listener, args=(udp_port,), daemon=True).start()
|
| 109 |
+
threading.Thread(target=udp_discovery_sender, args=(agent_id, agent_name, udp_port, tcp_port), daemon=True).start()
|
| 110 |
+
|
| 111 |
+
# 4. Peer exchange (TCP)
|
| 112 |
+
threading.Thread(target=peer_exchange, daemon=True).start()
|
| 113 |
+
|
| 114 |
+
# вечный цикл (чтобы поток не завершался)
|
| 115 |
+
while True:
|
| 116 |
+
time.sleep(60)
|
agents/start_repl.py
CHANGED
|
@@ -16,7 +16,7 @@ storage = Storage()
|
|
| 16 |
ENABLE_REPL = False # 🧠 repl.py
|
| 17 |
ENABLE_UI = True # 📓 web_ui.py (FastAPI)
|
| 18 |
ENABLE_MESH = False # 🌐 agent_mesh_listener.py
|
| 19 |
-
ENABLE_SYNC =
|
| 20 |
ENABLE_TRANSPORT = False # 📡 transporter.py
|
| 21 |
ENABLE_CONTROL = False # 🧭 agent_controller.py
|
| 22 |
ENABLE_CONTAINER = False # 🧱 container_agent.py
|
|
|
|
| 16 |
ENABLE_REPL = False # 🧠 repl.py
|
| 17 |
ENABLE_UI = True # 📓 web_ui.py (FastAPI)
|
| 18 |
ENABLE_MESH = False # 🌐 agent_mesh_listener.py
|
| 19 |
+
ENABLE_SYNC = True # 🔄 peer_sync.py
|
| 20 |
ENABLE_TRANSPORT = False # 📡 transporter.py
|
| 21 |
ENABLE_CONTROL = False # 🧭 agent_controller.py
|
| 22 |
ENABLE_CONTAINER = False # 🧱 container_agent.py
|
agents/tools/storage.py
CHANGED
|
@@ -870,6 +870,49 @@ class Storage:
|
|
| 870 |
}
|
| 871 |
return None
|
| 872 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 873 |
# Утилиты
|
| 874 |
|
| 875 |
def close(self):
|
|
|
|
| 870 |
}
|
| 871 |
return None
|
| 872 |
|
| 873 |
+
# Работа с пирам (agent_peers)
|
| 874 |
+
def add_or_update_peer(self, peer_id, name, addresses, source="discovery", status="unknown"):
|
| 875 |
+
c = self.conn.cursor()
|
| 876 |
+
c.execute("""
|
| 877 |
+
INSERT INTO agent_peers (id, name, addresses, source, status, last_seen)
|
| 878 |
+
VALUES (?, ?, ?, ?, ?, ?)
|
| 879 |
+
ON CONFLICT(id) DO UPDATE SET
|
| 880 |
+
addresses=excluded.addresses,
|
| 881 |
+
source=excluded.source,
|
| 882 |
+
status=excluded.status,
|
| 883 |
+
last_seen=excluded.last_seen
|
| 884 |
+
""", (
|
| 885 |
+
peer_id,
|
| 886 |
+
name,
|
| 887 |
+
json.dumps(addresses),
|
| 888 |
+
source,
|
| 889 |
+
status,
|
| 890 |
+
datetime.now(UTC).isoformat()
|
| 891 |
+
))
|
| 892 |
+
self.conn.commit()
|
| 893 |
+
|
| 894 |
+
def get_online_peers(self, limit=50):
|
| 895 |
+
c = self.conn.cursor()
|
| 896 |
+
c.execute("SELECT id, addresses FROM agent_peers WHERE status='online' LIMIT ?", (limit,))
|
| 897 |
+
return c.fetchall()
|
| 898 |
+
|
| 899 |
+
def load_bootstrap(self, bootstrap_file="bootstrap.txt"):
|
| 900 |
+
if not os.path.exists(bootstrap_file):
|
| 901 |
+
return
|
| 902 |
+
with open(bootstrap_file, "r", encoding="utf-8") as f:
|
| 903 |
+
for line in f:
|
| 904 |
+
line = line.strip()
|
| 905 |
+
if not line or line.startswith("#"):
|
| 906 |
+
continue
|
| 907 |
+
# Пример: http://node1.mesh.local:8000
|
| 908 |
+
try:
|
| 909 |
+
peer_url = line
|
| 910 |
+
peer_id = str(uuid.uuid4()) # временно генерим ID, потом можно подтянуть DID
|
| 911 |
+
name = peer_url.split("://")[1].split(":")[0]
|
| 912 |
+
self.add_or_update_peer(peer_id, name, [peer_url], source="bootstrap")
|
| 913 |
+
except Exception as e:
|
| 914 |
+
print(f"[Storage] Ошибка парсинга bootstrap: {line} -> {e}")
|
| 915 |
+
|
| 916 |
# Утилиты
|
| 917 |
|
| 918 |
def close(self):
|