| | |
| |
|
| | import os |
| | import sys |
| | import yaml |
| | import json |
| | import uuid |
| | import sqlite3 |
| | import hashlib |
| |
|
| | sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) |
| |
|
| | from datetime import datetime, UTC |
| | from werkzeug.security import generate_password_hash |
| | from tools.storage import Storage |
| | from tools.identity import generate_did |
| | from tools.crypto import generate_keypair |
| |
|
| | CONFIG_PATH = os.path.join(os.path.dirname(__file__), "config.yml") |
| | DB_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "agent_data.db")) |
| |
|
| | def load_config(path): |
| | with open(path, 'r', encoding='utf-8') as f: |
| | return yaml.safe_load(f) |
| |
|
| | def save_config(path, config): |
| | with open(path, 'w', encoding='utf-8') as f: |
| | yaml.dump(config, f, allow_unicode=True) |
| |
|
| | def expand_addresses(addresses): |
| | expanded = [] |
| | for addr in addresses: |
| | if addr.startswith("any://"): |
| | ip_port = addr[len("any://"):] |
| | expanded.append(f"tcp://{ip_port}") |
| | expanded.append(f"udp://{ip_port}") |
| | else: |
| | expanded.append(addr) |
| | return expanded |
| |
|
| | def init_identity(storage, config): |
| | if not config.get("agent_id"): |
| | |
| | did = generate_did() |
| | identity_id = did.split(":")[-1] |
| |
|
| | |
| | privkey, pubkey = generate_keypair(method="ed25519") |
| | privkey, pubkey = privkey.decode(), pubkey.decode() |
| |
|
| | |
| | identity = { |
| | "id": identity_id, |
| | "name": config.get("agent_name", "Unnamed"), |
| | "pubkey": pubkey, |
| | "privkey": privkey, |
| | "metadata": json.dumps({"role": config.get("agent_role", "core")}), |
| | "created_at": datetime.now(UTC).replace(microsecond=0).isoformat(), |
| | "updated_at": datetime.now(UTC).replace(microsecond=0).isoformat() |
| | } |
| | storage.add_identity(identity) |
| |
|
| | |
| | config["agent_id"] = did |
| | config["identity_agent"] = identity_id |
| | config["pubkey"] = pubkey |
| | config["privkey"] = privkey |
| |
|
| | save_config(CONFIG_PATH, config) |
| | print(f"[+] Создана личность: {identity_id}.") |
| | else: |
| | print("[=] agent_id уже задан, пропускаем генерацию DiD.") |
| |
|
| | def init_user(storage, config): |
| | user = config.get("default_user", {}) |
| | if not user.get("email"): |
| | print("[-] Не указан email пользователя — пропуск.") |
| | return |
| | password = user.get("password") |
| | if not password: |
| | print("[-] Не указан пароль пользователя — пропуск.") |
| | return |
| |
|
| | password_hash = generate_password_hash(password) |
| | did = generate_did() |
| | user_entry = { |
| | "username": user.get("username", "user"), |
| | "badges": user.get("badges", ""), |
| | "mail": user["email"], |
| | "password_hash": password_hash, |
| | "did": did, |
| | "ban": None, |
| | "info": json.dumps({}), |
| | "contacts": json.dumps([]), |
| | "language": "ru,en", |
| | "operator": 1 |
| | } |
| | storage.add_user(user_entry) |
| | print(f"[+] Пользователь {user['username']} добавлен.") |
| |
|
| | def init_llm_backends(storage, config): |
| | backends = config.get("llm_backends", []) |
| | storage.clear_llm_registry() |
| | for backend in backends: |
| | backend_id = str(uuid.uuid4()) |
| | desc = f"{backend.get('type', 'unknown')} model" |
| | llm = { |
| | "id": backend_id, |
| | "name": backend["name"], |
| | "endpoint": desc, |
| | "metadata": json.dumps(backend), |
| | "created_at": datetime.now(UTC).replace(microsecond=0).isoformat() |
| | } |
| | storage.add_llm(llm) |
| | print(f"[+] Зарегистрирован LLM: {backend['name']}") |
| |
|
| | def init_config_table(storage, config): |
| | exclude_keys = {"default_user", "llm_backends"} |
| | flat_config = {k: v for k, v in config.items() if k not in exclude_keys} |
| |
|
| | |
| | for addr_key in ("local_addresses", "global_addresses"): |
| | value = flat_config.get(addr_key) |
| | if not value: |
| | flat_config[addr_key] = [] |
| | else: |
| | flat_config[addr_key] = expand_addresses(value) |
| |
|
| | |
| | for key, value in flat_config.items(): |
| | storage.set_config(key, json.dumps(value)) |
| | print("[+] Конфигурация сохранена в БД.") |
| |
|
| | def update_pow_for_addresses(storage, difficulty=4): |
| | """ |
| | Обновляет PoW для всех локальных и глобальных адресов агента. |
| | Используется новый формат: |
| | { |
| | "addr": "tcp://95.69.92.23:8010", |
| | "nonce": 108834, |
| | "pow_hash": "0000665ea1440781356d7a9b899fc03a01a4f8342d3cfa3d75bb2619b66b4cfb", |
| | "difficulty": 4, |
| | "datetime": "2025-08-28T11:00:00+00:00" |
| | } |
| | """ |
| | raw_id = storage.get_config_value("agent_id") |
| | if not raw_id: |
| | print("[-] Нет agent_id в config — пропуск обновления PoW.") |
| | return |
| | agent_id = storage.normalize_did(raw_id) |
| | pubkey = storage.get_config_value("pubkey") |
| |
|
| | if not agent_id or not pubkey: |
| | print("[-] Нет agent_id/pubkey в config — пропуск обновления PoW.") |
| | return |
| |
|
| | for addr_key in ("local_addresses", "global_addresses"): |
| | raw = storage.get_config_value(addr_key) |
| | if not raw: |
| | continue |
| |
|
| | |
| | if isinstance(raw, str): |
| | try: |
| | addresses = json.loads(raw) |
| | except Exception as e: |
| | print(f"[!] Ошибка при чтении {addr_key}: {e}") |
| | continue |
| | else: |
| | addresses = raw |
| |
|
| | enriched = [] |
| | for addr in addresses: |
| | dt_now = datetime.now(UTC).replace(microsecond=0).isoformat() |
| |
|
| | if isinstance(addr, dict) and "addr" in addr and "pow_hash" in addr: |
| | |
| | enriched.append(addr) |
| | continue |
| |
|
| | |
| | addr_str = addr if isinstance(addr, str) else addr.get("address") |
| | dt_now = datetime.now(UTC).replace(microsecond=0).isoformat() |
| | if addr_str: |
| | nonce, hash_value, dt_now = storage.generate_pow( |
| | peer_id=agent_id, |
| | pubkey=pubkey, |
| | address=addr_str, |
| | dt=dt_now, |
| | difficulty=difficulty |
| | ) |
| | enriched.append({ |
| | "addr": addr_str, |
| | "nonce": nonce, |
| | "pow_hash": hash_value, |
| | "difficulty": difficulty, |
| | "datetime": dt_now |
| | }) |
| |
|
| | storage.set_config(addr_key, json.dumps(enriched)) |
| |
|
| | print("[+] Адреса обновлены с PoW в новом формате с datetime.") |
| |
|
| | def init_prompts_and_ethics(): |
| | folder = os.path.dirname(__file__) |
| | prompt_files = [ |
| | ("prompt.md", "full"), |
| | ("prompt-short.md", "short") |
| | ] |
| | ethics_file = "ethics.yml" |
| |
|
| | with sqlite3.connect(DB_PATH) as conn: |
| | cur = conn.cursor() |
| | |
| | |
| | for fname, ptype in prompt_files: |
| | fpath = os.path.join(folder, fname) |
| | if not os.path.exists(fpath): |
| | print(f"[-] Файл {fname} не найден, пропуск.") |
| | continue |
| | with open(fpath, "r", encoding="utf-8") as f: |
| | content = f.read() |
| | pid = hashlib.sha256(f"{fname}:{ptype}".encode()).hexdigest() |
| | cur.execute(""" |
| | INSERT INTO system_prompts (id, name, type, version, source, content, updated_at) |
| | VALUES (?, ?, ?, ?, ?, ?, ?) |
| | ON CONFLICT(id) DO UPDATE SET |
| | content=excluded.content, |
| | updated_at=excluded.updated_at |
| | """, (pid, fname, ptype, "1.0", "local", content, datetime.now(UTC).replace(microsecond=0).isoformat())) |
| | print(f"[+] Загружен промпт: {fname} ({ptype})") |
| |
|
| | |
| | efpath = os.path.join(folder, ethics_file) |
| | if os.path.exists(efpath): |
| | with open(efpath, "r", encoding="utf-8") as f: |
| | ethics_data = yaml.safe_load(f) |
| |
|
| | eid = ethics_data.get("id", "default_ethics") |
| | cur.execute(""" |
| | INSERT INTO ethics_policies ( |
| | id, version, source, |
| | sync_enabled, mesh_endpoint, consensus_threshold, check_interval, |
| | model_type, model_weights_json, principles_json, evaluation_json, |
| | violation_policy_json, audit_json, updated_at |
| | ) |
| | VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) |
| | ON CONFLICT(id) DO UPDATE SET |
| | version=excluded.version, |
| | source=excluded.source, |
| | sync_enabled=excluded.sync_enabled, |
| | mesh_endpoint=excluded.mesh_endpoint, |
| | consensus_threshold=excluded.consensus_threshold, |
| | check_interval=excluded.check_interval, |
| | model_type=excluded.model_type, |
| | model_weights_json=excluded.model_weights_json, |
| | principles_json=excluded.principles_json, |
| | evaluation_json=excluded.evaluation_json, |
| | violation_policy_json=excluded.violation_policy_json, |
| | audit_json=excluded.audit_json, |
| | updated_at=excluded.updated_at |
| | """, ( |
| | eid, |
| | ethics_data.get("version"), |
| | ethics_data.get("source", "local"), |
| | ethics_data.get("sync", {}).get("enabled", False), |
| | ethics_data.get("sync", {}).get("mesh_endpoint"), |
| | ethics_data.get("sync", {}).get("consensus_threshold"), |
| | ethics_data.get("sync", {}).get("check_interval"), |
| | ethics_data.get("model", {}).get("type"), |
| | json.dumps(ethics_data.get("model", {}).get("weights"), ensure_ascii=False), |
| | json.dumps(ethics_data.get("principles"), ensure_ascii=False), |
| | json.dumps(ethics_data.get("evaluation"), ensure_ascii=False), |
| | json.dumps(ethics_data.get("violation_policy"), ensure_ascii=False), |
| | json.dumps(ethics_data.get("audit"), ensure_ascii=False), |
| | datetime.now(UTC).replace(microsecond=0).isoformat() |
| | )) |
| | print(f"[+] Загружена этическая политика: {eid}") |
| | else: |
| | print(f"[-] Файл {ethics_file} не найден, пропуск.") |
| |
|
| | def ensure_directories(): |
| | for folder in ["logs", "scripts"]: |
| | full_path = os.path.abspath(os.path.join(os.path.dirname(__file__), folder)) |
| | if not os.path.exists(full_path): |
| | os.makedirs(full_path) |
| | print(f"[+] Создан каталог: {full_path}") |
| | else: |
| | print(f"[=] Каталог уже существует: {full_path}") |
| |
|
| | def is_db_initialized(db_path): |
| | if not os.path.exists(db_path): |
| | return False |
| | try: |
| | with sqlite3.connect(db_path) as conn: |
| | cursor = conn.cursor() |
| | cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='identity'") |
| | return cursor.fetchone() is not None |
| | except Exception: |
| | return False |
| |
|
| | def ensure_db_initialized(): |
| | config = load_config(CONFIG_PATH) |
| |
|
| | if not is_db_initialized(DB_PATH): |
| | print("[*] БД не инициализирована — выполняем инициализацию.") |
| | try: |
| | ensure_directories() |
| | storage = Storage() |
| | init_identity(storage, config) |
| | init_user(storage, config) |
| | init_llm_backends(storage, config) |
| | init_config_table(storage, config) |
| | update_pow_for_addresses(storage, difficulty=4) |
| | save_config(CONFIG_PATH, config) |
| | init_prompts_and_ethics() |
| | except Exception as e: |
| | print(f"[!] Ошибка при инициализации: {e}") |
| | sys.exit(1) |
| | else: |
| | print("[=] БД уже инициализирована.") |
| |
|
| | return config |
| |
|
| | if __name__ == "__main__": |
| | ensure_db_initialized() |