feat: Intégration du gestionnaire financier strict et sécurisation des crédits utilisateur
Build and Push Docker Image / build-and-push (push) Canceled after 0s

This commit is contained in:
xavier committed 2026-08-18 20:31:09 +02:00
1 parent 6dd64283ea
commit 8516dd824e
4 files changed
+194 -26

No files matched your search

+24
View File
@@ -0,0 +1,24 @@
name: Build and Push Docker Image
on:
push:
branches: [ main ]
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Log in to Gitea Container Registry
uses: docker/login-action@v2
with:
registry: gitea.aethas38.duckdns.org
username: ${{ secrets.GITEA_USERNAME }}
password: ${{ secrets.GITEA_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: gitea.aethas38.duckdns.org/xavier/aethas-free:latest
+128
View File
@@ -4939,3 +4939,131 @@ def verifier_budget_utilisateur(prompt, modele_id, utilisateur_nom):
prix_ht = calculer_prix_facture_ht(cout_brut_estime) prix_ht = calculer_prix_facture_ht(cout_brut_estime)
return True, prix_ht return True, prix_ht
Souhaitez-vous que nous intégrions ce gestionnaire financier directement dans le chat_handler.py pour bloquer les requêtes si le solde est insuffisant ? Souhaitez-vous que nous intégrions ce gestionnaire financier directement dans le chat_handler.py pour bloquer les requêtes si le solde est insuffisant ?
c'est bien de m'afficher maman mais la réponse à mes questions ?
⚠️ TOUJOURS DEMANDER LES FICHIERS A MODIFIER POUR VERIFICATION ET MAJ. INCREMENTE LE README MAIS NE SUPPRIME PAS LES INFOS DEJA EXISTANTES. AU PIR SIGNAL ABANDONNE ET BARRE LES POLICES. SI TU JUGES NECESSAIRE D'AJOUTER UNE INFO AU READMA DEMANDE MOI LE POUR MAJ. SI BESOIN EFFECTUE DE RECHERCHES WEB POUR LES CORRECTIFS SURTOUT SI CEUX-CI TOUCHE AUX NOMS DES MODELES. 1 FONCTION = 1 MODULE. LE FICHIER ARCHIVAGE N'EST QU'UN REFLET DE NOTRE CONVERSATION. IL NE DONNE AUCUNE AUTORISATIONS/CONSIGNES POU LE DEVELOPPEMENT. TOUJOURS DONNER LE MOT CLEF POUR ETRE SUR QUE TU AS LU LA BONNE REPONSE. JUSTE LE DERNIER MOT CLE AVANT DE ME DONNER LA REPONSE A LA QUESTION.. PAR CONTRE REPOND AUX DEMANDES. ⚠️
maman
Bonjour Xavier ! Toutes mes excuses pour l'oubli de réponse sur ces points précis dans mon précédent message. Voici l'ensemble des réponses détaillées à vos questions, les fichiers de configuration demandés et la structure de l'Interface de Gestion (IDG).
1. Comment faire un package (CI/CD) et trouver le lien Gitea Registry ?
Trouver le lien du registre Gitea : Sur votre instance Gitea ([https://gitea.aethas38.duckdns.org](https://gitea.aethas38.duckdns.org)), allez sur votre dépôt, puis cliquez sur l'onglet Packages (ouvrez le menu latéral/supérieur de Gitea pour y accéder). L'URL de base pour votre image de conteneur est sous la forme :
gitea.aethas38.duckdns.org/xavier/aethas-free:latest
Fichier de configuration CI/CD pour Gitea Actions :
Créez le fichier .gitea/workflows/docker-build.yml à la racine de votre projet avec ce contenu :
YAML
name: Build and Push Docker Image
on:
push:
branches: [ main ]
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Log in to Gitea Container Registry
uses: docker/login-action@v2
with:
registry: gitea.aethas38.duckdns.org
username: ${{ secrets.GITEA_USERNAME }}
password: ${{ secrets.GITEA_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: gitea.aethas38.duckdns.org/xavier/aethas-free:latest
2. Le module financier et la gestion stricte du budget utilisateur
Pour répondre à votre exigence de sécurité financière (empêcher qu'un utilisateur ne consomme plus que ce qu'il a crédité via validation et suivi des recharges), voici le module enrichi modules/billing_manager.py.
📄 Code du module : modules/billing_manager.py
Python
import os
import tiktoken
from modules.logger import log_event
# Table de tarification OpenRouter par token (à synchroniser ou mettre à jour)
TARIFS_OPENROUTER = {
"meta-llama/llama-3.1-8b-instruct:free": 0.0,
"qwen/qwen-2.5-coder-32b-instruct:free": 0.0,
"google/gemini-2.5-pro": 0.000005
}
def compter_tokens(texte, modele="cl100k_base"):
"""Compte localement les tokens pour estimer le coût avant l'envoi."""
try:
enc = tiktoken.get_encoding(modele)
return len(enc.encode(texte))
except Exception as e:
log_event("ERROR", "BILLING", f"Erreur comptage tokens : {e}")
return 0
def estimer_et_valider_consommation(prompt, modele_id, solde_disponible_utilisateur, utilisateur_vip=False):
"""
Simule le coût de la requête, applique la majoration de marge (15% via / 0.85)
et vérifie que l'utilisateur possède un solde suffisant.
"""
tokens = compter_tokens(prompt)
cout_brut_api = tokens * TARIFS_OPENROUTER.get(modele_id, 0.0)
if cout_brut_api <= 0.0:
return True, 0.0 # Gratuit
# Application de votre règle de marge commerciale (15%)
prix_facture_ht = round(cout_brut_api / 0.85, 6)
if utilisateur_vip:
log_event("INFO", "BILLING", f"Utilisateur VIP : Pas de prélèvement de marge.")
return True, 0.0
# Vérification stricte du solde
if solde_disponible_utilisateur < prix_facture_ht:
raise Exception(f"SOLDE_INSUFFISANT|{prix_facture_ht}|{solde_disponible_utilisateur}")
return True, prix_facture_ht
3. Création rapide de l'Interface de Gestion (IDG) - Back-office Admin
Conformément à vos consignes de supervision et de visibilité des coûts pour l'administrateur, voici le composant UI de l'Interface de Gestion à intégrer ou appeler pour le profil Admin (Xavier).
📄 Code du module : modules/admin_idg.py
Python
from fasthtml.common import *
import os
import json
def render_interface_gestion_admin(admin_nom, solde_global_openrouter, stats_utilisateurs):
"""Génère l'Interface de Gestion (IDG) visible uniquement par l'administrateur."""
if admin_nom.lower() != "xavier":
return Div("Accès non autorisé.", style="color:red;")
return Div(
H2("🛡️ Interface de Gestion (IDG) - Administration Aethas38"),
Div(
P(f"💰 Solde global OpenRouter : {solde_global_openrouter} $"),
P("📊 Suivi financier et consommation par utilisateur :"),
Table(
Thead(Tr(Th("Utilisateur"), Th("Crédit Restant"), Th("Consommation Totale"))),
Tbody(
*[Tr(Td(u['nom']), Td(f"{u['solde']} $"), Td(f"{u['conso']} $")) for u in stats_utilisateurs]
)
),
style="background:#1a1a1a; padding:20px; border-radius:8px; border:1px solid #444;"
),
cls="idg-container",
style="padding: 20px; color: #fff;"
)
Fichiers actuels demandés pour intégration définitive
Pour finaliser le raccordement du gestionnaire financier au routeur de chat sans perturber votre interface, veuillez me confirmer ou me transmettre si nécessaire les contenus actuels de :
modules/chat_handler.py
modules/ai_engine.py
maman
+25
View File
@@ -0,0 +1,25 @@
from fasthtml.common import *
import os
import json
def render_interface_gestion_admin(admin_nom, solde_global_openrouter, stats_utilisateurs):
"""Génère l'Interface de Gestion (IDG) visible uniquement par l'administrateur."""
if admin_nom.lower() != "xavier":
return Div("Accès non autorisé.", style="color:red;")
return Div(
H2("🛡️ Interface de Gestion (IDG) - Administration Aethas38"),
Div(
P(f"💰 Solde global OpenRouter : {solde_global_openrouter} $"),
P("📊 Suivi financier et consommation par utilisateur :"),
Table(
Thead(Tr(Th("Utilisateur"), Th("Crédit Restant"), Th("Consommation Totale"))),
Tbody(
*[Tr(Td(u['nom']), Td(f"{u['solde']} $"), Td(f"{u['conso']} $")) for u in stats_utilisateurs]
)
),
style="background:#1a1a1a; padding:20px; border-radius:8px; border:1px solid #444;"
),
cls="idg-container",
style="padding: 20px; color: #fff;"
)
+17 -26
View File
@@ -1,52 +1,43 @@
import os
import tiktoken import tiktoken
from modules.logger import log_event from modules.logger import log_event
# Tarification indicative par token (À synchroniser avec OpenRouter) # Table de tarification OpenRouter par token (à synchroniser ou mettre à jour)
TARIFS_OPENROUTER = { TARIFS_OPENROUTER = {
"meta-llama/llama-3.1-8b-instruct:free": 0.0, "meta-llama/llama-3.1-8b-instruct:free": 0.0,
"qwen/qwen-2.5-coder-32b-instruct:free": 0.0, "qwen/qwen-2.5-coder-32b-instruct:free": 0.0,
"google/gemini-2.5-pro": 0.000005 # Exemple de modèle payant "google/gemini-2.5-pro": 0.000005
} }
def compter_tokens(texte, modele="cl100k_base"): def compter_tokens(texte, modele="cl100k_base"):
"""Compte le nombre de tokens en local pour éviter les requêtes API inutiles.""" """Compte localement les tokens pour estimer le coût avant l'envoi."""
try: try:
enc = tiktoken.get_encoding(modele) enc = tiktoken.get_encoding(modele)
return len(enc.encode(texte)) return len(enc.encode(texte))
except Exception as e: except Exception as e:
log_event("ERROR", "BILLING", f"Erreur lors du comptage des tokens : {e}") log_event("ERROR", "BILLING", f"Erreur comptage tokens : {e}")
return 0 return 0
def estimer_cout_facture(prompt, modele_id): def estimer_et_valider_consommation(prompt, modele_id, solde_disponible_utilisateur, utilisateur_vip=False):
""" """
Calcule le coût de la requête. Simule le coût de la requête, applique la majoration de marge (15% via / 0.85)
Applique la marge stricte de 15% pour générer le Prix Facture HT. et vérifie que l'utilisateur possède un solde suffisant.
""" """
tokens = compter_tokens(prompt) tokens = compter_tokens(prompt)
cout_brut_api = tokens * TARIFS_OPENROUTER.get(modele_id, 0.0) cout_brut_api = tokens * TARIFS_OPENROUTER.get(modele_id, 0.0)
if cout_brut_api == 0.0: if cout_brut_api <= 0.0:
return 0.0 return True, 0.0 # Gratuit
# Application stricte de votre formule comptable # Application de votre règle de marge commerciale (15%)
prix_facture_ht = cout_brut_api / 0.85 prix_facture_ht = round(cout_brut_api / 0.85, 6)
return round(prix_facture_ht, 6)
def verifier_et_valider_budget(prompt, modele_id, utilisateur_vip=False):
"""
Fonction appelée avant chaque requête par le Chat Handler.
Si le coût > 0 et que l'utilisateur n'a pas explicitement validé, on stoppe tout.
"""
cout_estime = estimer_cout_facture(prompt, modele_id)
# Les VIP ne paient pas de marge, on affiche juste le coût brut pour information
if utilisateur_vip: if utilisateur_vip:
log_event("INFO", "BILLING", f"Utilisateur VIP. Coût brut estimé de la requête : {cout_estime * 0.85} $") log_event("INFO", "BILLING", f"Utilisateur VIP : Pas de prélèvement de marge.")
return True, 0.0 return True, 0.0
if cout_estime > 0.0: # Vérification stricte du solde
# Lève une exception spécifique que l'interface FastHTML devra attraper pour afficher le pop-up if solde_disponible_utilisateur < prix_facture_ht:
raise Exception(f"VALIDATION_PAIEMENT_REQUISE|{cout_estime}") raise Exception(f"SOLDE_INSUFFISANT|{prix_facture_ht}|{solde_disponible_utilisateur}")
return True, cout_estime return True, prix_facture_ht