#!/usr/bin/env python3
"""
Index des connaissances Funk dans Qdrant (hermes_memory).
Exécuter avec : /srv/data/hermes/hermes-agent/venv/bin/python index_knowledge.py
"""

import requests
import uuid
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct, VectorParams, Distance

EMBEDDINGS_URL = "http://192.168.10.20:1234/v1/embeddings"
EMBEDDINGS_MODEL = "qwen3-8b"
QDRANT_HOST = "localhost"
QDRANT_PORT = 6333
COLLECTION = "hermes_memory"
VECTOR_DIM = 4096

# --- Connaissances à indexer ---

KNOWLEDGE = [
    {
        "category": "architecture",
        "tags": ["homelab", "funk", "réseau", "machines"],
        "text": "Funk homelab: storage-01 (192.168.10.1 LAN cluster, 192.168.1.200 LAN domestique, AlmaLinux 9.7) est passerelle + bastion + données. gpu-01 (192.168.10.20, AlmaLinux 9.7) héberge llama-server avec RX 6700XT. Cluster Kubernetes Talos sur compute-01 (192.168.10.11), compute-02 (192.168.10.12), compute-03 (192.168.10.13). storage-01 et gpu-01 sont hors du cluster k8s.",
    },
    {
        "category": "architecture",
        "tags": ["réseau", "kubernetes", "metallb", "dns"],
        "text": "Réseau Kubernetes: CNI Flannel pods 10.42.0.0/16, services 10.43.0.0/16, MetalLB pool 192.168.10.200-230. Domaine lab.local avec wildcard DNS via dnsmasq sur storage-01. Ingress: <service>.lab.local vers IP MetalLB de Traefik.",
    },
    {
        "category": "litellm",
        "tags": ["litellm", "proxy", "modèle", "switch"],
        "text": "LiteLLM tourne sur storage-01 port 4000. master_key doit valoir exactement lm-studio. Hermes utilise l'alias hermes-default. Pour switcher: sudo hermes-switch qwen (Qwen3-8B local gratuit) ou sudo hermes-switch claude (Claude Sonnet 4.6 ~1-2$ par session). Le script modifie /etc/litellm/config.yaml et redémarre le service.",
    },
    {
        "category": "litellm",
        "tags": ["litellm", "config", "modèles", "anthropic"],
        "text": "LiteLLM config /etc/litellm/config.yaml expose: hermes-default (alias switchable), qwen3-8b (gpu-01:1234 GPU), claude-sonnet-4-6 (API Anthropic), claude-opus-4-7 (API Anthropic). Les alias CPU qwen3-1.7b-system/-monitor ont été retirés le 2026-06-20 avec leurs serveurs. ANTHROPIC_API_KEY dans l'environnement systemd via vault Ansible.",
    },
    {
        "category": "hermes",
        "tags": ["hermes", "config", "LM_API_KEY", "provider"],
        "text": "LM_API_KEY doit valoir exactement lm-studio pour activer le provider LM Studio dans Hermes. Toute autre valeur échoue silencieusement. Le fichier /srv/data/hermes/.env peut écraser les variables systemd — Ansible gère les deux via lineinfile.",
    },
    {
        "category": "hermes",
        "tags": ["hermes", "config", "config.yaml", "provider"],
        "text": "config.yaml Hermes doit contenir model.provider: lmstudio ET model.base_url: http://127.0.0.1:4000/v1. Sans provider, l'inférence échoue avec AuthError même si base_url est correct. model.default: hermes-default pointe sur l'alias LiteLLM.",
    },
    {
        "category": "hermes",
        "tags": ["hermes", "cli", "path", "git"],
        "text": "Toujours lancer hermes -z depuis /srv/data/hermes pour éviter l'erreur PermissionError sur .git. Hermes remonte l'arborescence à la recherche d'un .git et échoue s'il tombe sur /home/ansible/.git sans permission. HERMES_HOME=/srv/data/hermes, données sur NVMe (bind-mount /home/data depuis 2026-05-29).",
    },
    {
        "category": "hermes",
        "tags": ["hermes", "mémoire", "sessions", "qdrant"],
        "text": "Hermes a deux systèmes de mémoire: mémoire native dans /srv/data/hermes/memories/ (texte, automatique, recherche par mots-clés) et Qdrant (vectoriel, sémantique, skill mlops/qdrant installé). Le skill qdrant-vector-search est actif. qdrant-client v1.18 dans le venv /srv/data/hermes/hermes-agent/venv/.",
    },
    {
        "category": "hermes",
        "tags": ["hermes", "coût", "tokens", "claude"],
        "text": "Coût Hermes avec Claude Sonnet 4.6: system prompt ~15000 tokens par échange, contexte s'accumule. Une session de debug ~375k tokens input = ~1.20$. Utiliser Qwen local (gratuit) pour les tâches courantes, Claude uniquement pour le debug complexe ponctuel.",
    },
    {
        "category": "gpu",
        "tags": ["rocm", "amd", "gfx1031", "HSA", "llama-server"],
        "text": "gpu-01 RX 6700XT (gfx1031) non officiellement supportée par ROCm. Toujours définir HSA_OVERRIDE_GFX_VERSION=10.3.0 dans l'environnement llama-server et tout service ROCm. Sans cette variable ROCm échoue. Défini dans le service systemd llama-server via Ansible.",
    },
    {
        "category": "gpu",
        "tags": ["llama-server", "embeddings", "pooling", "vecteurs"],
        "text": "llama-server doit être démarré avec --embeddings --pooling mean pour exposer /v1/embeddings compatible OpenAI. Sans --pooling mean: erreur 'Pooling type none is not OAI compatible'. Qwen3-8B produit des vecteurs de dimension 3584. Endpoint: http://192.168.10.20:1234/v1/embeddings.",
    },
    {
        "category": "gpu",
        "tags": ["modèles", "qwen", "gemma", "performance"],
        "text": "Modèle actif gpu-01: Qwen3-8B Q4_K_M (~60 tok/s génération, ~300 tok/s prefill, ROCm 7.x natif). Contexte 32768 tokens (VRAM: ~5GB modèle + ~4GB KV cache = ~9GB sur 12GB). Alias API: qwen3-8b. Instance embed: nomic-embed-text sur :1238 (CPU, dim 768) pour mémoire STT + RAG funk-docs. Les anciennes instances CPU qwen3-1.7b-system (:1236), qwen3-1.7b-monitor (:1237) et dev (:1235) ont été RETIRÉES le 2026-06-20 (inutilisées). Ne pas utiliser Gemma 4: crash ROCm >800 tokens.",
    },
    {
        "category": "postgresql",
        "tags": ["postgresql", "pgdg", "PGDATA", "RAID5"],
        "text": "PostgreSQL 16 installé depuis repo officiel pgdg sur storage-01. PGDATA sur NVMe: /srv/data/postgres (bind-mount /home/data depuis 2026-05-29). Override systemd dans /etc/systemd/system/postgresql-16.service.d/pgdata.conf. Auth: peer en local, scram-sha-256 pour connexions réseau. Bases: hermes, litellm, n8n, openwebui avec utilisateurs dédiés.",
    },
    {
        "category": "qdrant",
        "tags": ["qdrant", "api", "client", "python"],
        "text": "Qdrant v1.13.4 sur storage-01 port 6333 (HTTP) et 6334 (gRPC). Données: /srv/data/qdrant. qdrant-client v1.18 installé dans /srv/data/hermes/hermes-agent/venv/. Utiliser client.query_points() et non client.search() (supprimé en v1.14+). Collection hermes_memory: dim=3584, distance=COSINE.",
    },
    {
        "category": "firewall",
        "tags": ["nftables", "firewall", "ports", "ansible"],
        "text": "storage-01 utilise nftables géré centralement par le rôle gateway Ansible (nftables.conf.j2). Ne jamais ajouter de tâches firewalld dans les autres rôles. Ports ouverts cluster (192.168.10.0/24): 5432 PostgreSQL, 6333-6334 Qdrant, 8080 Hermes gateway, 9000-9001 MinIO. SSH ouvert depuis LAN domestique et cluster.",
    },
    {
        "category": "ansible",
        "tags": ["ansible", "vault", "secrets", "variables"],
        "text": "Secrets Ansible dans group_vars/all/vault.yml: vault_anthropic_api_key, vault_pg_hermes_password, vault_pg_litellm_password, vault_pg_n8n_password, vault_pg_openwebui_password, vault_postfix_relay_password. Rôles idempotents: common, gateway, dnsmasq, nfs_server, postgresql, qdrant, minio, litellm, hermes_agent, rag, alertmanager_webhook, postfix_relay, node_exporter, llama_server, rocm. Playbooks: storage-01.yml et gpu-01.yml.",
    },
    {
        "category": "ansible",
        "tags": ["ansible", "tags", "déploiement", "commandes"],
        "text": "Commandes Ansible: cd ansible/ && ansible-playbook -i inventory.yml playbooks/storage-01.yml --tags postgresql. Pour gpu-01: ansible-playbook -i inventory.yml playbooks/gpu-01.yml --tags llama_server. Dry-run: ajouter --check. Collections requises: ansible.posix, community.general, community.postgresql.",
    },
]


