fix: use mammouth public api and improve model mapping
This commit is contained in:
102
update_models.py
102
update_models.py
@@ -7,38 +7,33 @@ from dotenv import load_dotenv
|
|||||||
# Charger .env.global depuis le répertoire parent
|
# Charger .env.global depuis le répertoire parent
|
||||||
load_dotenv("../.env.global")
|
load_dotenv("../.env.global")
|
||||||
|
|
||||||
MAMMOUTH_APIKEY = os.getenv("MAMMOUTH_APIKEY")
|
|
||||||
AIANALASYS_APIKEY = os.getenv("AIANALASYS_APIKEY")
|
AIANALASYS_APIKEY = os.getenv("AIANALASYS_APIKEY")
|
||||||
|
|
||||||
def get_mammouth_models():
|
def get_mammouth_models():
|
||||||
# Mammouth utilise l'API OpenRouter (revendeur)
|
# Utilisation de l'endpoint public LiteLLM de Mammouth
|
||||||
url = "https://openrouter.ai/api/v1/models"
|
url = "https://mammouth.ai/public/models"
|
||||||
headers = {"Authorization": f"Bearer {MAMMOUTH_APIKEY}"}
|
|
||||||
try:
|
try:
|
||||||
response = requests.get(url, headers=headers)
|
response = requests.get(url)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return response.json()['data']
|
# Le format retourné est {'data': [ {id, model_info: {input_cost_per_token, ...}} ]}
|
||||||
|
return response.json().get('data', [])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error fetching Mammouth models: {e}")
|
print(f"Error fetching Mammouth public models: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_aa_data():
|
def get_aa_data():
|
||||||
# URL correcte d'après la doc (version v2)
|
|
||||||
url = "https://artificialanalysis.ai/api/v2/data/llms/models"
|
url = "https://artificialanalysis.ai/api/v2/data/llms/models"
|
||||||
headers = {"x-api-key": AIANALASYS_APIKEY}
|
headers = {"x-api-key": AIANALASYS_APIKEY}
|
||||||
try:
|
try:
|
||||||
response = requests.get(url, headers=headers)
|
response = requests.get(url, headers=headers)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
# Le diagnostic a montré que les données sont dans 'data'
|
|
||||||
return response.json().get('data', [])
|
return response.json().get('data', [])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error fetching Artificial Analysis data: {e}")
|
print(f"Error fetching Artificial Analysis data: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def generate_markdown(models_data):
|
def generate_markdown(models_data):
|
||||||
# Trier par catégorie (genre)
|
|
||||||
categories = {}
|
categories = {}
|
||||||
|
|
||||||
for m in models_data:
|
for m in models_data:
|
||||||
cat = m.get('category', 'General')
|
cat = m.get('category', 'General')
|
||||||
if cat not in categories:
|
if cat not in categories:
|
||||||
@@ -46,10 +41,9 @@ def generate_markdown(models_data):
|
|||||||
categories[cat].append(m)
|
categories[cat].append(m)
|
||||||
|
|
||||||
md = "# Table des Modèles Mammouth.ai\n\n"
|
md = "# Table des Modèles Mammouth.ai\n\n"
|
||||||
md += "*Mise à jour automatique via Artificial Analysis & Mammouth API*\n\n"
|
md += "*Mise à jour automatique via Artificial Analysis & Mammouth Public API*\n\n"
|
||||||
md += "Dernière mise à jour : " + time.strftime("%Y-%m-%d %H:%M:%S") + "\n\n"
|
md += "Dernière mise à jour : " + time.strftime("%Y-%m-%d %H:%M:%S") + "\n\n"
|
||||||
|
|
||||||
# Liste des catégories dans un ordre spécifique
|
|
||||||
order = ['Coding', 'Agents', 'General']
|
order = ['Coding', 'Agents', 'General']
|
||||||
sorted_cats = sorted(categories.keys(), key=lambda x: order.index(x) if x in order else 99)
|
sorted_cats = sorted(categories.keys(), key=lambda x: order.index(x) if x in order else 99)
|
||||||
|
|
||||||
@@ -58,78 +52,80 @@ def generate_markdown(models_data):
|
|||||||
md += f"## {cat}\n\n"
|
md += f"## {cat}\n\n"
|
||||||
md += "| Modèle | Prix (In / Out / 1M) | Performance (AA Index) | Vitesse (TPS) |\n"
|
md += "| Modèle | Prix (In / Out / 1M) | Performance (AA Index) | Vitesse (TPS) |\n"
|
||||||
md += "| :--- | :--- | :--- | :--- |\n"
|
md += "| :--- | :--- | :--- | :--- |\n"
|
||||||
# Trier par performance (AA index)
|
# On trie d'abord par score décroissant, puis par prix croissant
|
||||||
models.sort(key=lambda x: x.get('score') or 0, reverse=True)
|
models.sort(key=lambda x: (x.get('score') or 0, -(x.get('price_in') or 0)), reverse=True)
|
||||||
for m in models:
|
for m in models:
|
||||||
p_in = f"${m['price_in']:.2f}" if m['price_in'] is not None else "N/A"
|
p_in = f"${m['price_in']:.2f}"
|
||||||
p_out = f"${m['price_out']:.2f}" if m['price_out'] is not None else "N/A"
|
p_out = f"${m['price_out']:.2f}"
|
||||||
score = f"**{m['score']:.1f}**" if m['score'] else "N/A"
|
score = f"**{m['score']:.1f}**" if m['score'] else "N/A"
|
||||||
speed = f"{m['speed']:.1f}" if m['speed'] else "N/A"
|
speed = f"{m['speed']:.1f}" if m['speed'] else "N/A"
|
||||||
md += f"| {m['name']} | {p_in} / {p_out} | {score} | {speed} |\n"
|
md += f"| {m['name']} | {p_in} / {p_out} | {score} | {speed} |\n"
|
||||||
md += "\n"
|
md += "\n"
|
||||||
|
|
||||||
return md
|
return md
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("Fetching Mammouth models...")
|
print("Fetching Mammouth public models...")
|
||||||
mammouth_models = get_mammouth_models()
|
mammouth_models = get_mammouth_models()
|
||||||
|
if not mammouth_models:
|
||||||
|
print("No models found from Mammouth.")
|
||||||
|
return
|
||||||
|
|
||||||
print("Fetching Artificial Analysis data...")
|
print("Fetching Artificial Analysis data...")
|
||||||
aa_data = get_aa_data()
|
aa_raw = get_aa_data()
|
||||||
|
|
||||||
# Créer un dictionnaire de mapping pour AA (clé: nom du modèle en minuscule)
|
# Construction du mapping AA (index par nom et par ID technique)
|
||||||
aa_map = {}
|
aa_map = {}
|
||||||
for aa_m in aa_data:
|
for aa_m in aa_raw:
|
||||||
name = aa_m.get('model_name', '').lower()
|
name = aa_m.get('model_name', '').lower()
|
||||||
aa_map[name] = aa_m
|
model_id = aa_m.get('model_id', '').lower()
|
||||||
|
if name: aa_map[name] = aa_m
|
||||||
|
if model_id: aa_map[model_id] = aa_m
|
||||||
|
|
||||||
enriched_models = []
|
enriched_models = []
|
||||||
|
|
||||||
for m in mammouth_models:
|
for m in mammouth_models:
|
||||||
m_id = m['id']
|
m_id = m.get('id', '')
|
||||||
m_name = m['name'].lower()
|
info = m.get('model_info', {})
|
||||||
short_name = m_id.split('/')[-1].lower()
|
|
||||||
|
|
||||||
# Mapping logique plus complet
|
# On ignore les modèles sans ID
|
||||||
aa_info = aa_map.get(m_name) or aa_map.get(short_name)
|
if not m_id: continue
|
||||||
|
|
||||||
|
# Normalisation du nom pour le mapping
|
||||||
|
m_id_low = m_id.lower()
|
||||||
|
short_name = m_id_low.split('/')[-1]
|
||||||
|
|
||||||
|
# Recherche de correspondance dans AA (Précis puis Approché)
|
||||||
|
aa_info = aa_map.get(m_id_low) or aa_map.get(short_name)
|
||||||
|
|
||||||
# Si pas de match exact, on cherche par sous-chaîne ou flou
|
|
||||||
if not aa_info:
|
if not aa_info:
|
||||||
|
# Recherche par sous-chaîne pour les modèles comme "mistral-large-2407"
|
||||||
for key in aa_map:
|
for key in aa_map:
|
||||||
if key in m_name or m_name in key or key in short_name or short_name in key:
|
if key in m_id_low or m_id_low in key:
|
||||||
aa_info = aa_map[key]
|
aa_info = aa_map[key]
|
||||||
break
|
break
|
||||||
|
|
||||||
# Extraction des prix Mammouth (prix pour 1 token chez OpenRouter)
|
# Extraction des prix (LiteLLM: prix par 1 token)
|
||||||
pricing = m.get('pricing', {})
|
|
||||||
try:
|
try:
|
||||||
price_in = float(pricing.get('prompt', 0)) * 1000000
|
price_in = float(info.get('input_cost_per_token', 0)) * 1000000
|
||||||
price_out = float(pricing.get('completion', 0)) * 1000000
|
price_out = float(info.get('output_cost_per_token', 0)) * 1000000
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
price_in = 0
|
price_in, price_out = 0, 0
|
||||||
price_out = 0
|
|
||||||
|
|
||||||
score = None
|
score = None
|
||||||
speed = None
|
speed = None
|
||||||
# On essaie d'extraire la catégorie de AA, sinon on devine
|
|
||||||
category = "General"
|
|
||||||
|
|
||||||
if aa_info:
|
if aa_info:
|
||||||
evals = aa_info.get('evaluations', {})
|
evals = aa_info.get('evaluations', {})
|
||||||
# On cherche l'intelligence index
|
|
||||||
score = evals.get('artificial_analysis_intelligence_index')
|
score = evals.get('artificial_analysis_intelligence_index')
|
||||||
speed = aa_info.get('median_output_tokens_per_second')
|
speed = aa_info.get('median_output_tokens_per_second')
|
||||||
|
|
||||||
# Détermination de la catégorie (Genre)
|
# Catégorisation simplifiée
|
||||||
if any(x in m_name or x in short_name for x in ['coding', 'code', 'starcoder', 'stable-code', 'deepseek-coder']):
|
category = "General"
|
||||||
|
if any(x in m_id_low for x in ['coding', 'code', 'starcoder', 'coder']):
|
||||||
category = "Coding"
|
category = "Coding"
|
||||||
elif any(x in m_name or x in short_name for x in ['agent', 'hermes', 'tool']):
|
elif any(x in m_id_low for x in ['agent', 'hermes', 'tool', 'function']):
|
||||||
category = "Agents"
|
category = "Agents"
|
||||||
else:
|
|
||||||
category = "General"
|
|
||||||
|
|
||||||
enriched_models.append({
|
enriched_models.append({
|
||||||
'name': m['name'],
|
'name': m_id,
|
||||||
'price_in': price_in,
|
'price_in': price_in,
|
||||||
'price_out': price_out,
|
'price_out': price_out,
|
||||||
'score': score,
|
'score': score,
|
||||||
@@ -137,12 +133,14 @@ def main():
|
|||||||
'category': category
|
'category': category
|
||||||
})
|
})
|
||||||
|
|
||||||
# On ne garde que les modèles qui ont un score de performance OU un prix raisonnable
|
# Filtrer les modèles : prix > 0 (ceux qui sont configurés)
|
||||||
# (Certains modèles sont gratuits ou ont des prix nuls)
|
final_list = [m for m in enriched_models if m['price_in'] > 0 or m['price_out'] > 0]
|
||||||
final_list = [m for m in enriched_models if m['price_in'] > 0 or m['score'] is not None]
|
|
||||||
|
|
||||||
|
if not final_list:
|
||||||
|
print("No valid models found after filtering.")
|
||||||
|
return
|
||||||
|
|
||||||
markdown = generate_markdown(final_list)
|
markdown = generate_markdown(final_list)
|
||||||
|
|
||||||
with open("README.md", "w", encoding="utf-8") as f:
|
with open("README.md", "w", encoding="utf-8") as f:
|
||||||
f.write(markdown)
|
f.write(markdown)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user