Compare commits

...
2 Commits
Author SHA1 Message Date
xavier 6f7e3efd6e feat: ajout du système de rotation de logs (7j), correction du modèle générique OpenRouter, création du Wiki et préparation BDD modèles/finances
Build and Push Docker Image / build-and-push (push) Successful in 44s
2026-10-07 14:31:46 +02:00
xavier f794937fd8 fix: intégration du support natif Gemini et affichage des erreurs API détaillées
Build and Push Docker Image / build-and-push (push) Successful in 41s
2026-10-07 14:09:24 +02:00
4 changed files with 114 additions and 40 deletions

No files matched your search

+20
View File
@@ -0,0 +1,20 @@
# 📚 WIKI AETHAS38 - Configuration des Fournisseurs IA
Ce document détaille la procédure pour obtenir les clés API avec les droits complets (Inférence + Accès financier) nécessaires au fonctionnement de l'Orchestrateur AETHAS38.
## 1. OpenRouter (Prioritaire)
OpenRouter agit comme un agrégateur. C'est le fournisseur recommandé car il permet d'accéder à presque tous les modèles (y compris Gemini, Claude, Llama) avec un seul portefeuille prépayé.
* **Obtenir la clé API :** Allez sur [OpenRouter Keys](https://openrouter.ai/keys). Créez une clé standard sans limite de crédit pour permettre à AETHAS38 de requêter les modèles.
* **Accès Financier (Management Key) :** Pour que AETHAS38 lise votre solde en temps réel, vous devez générer une clé spécifique d'administration (Management Key) sur la même page, ou accorder les droits `read_balance` à votre clé API principale selon les dernières mises à jour de leur interface.
## 2. Google Gemini Studio
* **Obtenir la clé API :** Rendez-vous sur [Google AI Studio](https://aistudio.google.com/app/apikey).
* **Levée des quotas :** Par défaut, la clé est gratuite mais soumise à des limites strictes (ex: 15 requêtes/minute) qui bloqueront le travail en parallèle d'AETHAS38. Pour un accès total, liez votre projet AI Studio à un compte de facturation Google Cloud Platform (GCP).
## 3. DeepSeek & Groq
* **DeepSeek :** [DeepSeek Platform](https://platform.deepseek.com/api_keys). Facturation à l'usage (Token). Il faut provisionner le compte par carte bancaire (Top-up).
* **Groq :** [Groq Console](https://console.groq.com/keys). Actuellement en phase gratuite avec quotas de requêtes très élevés, idéal pour les travailleurs de base de l'orchestrateur.
## 4. Cloudflare Workers AI & HuggingFace
* **Cloudflare :** [Cloudflare Dash](https://dash.cloudflare.com/profile/api-tokens). Créez un token personnalisé avec la permission `Workers AI : Read/Write`. L'ID de compte (Account ID) est visible dans la barre latérale droite de l'aperçu de la zone.
* **HuggingFace :** [HF Settings](https://huggingface.co/settings/tokens). Générez un token *Fine-Grained* et cochez les permissions liées à l'`Inference API` pour permettre l'exécution des modèles serverless.
+45 -40
View File
@@ -1,53 +1,58 @@
from openai import OpenAI from openai import OpenAI
from fastapi import HTTPException from fastapi import HTTPException
from .models import SystemSettings from .models import SystemSettings
from .logger import system_logger
def get_ai_response(messages: list, settings: SystemSettings) -> str: def get_ai_response(messages: list, settings: SystemSettings) -> str:
""" """Route la conversation vers le premier fournisseur IA disponible, priorité à Gemini Direct."""
Route la conversation vers le premier fournisseur IA disponible configuré par l'admin.
Prend l'historique des messages et retourne le texte généré.
"""
if not settings: if not settings:
system_logger.error("Configuration système introuvable lors de l'appel IA.")
raise HTTPException(status_code=500, detail="Configuration système introuvable.") raise HTTPException(status_code=500, detail="Configuration système introuvable.")
# Formatage de l'historique pour l'API (OpenAI compatible)
formatted_messages = [{"role": msg.role, "content": msg.content} for msg in messages] formatted_messages = [{"role": msg.role, "content": msg.content} for msg in messages]
# 1. Test OpenRouter (Idéal car donne accès à tout)
if settings.openrouter_api_key:
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=settings.openrouter_api_key,
)
model = "google/gemini-pro" # Fallback par défaut via OpenRouter
# 2. Test DeepSeek
elif settings.deepseek_api_key:
client = OpenAI(
base_url="https://api.deepseek.com/v1",
api_key=settings.deepseek_api_key,
)
model = "deepseek-chat"
# 3. Test Groq
elif settings.groq_api_key:
client = OpenAI(
base_url="https://api.groq.com/openai/v1",
api_key=settings.groq_api_key,
)
model = "llama3-8b-8192"
else:
raise HTTPException(status_code=400, detail="Aucun moteur IA (OpenRouter, DeepSeek, Groq) n'est configuré avec une clé API.")
try: try:
response = client.chat.completions.create( # 1. Priorité absolue : Test Gemini direct (Rapide, sans intermédiaire)
model=model, if settings.gemini_api_key:
messages=formatted_messages, client = OpenAI(
# Identifiant unique de l'application pour OpenRouter base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
extra_headers={"HTTP-Referer": "https://aethas38.duckdns.org", "X-Title": "AETHAS38 Multi-IA"} if settings.openrouter_api_key else {} api_key=settings.gemini_api_key
) )
# Utilisation de l'identifiant standard API pour la version Flash
model = "gemini-1.5-flash"
system_logger.info(f"Appel IA via Google Gemini direct (Modèle: {model})")
response = client.chat.completions.create(model=model, messages=formatted_messages)
# 2. Test OpenRouter (Fallback)
elif settings.openrouter_api_key:
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=settings.openrouter_api_key)
model = "google/gemini-1.5-pro" # Identifiant mis à jour et valide
system_logger.info(f"Appel IA via OpenRouter (Modèle: {model})")
response = client.chat.completions.create(
model=model,
messages=formatted_messages,
extra_headers={"HTTP-Referer": "https://aethas38.duckdns.org", "X-Title": "AETHAS38"}
)
# 3. Test DeepSeek
elif settings.deepseek_api_key:
client = OpenAI(base_url="https://api.deepseek.com/v1", api_key=settings.deepseek_api_key)
model = "deepseek-chat"
system_logger.info(f"Appel IA via DeepSeek (Modèle: {model})")
response = client.chat.completions.create(model=model, messages=formatted_messages)
# 4. Test Groq
elif settings.groq_api_key:
client = OpenAI(base_url="https://api.groq.com/openai/v1", api_key=settings.groq_api_key)
model = "llama3-8b-8192"
system_logger.info(f"Appel IA via Groq (Modèle: {model})")
response = client.chat.completions.create(model=model, messages=formatted_messages)
else:
raise HTTPException(status_code=400, detail="Aucune clé API IA n'est configurée.")
return response.choices[0].message.content return response.choices[0].message.content
except Exception as e: except Exception as e:
print(f"Erreur API IA : {str(e)}") system_logger.error(f"Erreur API IA : {str(e)}")
raise HTTPException(status_code=502, detail="Erreur de communication avec le fournisseur IA.") raise HTTPException(status_code=502, detail=f"Détail du fournisseur : {str(e)}")
+24
View File
@@ -0,0 +1,24 @@
import logging
from logging.handlers import TimedRotatingFileHandler
import os
# Création du dossier logs à la racine du projet
log_dir = os.path.join(os.getcwd(), "logs")
os.makedirs(log_dir, exist_ok=True)
# Configuration du logger
system_logger = logging.getLogger("AETHAS38_Logger")
system_logger.setLevel(logging.INFO)
# Rotation tous les jours à minuit, conservation sur 7 jours
handler = TimedRotatingFileHandler(
filename=os.path.join(log_dir, "aethas38_system.log"),
when="midnight",
interval=1,
backupCount=7,
encoding="utf-8"
)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%d/%m/%Y %H:%M:%S')
handler.setFormatter(formatter)
system_logger.addHandler(handler)
+25
View File
@@ -2,6 +2,8 @@ from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, T
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from datetime import datetime, timezone from datetime import datetime, timezone
from .database import Base from .database import Base
from sqlalchemy import Float, DateTime
from datetime import datetime, timezone
class User(Base): class User(Base):
__tablename__ = "users" __tablename__ = "users"
@@ -62,3 +64,26 @@ class SystemSettings(Base):
cloudflare_account_id = Column(String, nullable=True) cloudflare_account_id = Column(String, nullable=True)
cloudflare_api_token = Column(String, nullable=True) cloudflare_api_token = Column(String, nullable=True)
huggingface_api_key = Column(String, nullable=True) huggingface_api_key = Column(String, nullable=True)
class AIModel(Base):
"""Stocke la liste des modèles extraits depuis les fournisseurs."""
__tablename__ = "ai_models"
id = Column(Integer, primary_key=True, index=True)
provider = Column(String, index=True) # ex: openrouter, groq
model_id = Column(String, unique=True, index=True) # ex: google/gemini-1.5-pro
name = Column(String)
context_length = Column(Integer)
pricing_prompt = Column(Float) # Coût pour 1M tokens
pricing_completion = Column(Float)
last_updated = Column(DateTime, default=lambda: datetime.now(timezone.utc))
class FinancialLog(Base):
"""Suivi financier par fournisseur."""
__tablename__ = "financial_logs"
id = Column(Integer, primary_key=True, index=True)
provider = Column(String, index=True)
balance = Column(Float) # Crédit restant
total_usage = Column(Float) # Consommation totale
checked_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))