316 lines
9.0 KiB
Markdown
316 lines
9.0 KiB
Markdown
|
|
# Trilium Automation Setup - Fichier Unique avec Tous les Scripts
|
||
|
|
# Projet : Context Continuity
|
||
|
|
# Date : 27 mai 2026
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Description
|
||
|
|
Ce fichier contient tous les scripts Python et configurations nécessaires pour automatiser la gestion de contexte multi-LLM avec Trilium.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Structure des Fichiers
|
||
|
|
```
|
||
|
|
trilium_automation/
|
||
|
|
├── init_trilium_architecture.py # Initialise l'arborescence Trilium
|
||
|
|
├── trilium_api.py # Bibliothèque pour interagir avec l'API ETAPI
|
||
|
|
├── create_conversation.py # Crée des notes de conversation LLM
|
||
|
|
├── generate_context.py # Génère un contexte pour les LLM
|
||
|
|
└── .env.example # Exemple de fichier d'environnement
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. Fichier .env.example
|
||
|
|
```ini
|
||
|
|
# Fichier : .env
|
||
|
|
# À placer dans /volume1/homes/Master/App/Automation/
|
||
|
|
# Ne jamais commiter ce fichier dans Git !
|
||
|
|
|
||
|
|
TRILIUM_TOKEN=ton_token_etapi_ici
|
||
|
|
TRILIUM_API_URL=http://localhost:4292/etapi
|
||
|
|
ROOT_NOTE_ID=root
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. Script : init_trilium_architecture.py
|
||
|
|
```python
|
||
|
|
import requests
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
from dotenv import load_dotenv
|
||
|
|
|
||
|
|
# Charger les variables d'environnement depuis .env
|
||
|
|
load_dotenv()
|
||
|
|
|
||
|
|
# ===== CONFIGURATION =====
|
||
|
|
TRILIUM_API_URL = os.getenv("TRILIUM_API_URL", "http://localhost:4292/etapi")
|
||
|
|
TRILIUM_TOKEN = os.getenv("TRILIUM_TOKEN")
|
||
|
|
HEADERS = {
|
||
|
|
"Authorization": TRILIUM_TOKEN,
|
||
|
|
"Content-Type": "application/json"
|
||
|
|
}
|
||
|
|
|
||
|
|
# ID de la note racine (root) - à remplacer par le tien
|
||
|
|
ROOT_NOTE_ID = os.getenv("ROOT_NOTE_ID", "root")
|
||
|
|
|
||
|
|
# ===== FONCTIONS =====
|
||
|
|
def create_note(parent_id, title, note_type="book"):
|
||
|
|
"""Crée une note de type 'book' (dossier) ou 'text' dans Trilium."""
|
||
|
|
data = {
|
||
|
|
"parentNoteId": parent_id,
|
||
|
|
"title": title,
|
||
|
|
"type": note_type,
|
||
|
|
"content": f"# {title}\n\n*Dossier créé automatiquement via API ETAPI.*"
|
||
|
|
}
|
||
|
|
response = requests.post(
|
||
|
|
f"{TRILIUM_API_URL}/notes",
|
||
|
|
headers=HEADERS,
|
||
|
|
json=data
|
||
|
|
)
|
||
|
|
if response.status_code == 200:
|
||
|
|
note_id = response.json().get("noteId")
|
||
|
|
print(f"✅ Dossier créé : {title} (ID: {note_id})")
|
||
|
|
return note_id
|
||
|
|
else:
|
||
|
|
raise Exception(f"❌ Erreur {response.status_code} : {response.text}")
|
||
|
|
|
||
|
|
def get_note_id_by_title(title, parent_id):
|
||
|
|
"""Récupère l'ID d'une note par son titre (pour éviter les doublons)."""
|
||
|
|
search_url = f"{TRILIUM_API_URL}/search?q=title:{title}"
|
||
|
|
response = requests.get(search_url, headers=HEADERS)
|
||
|
|
if response.status_code == 200:
|
||
|
|
notes = response.json()
|
||
|
|
for note in notes:
|
||
|
|
if note.get("title") == title and note.get("parentNoteId") == parent_id:
|
||
|
|
return note.get("noteId")
|
||
|
|
return None
|
||
|
|
|
||
|
|
# ===== EXÉCUTION =====
|
||
|
|
def main():
|
||
|
|
print(" Initialisation de l'architecture Trilium pour 'Context Continuity'...")
|
||
|
|
|
||
|
|
# 1. Créer le dossier principal "Context Continuity" (s'il n'existe pas)
|
||
|
|
context_continuity_id = get_note_id_by_title("Context Continuity", ROOT_NOTE_ID)
|
||
|
|
if not context_continuity_id:
|
||
|
|
context_continuity_id = create_note(ROOT_NOTE_ID, "Context Continuity", "book")
|
||
|
|
else:
|
||
|
|
print(f"✅ Dossier existant : Context Continuity (ID: {context_continuity_id})")
|
||
|
|
|
||
|
|
# 2. Créer les sous-dossiers
|
||
|
|
subfolders = ["Conversations", "Backlog", "Décisions", "Glossaire", "Historique"]
|
||
|
|
folder_ids = {}
|
||
|
|
|
||
|
|
for folder in subfolders:
|
||
|
|
folder_id = get_note_id_by_title(folder, context_continuity_id)
|
||
|
|
if not folder_id:
|
||
|
|
folder_id = create_note(context_continuity_id, folder, "book")
|
||
|
|
else:
|
||
|
|
print(f"✅ Dossier existant : {folder} (ID: {folder_id})")
|
||
|
|
folder_ids[folder] = folder_id
|
||
|
|
|
||
|
|
# 3. Afficher les IDs pour référence
|
||
|
|
print("\n IDs des dossiers créés (à utiliser dans tes scripts) :")
|
||
|
|
for folder, folder_id in folder_ids.items():
|
||
|
|
print(f"{folder}: {folder_id}")
|
||
|
|
|
||
|
|
# 4. Sauvegarder les IDs dans un fichier JSON
|
||
|
|
with open("trilium_folder_ids.json", "w") as f:
|
||
|
|
json.dump({
|
||
|
|
"root_id": ROOT_NOTE_ID,
|
||
|
|
"context_continuity_id": context_continuity_id,
|
||
|
|
**folder_ids
|
||
|
|
}, f, indent=4)
|
||
|
|
print("\n IDs sauvegardés dans trilium_folder_ids.json")
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. Script : trilium_api.py
|
||
|
|
```python
|
||
|
|
import requests
|
||
|
|
import os
|
||
|
|
from dotenv import load_dotenv
|
||
|
|
|
||
|
|
# Charger les variables d'environnement
|
||
|
|
load_dotenv()
|
||
|
|
|
||
|
|
TRILIUM_API_URL = os.getenv("TRILIUM_API_URL", "http://localhost:4292/etapi")
|
||
|
|
TRILIUM_TOKEN = os.getenv("TRILIUM_TOKEN")
|
||
|
|
HEADERS = {
|
||
|
|
"Authorization": TRILIUM_TOKEN,
|
||
|
|
"Content-Type": "application/json"
|
||
|
|
}
|
||
|
|
|
||
|
|
def create_note(parent_id, title, content, note_type="text"):
|
||
|
|
"""Crée une note dans Trilium."""
|
||
|
|
data = {
|
||
|
|
"parentNoteId": parent_id,
|
||
|
|
"title": title,
|
||
|
|
"type": note_type,
|
||
|
|
"content": content
|
||
|
|
}
|
||
|
|
response = requests.post(
|
||
|
|
f"{TRILIUM_API_URL}/notes",
|
||
|
|
headers=HEADERS,
|
||
|
|
json=data
|
||
|
|
)
|
||
|
|
if response.status_code == 200:
|
||
|
|
return response.json().get("noteId")
|
||
|
|
else:
|
||
|
|
raise Exception(f"Erreur {response.status_code} : {response.text}")
|
||
|
|
|
||
|
|
def get_note(note_id):
|
||
|
|
"""Récupère une note par son ID."""
|
||
|
|
response = requests.get(
|
||
|
|
f"{TRILIUM_API_URL}/notes/{note_id}",
|
||
|
|
headers=HEADERS
|
||
|
|
)
|
||
|
|
return response.json()
|
||
|
|
|
||
|
|
def update_note(note_id, content):
|
||
|
|
"""Met à jour une note existante."""
|
||
|
|
data = {"content": content}
|
||
|
|
response = requests.put(
|
||
|
|
f"{TRILIUM_API_URL}/notes/{note_id}",
|
||
|
|
headers=HEADERS,
|
||
|
|
json=data
|
||
|
|
)
|
||
|
|
return response.status_code == 200
|
||
|
|
|
||
|
|
def search_notes(query):
|
||
|
|
"""Recherche des notes par titre ou contenu."""
|
||
|
|
response = requests.get(
|
||
|
|
f"{TRILIUM_API_URL}/search?q={query}",
|
||
|
|
headers=HEADERS
|
||
|
|
)
|
||
|
|
return response.json()
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. Script : create_conversation.py
|
||
|
|
```python
|
||
|
|
from trilium_api import create_note
|
||
|
|
from datetime import datetime
|
||
|
|
import os
|
||
|
|
from dotenv import load_dotenv
|
||
|
|
|
||
|
|
load_dotenv()
|
||
|
|
|
||
|
|
# IDs des dossiers (à remplacer par les tiens)
|
||
|
|
CONVERSATIONS_ID = os.getenv("CONVERSATIONS_ID", "ID_DU_DOSSIER_CONVERSATIONS")
|
||
|
|
|
||
|
|
def create_conversation(llm, title, content):
|
||
|
|
"""Crée une note de conversation dans Trilium."""
|
||
|
|
date = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||
|
|
note_title = f"[{llm}] - {date} - {title}"
|
||
|
|
note_content = f"""# {note_title}
|
||
|
|
|
||
|
|
**Projet** : [[Context Continuity]]
|
||
|
|
**LLM** : {llm}
|
||
|
|
**Date** : {date}
|
||
|
|
**Statut** : En cours
|
||
|
|
|
||
|
|
---
|
||
|
|
## Contexte
|
||
|
|
{content}
|
||
|
|
|
||
|
|
---
|
||
|
|
**Liens** :
|
||
|
|
- [[Backlog/]]
|
||
|
|
- [[Décisions/]]
|
||
|
|
"""
|
||
|
|
note_id = create_note(
|
||
|
|
parent_id=CONVERSATIONS_ID,
|
||
|
|
title=note_title,
|
||
|
|
content=note_content
|
||
|
|
)
|
||
|
|
print(f"Note créée : {note_id} ({note_title})")
|
||
|
|
return note_id
|
||
|
|
|
||
|
|
# Exemple d'utilisation
|
||
|
|
if __name__ == "__main__":
|
||
|
|
create_conversation(
|
||
|
|
llm="Le Chat",
|
||
|
|
title="Configuration Trilium pour Context Continuity",
|
||
|
|
content="Discussion sur la configuration de Trilium pour synchroniser les contextes entre LLM."
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. Script : generate_context.py
|
||
|
|
```python
|
||
|
|
from trilium_api import search_notes, get_note
|
||
|
|
from datetime import datetime
|
||
|
|
import os
|
||
|
|
from dotenv import load_dotenv
|
||
|
|
|
||
|
|
load_dotenv()
|
||
|
|
|
||
|
|
CONTEXT_CONTINUITY_ID = os.getenv("CONTEXT_CONTINUITY_ID", "ID_DU_DOSSIER_CONTEXT_CONTINUITY")
|
||
|
|
|
||
|
|
def generate_context(project_name, limit=10):
|
||
|
|
"""Génère un contexte pour un projet donné en agrégeant les notes Trilium."""
|
||
|
|
query = f"projet:{project_name}"
|
||
|
|
notes = search_notes(query)
|
||
|
|
|
||
|
|
context = f"# Contexte du projet : {project_name}\n\n"
|
||
|
|
context += "## Conversations récentes\n"
|
||
|
|
for note in notes[:limit]:
|
||
|
|
note_data = get_note(note["noteId"])
|
||
|
|
context += f"- **{note_data['title']}** (LLM: {note_data.get('llm', 'N/A')}, Date: {note_data.get('date', 'N/A')})\n"
|
||
|
|
context += f" {note_data['content'][:200]}...\n\n"
|
||
|
|
|
||
|
|
context += f"\n*Généré le {datetime.now().strftime('%Y-%m-%d %H:%M')}*"
|
||
|
|
return context
|
||
|
|
|
||
|
|
# Exemple d'utilisation
|
||
|
|
if __name__ == "__main__":
|
||
|
|
context = generate_context("Context Continuity")
|
||
|
|
print(context)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Instructions d'Utilisation
|
||
|
|
|
||
|
|
### 1. Préparer l'environnement
|
||
|
|
```bash
|
||
|
|
# Installer les dépendances
|
||
|
|
pip install requests python-dotenv
|
||
|
|
|
||
|
|
# Créer le fichier .env
|
||
|
|
cd /volume1/homes/Master/App/Automation/
|
||
|
|
echo "TRILIUM_TOKEN=ton_token_etapi" > .env
|
||
|
|
echo "TRILIUM_API_URL=http://localhost:4292/etapi" >> .env
|
||
|
|
echo "ROOT_NOTE_ID=root" >> .env
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Exécuter les scripts
|
||
|
|
```bash
|
||
|
|
# Initialiser l'arborescence Trilium
|
||
|
|
python3 init_trilium_architecture.py
|
||
|
|
|
||
|
|
# Créer une conversation
|
||
|
|
python3 create_conversation.py
|
||
|
|
|
||
|
|
# Générer un contexte
|
||
|
|
python3 generate_context.py > contexte.md
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Vérifier les résultats
|
||
|
|
- Ouvre Trilium et vérifie que les dossiers et notes sont créés.
|
||
|
|
- Le fichier `trilium_folder_ids.json` contient les IDs des dossiers.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Notes Importantes
|
||
|
|
- **Ne jamais partager le fichier .env** (il contient ton token ETAPI).
|
||
|
|
- Les scripts supposent que Trilium est accessible via `http://localhost:4292/etapi`.
|
||
|
|
- Si tu utilises le Reverse Proxy, remplace `TRILIUM_API_URL` par `https://trilium.bertha-cloud.fr/etapi`.
|