Résumez cet article avec :
« Compatible tool calling » n'a pas le même sens pour une fiche modèle, un provider, un catalogue et le modèle lui-même. Nous avons testé 44 endpoints pour mesurer l'écart.
Demandez à quatre personnes si un modèle « supporte le tool calling » et vous pouvez obtenir quatre réponses différentes, toutes techniquement vraies.
La fiche du modèle dit qu'il a été entraîné pour. La documentation du provider dit que l'endpoint accepte un champ tools. Le catalogue d'une gateway affiche un indicateur à true. Et le modèle, le jour où vous mettez en production, appellera votre outil ou pas.
C'est l'écart de compréhension des capacités des LLM. Les développeurs lisent « compatible » comme « fonctionne comme prévu, à chaque fois ». En production, cela veut souvent dire « la requête ne sera pas rejetée ». La différence n'apparaît que lorsqu'une fonctionnalité casse, en général sans erreur.
Pour la mesurer, nous avons envoyé les mêmes cinq requêtes à 44 endpoints : quatre familles de modèles open-weight très utilisées, chacune disponible via 10 à 13 routes de providers, toutes appelées via l'API d'Eden AI le 16 octobre 2026. Eden AI n'héberge ni n'entraîne ces modèles : il donne aux développeurs une seule API vers les providers qui les servent, et c'est précisément ce qui rend possible un test côte à côte comme celui-ci. Cet article présente ce que nous avons trouvé, et comment le tester avant vos utilisateurs.
Quatre sources de vérité qui s'accordent rarement
Pour décider si un modèle peut gérer une fonctionnalité, une équipe s'appuie en général sur l'une de ces quatre sources :
- La fiche du modèle : ce que le labo a entraîné et évalué. Elle décrit des poids, pas un service en production.
- La documentation du provider : ce qu'un service d'inférence dit accepter. Deux providers qui servent les mêmes poids peuvent documenter des paramètres différents.
- Un catalogue ou une gateway : une vue normalisée de plusieurs providers, utile pour comparer, mais seulement aussi juste que les déclarations derrière.
- Le comportement observé : ce qui revient réellement d'une vraie requête.
Les trois premières sont des déclarations. Seule la quatrième est une preuve. Elles s'écartent pour des raisons banales : les providers servent des versions ou des points de contrôle différents d'un même modèle, avec une quantization différente, sur des moteurs d'inférence différents, et activent ou non des fonctions comme le décodage contraint ou l'entrée multimodale indépendamment du modèle lui-même.
Ce que nous avons testé
Nous avons choisi quatre familles servies par de nombreux providers : GLM-5.3, DeepSeek V4.1 Flash, gpt-oss-120b et Kimi K3. Pour chaque route, nous avons envoyé cinq requêtes :
| Test | Ce que nous avons envoyé | Ce qui compte comme « fonctionne » |
|---|---|---|
| Effort de raisonnement | reasoning_effort: "medium" sur un calcul simple | La requête est acceptée et la réponse est juste |
| Schéma JSON strict | response_format avec un schéma strict de trois champs | Le contenu se lit comme du JSON avec exactement ces champs |
| Appel d'outil forcé | Un outil, tool_choice: "required", et une demande qui n'en a pas besoin | La réponse contient un appel d'outil avec des arguments JSON valides |
| Entrée image | Une petite image entièrement rouge et « quelle est la couleur dominante ? » | Le modèle répond « rouge » |
| Usage en streaming | stream: true avec include_usage | Le flux se termine par le nombre de tokens |
Chaque résultat est classé de trois façons. Ça a fonctionné. Ça a échoué avec une erreur explicite. Ou ça a échoué en silence : une réponse HTTP 200 d'apparence normale qui ne faisait pas ce qui était demandé. Les routes limitées en débit ou hors délai pendant le test sont comptées comme non mesurées, pas comme des échecs. Pour nous assurer que la gateway elle-même n'était pas en cause, nous avons reproduit deux de ces cas directement sur les API des providers, sans passer par Eden AI, avec les mêmes résultats.
Les résultats
| Test | Routes mesurées | Fonctionne | Erreur explicite | Échec silencieux |
|---|---|---|---|---|
| Effort de raisonnement « medium » | 42 | 41 | 1 | 0 |
| Schéma JSON strict | 42 | 36 | 3 | 3 |
| Appel d'outil forcé | 40 | 31 | 1 | 8 |
| Image, modèles texte seul | 23 | n/a | 14 | 9 |
| Image, modèles vision | 18 | 18 | 0 | 0 |
| Usage en streaming | 42 | 42 | 0 | 0 |
Le constat n'est pas que les providers sont peu fiables. La plupart des routes ont fait ce qui était demandé, et le streaming a fonctionné partout. L'écart se concentre sur quelques fonctionnalités, et sur un comportement à risque : des requêtes qui échouent sans le dire.
Cinq endroits où « compatible » et « fonctionne » divergent
1. Un appel d'outil forcé qui n'arrive jamais
C'est l'écart le plus net. Nous avons envoyé un outil, mis tool_choice à "required" et demandé une blague. Un modèle qui respecte le paramètre doit appeler l'outil quand même.
Sur 31 routes sur 40, c'est ce qui s'est passé. Sur 8, la réponse était un HTTP 200 normal avec une blague et aucun appel d'outil. Un seul provider a renvoyé une erreur explicite, indiquant que l'appel d'outil était obligatoire mais que le modèle n'en avait fait aucun.
Les mêmes poids se sont comportés des deux façons. Pour gpt-oss-120b, sept routes ont forcé l'appel, quatre ont ignoré le paramètre en silence et une a rejeté la requête. Pour un agent qui compte sur required pour garantir une action structurée, le cas silencieux est le pire : le code qui lit tool_calls ne reçoit rien et doit deviner ce que cela signifie.
2. Une sortie structurée acceptée, puis ignorée
Le schéma JSON strict a fonctionné sur 36 routes sur 42. Les six autres se répartissent à égalité. Trois providers ont refusé le paramètre avec un message clair, l'un d'eux suggérant même le mode json_object. Trois l'ont accepté et ont renvoyé autre chose : un simple [1], un contenu vide, ou le raisonnement du modèle rédigé en texte libre.
Un refus explicite se gère facilement : on passe à un autre mode ou à une autre route. Une requête acceptée avec une sortie non conforme, non, car rien dans la réponse n'indique que le schéma n'a pas été appliqué.
3. Des images envoyées à un modèle texte seul
GLM-5.3 et gpt-oss-120b ne lisent que du texte, nous attendions donc un échec sur chaque requête avec image. La question était la manière. Quatorze routes ont rejeté la requête avec un message utile, du type « This model does not support image input ». Neuf l'ont acceptée, et le modèle a simplement répondu qu'il ne voyait aucune image.
Pour un chatbot, cette réponse est simplement déroutante. Pour une chaîne de traitement qui envoie des scans à un modèle et enregistre ce qui revient, c'est une perte de données silencieuse.
4. Un paramètre qu'un seul provider ne comprend pas
reasoning_effort: "medium" a été accepté sur 41 routes sur 42. Sur un provider qui sert DeepSeek V4.1 Flash, il a renvoyé un HTTP 400 avec le message Invalid request et "param": null. La même requête avec "none", "low" ou "high" fonctionne.
Rien dans cette erreur ne désigne le paramètre ni ne liste les valeurs acceptées. Un développeur qui la voit dans ses logs de production doit découper la requête à la main pour trouver la cause.
5. Le modèle demandé n'est pas toujours celui qui répond
Deux comportements observés ces dernières semaines complètent le tableau, tous deux reproductibles via des API publiques :
- Des identifiants retirés qui répondent encore : quand un provider retire un modèle, les anciens identifiants peuvent continuer à renvoyer un HTTP 200 pendant un temps, servis par le modèle de remplacement, ce qui évite de casser les intégrations du jour au lendemain. Le champ
modelde la réponse renvoie toujours l'identifiant envoyé, donc la réponse seule ne dit pas quel modèle a répondu : ce sont les notes de dépréciation du provider qui le disent. - Demander au modèle n'est pas une vérification : quand nous avons demandé à plusieurs modèles qui ils étaient, un modèle DeepSeek a répondu « GPT-4 » et un autre « Claude ». L'auto-identification reflète les données d'entraînement, pas l'infrastructure qui sert la requête.
Pourquoi l'écart existe, et pourquoi il ne se refermera pas tout seul
Rien de tout cela ne suppose de la négligence. Une capacité n'est pas une propriété du modèle seul, c'est celle d'un modèle, d'une version, d'une configuration de service et de la façon dont un provider traite les requêtes, à un moment donné. Chacune de ces couches évolue à son propre rythme.
Les catalogues aident à comparer, mais c'est souvent là que le malentendu commence. Les utilisateurs lisent un indicateur de capacité comme une promesse valable pour chaque requête, alors qu'il dit seulement ce qu'une route déclare accepter. Un indicateur ne peut pas dire comment un provider gère un cas limite, ni ce qui se passe quand un paramètre est ignoré en silence. La seule référence fiable est un test que vous lancez vous-même, sur les routes que vous utilisez vraiment.
Comment réduire l'écart avant vos utilisateurs
- Écrivez des tests de capacités, pas seulement des tests de prompts : pour chaque fonctionnalité dont dépend votre produit, gardez une petite requête avec un résultat vérifiable automatiquement, comme les cinq ci-dessus. Lancez-les en CI et à intervalle régulier, pas seulement au choix du modèle. Notre guide pour tester plusieurs LLM sans réécrire votre code montre comment le mettre en place.
- Vérifiez la structure, pas le code HTTP : un 200 n'est pas un succès. Vérifiez que
tool_callsexiste quand vous l'avez exigé, que le contenu respecte votre schéma, et que l'usage est présent si vous facturez dessus. - Traitez les échecs silencieux comme des erreurs dans votre propre code : si
tool_choicevalaitrequiredet qu'aucun appel d'outil n'est revenu, levez une erreur. Si une image a été envoyée et que la réponse dit qu'il n'y en a pas, levez une erreur. - Utilisez les identifiants datés quand ils existent : les alias sont pratiques, mais ils peuvent passer à une nouvelle version ou à un modèle de remplacement sans prévenir.
- Ne basculez que vers des routes équivalentes : un fallback vers une route qui n'a pas la capacité dont vous avez besoin transforme une panne en échec silencieux. Choisissez vos fallbacks sur les fonctions utilisées, pas seulement sur le nom du modèle.
- Journalisez ce que vous envoyez et ce que vous recevez : quand un comportement change, la requête, la route et la réponse brute permettent de le voir en quelques minutes plutôt qu'en quelques jours.
Voici une version minimale du test d'appel d'outil forcé, lancée sur toutes les routes qui servent une même famille de modèles :
import json, requests
from openai import OpenAI
client = OpenAI(base_url="https://api.edenai.run/v3", api_key="YOUR_EDENAI_API_KEY")
# Toutes les routes qui servent la même famille de modèles, depuis le catalogue public
models = requests.get("https://api.edenai.run/v3/models").json()["data"]
routes = [m["id"] for m in models if m["id"].lower().endswith("glm-5.3")]
tools = [{"type": "function", "function": {
"name": "get_weather", "description": "Get the weather for a city",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}]
for route in routes:
try:
r = client.chat.completions.create(model=route, tools=tools, tool_choice="required",
messages=[{"role": "user", "content": "Tell me a joke."}])
calls = r.choices[0].message.tool_calls or []
print(route, "works" if calls else "SILENT FAILURE: no tool call")
except Exception as e:
print(route, "explicit error:", str(e)[:80])Comment Eden AI vous aide à mesurer l'écart
Eden AI ne fait pas disparaître l'écart, et nous ne le prétendons pas. Il n'héberge pas non plus les modèles : il vous relie, par une seule API, aux providers qui les servent. Ce qu'il change, c'est le coût de la mesure, et de ce que vous faites ensuite de ses résultats.
- Une seule API pour toutes les routes : avec une clé et un format de requête uniques, tester la même fonctionnalité sur dix providers revient à boucler sur des noms de modèles, comme dans l'exemple ci-dessus, plutôt qu'à construire et maintenir dix intégrations.
- Les capacités déclarées au même endroit : l'endpoint public
/v3/modelsliste chaque route avec ses capacités déclarées, ses régions et sa politique de conservation des données, ce qui permet de construire votre matrice de tests. Les mêmes informations sont filtrables dans le catalogue de modèles Eden AI. - Fixer une route, ou laisser Eden AI choisir : appelez
provider/modelpour fixer une route qui a passé vos tests, ou demandez un modèle par son seul nom et laissez Eden AI choisir le provider à chaque appel quand la différence ne compte pas pour cette fonctionnalité. - Changer sans migration : quand une route régresse, déplacer le trafic vers une route qui a passé les tests se fait en changeant le nom du modèle, et les fallbacks maintiennent le service pendant ce temps.
Les déclarations sont un point de départ. Les tests sont ce sur quoi vous mettez en production. Pour les lancer sur vos propres routes, créez votre clé API Eden AI et partez de l'exemple ci-dessus.
Questions fréquentes
Que veut vraiment dire « compatible tool calling » ?
En général, que l'endpoint accepte un champ tools sans rejeter la requête. Cela ne garantit pas que le modèle appellera un outil quand on le lui demande, qu'il respectera tool_choice, ni que les arguments seront du JSON valide. Ces comportements dépendent du modèle, de la version et de l'infrastructure du provider.
Pourquoi le même modèle se comporte-t-il différemment selon les providers ?
Les providers peuvent servir des versions ou des quantizations différentes des mêmes poids, utiliser des moteurs d'inférence différents, et implémenter différemment le décodage contraint, la lecture des appels d'outils ou l'entrée multimodale. Deux endpoints qui portent le même nom de modèle sont deux services différents.
Qu'est-ce qu'un échec silencieux dans une API LLM ?
Une réponse HTTP 200 d'apparence normale qui ne fait pas ce que la requête demandait : un appel d'outil obligatoire absent, un schéma JSON non appliqué, ou une image perdue avant d'atteindre le modèle. Ces échecs sont plus difficiles à repérer que des erreurs, car le code HTTP ne les signale pas.
À quelle fréquence lancer des tests de capacités ?
Au minimum à chaque changement de modèle, de provider ou d'alias, et à intervalle régulier en production. Le comportement d'un provider peut changer sans aucune note de version de votre côté, donc un passage quotidien ou hebdomadaire détecte les régressions avant vos utilisateurs.
Peut-on se fier à un catalogue de modèles pour les capacités ?
Pour présélectionner des routes et construire votre matrice de tests, oui, mais pas comme une garantie. Un indicateur de capacité dit ce qu'une route déclare accepter, pas comment chaque requête se comportera. Confirmez chaque fonctionnalité dont vous dépendez par un test sur la route exacte que vous utilisez.
Eden AI héberge-t-il les modèles auxquels il donne accès ?
Non. Eden AI est une couche d'API : il vous relie aux providers qui hébergent et servent chaque modèle, et vous permet de les appeler avec la même clé et le même format de requête. Le provider qui sert une requête est indiqué dans le nom de modèle que vous envoyez, ou choisi par Eden AI quand vous demandez un modèle par son seul nom.



![[AUTO-DRAFT] OpenAI Quietly Cut Codex Context Window by 27 Percent: Why Provider-Specific Limits Are Not a Contract](https://cdn.prod.website-files.com/61e7d259b7746e3f63f0b6be/6a74828e12326e8199f45718_6a74828c58976902eb769454_openai-quietly-cut-codex-context-window-by-27-percent-why-provider-specific--openai.png)