def get_embedding(text: str) -> list[float]:
    response = requests.post(
        EMBEDDINGS_URL,
        json={"model": EMBEDDINGS_MODEL, "input": [text]},
        timeout=30,
    )
    response.raise_for_status()
    return response.json()["data"][0]["embedding"]


def main():
    client = QdrantClient(QDRANT_HOST, port=QDRANT_PORT)

    # Recréer la collection proprement
    existing = [c.name for c in client.get_collections().collections]
    if COLLECTION in existing:
        client.delete_collection(COLLECTION)
        print(f"Collection '{COLLECTION}' supprimée (recréation propre)")

    client.create_collection(
        collection_name=COLLECTION,
        vectors_config=VectorParams(size=VECTOR_DIM, distance=Distance.COSINE),
    )
    print(f"Collection '{COLLECTION}' créée (dim={VECTOR_DIM}, cosine)")

    points = []
    for i, item in enumerate(KNOWLEDGE):
        print(f"[{i+1}/{len(KNOWLEDGE)}] Embedding: {item['text'][:60]}...")
        embedding = get_embedding(item["text"])
        points.append(
            PointStruct(
                id=i + 1,
                vector=embedding,
                payload={
                    "text": item["text"],
                    "category": item["category"],
                    "tags": item["tags"],
                    "source": "funk-sessions-2026-05",
                },
            )
        )

    client.upsert(collection_name=COLLECTION, points=points)
    print(f"\nOK — {len(points)} chunks indexés dans '{COLLECTION}'")

    # Test rapide
    print("\n--- Test recherche: 'erreur hermes modele provider' ---")
    query_vec = get_embedding("erreur hermes modele provider")
    results = client.query_points(
        collection_name=COLLECTION,
        query=query_vec,
        limit=3,
    )
    for r in results.points:
        print(f"  Score={r.score:.4f} [{r.payload['category']}] {r.payload['text'][:80]}...")


if __name__ == "__main__":
    main()
