Sélection du modèle de code Claude : paramètres par défaut, changement de modèle et coût

Sélection du modèle de code Claude : paramètres par défaut, changement de modèle et coût

Pour le choix du modèle de code Claude, utilisez Haiku pour les modifications mineures et vérifiables, Sonnet pour la mise en œuvre courante, et Opus pour les tâches présentant davantage d'ambiguïté ou nécessitant davantage de raisonnement. Anthropic recommande actuellement Opus 5.5 comme point de départ général ; ce sont vos besoins, la charge de travail liée aux révisions et votre budget qui détermineront si un modèle plus petit est plus adapté.

Pour changer de modèle, ouvrez /modèle. Presse Entrer pour enregistrer une valeur par défaut, ou s pour modifier uniquement la session en cours. Pour un lancement unique, utilisez claude --modèle opus. Ces choix ont une persistance différente ; il est donc tout aussi important de déterminer la durée de la commutation que de choisir le modèle.

Le travail de programmation implique également des recherches, des explications, de la documentation et des ressources créatives. GlobalGPT rassemble les meilleurs modèles d'IA et de nombreux outils d'IA au sein d'un tableau de bord abordable, tandis que son interface en ligne de commande (CLI) relie ces outils au terminal et aux flux de travail de développement. Créez votre flux de travail avec GlobalGPT pour regrouper ces tâches connexes tout au long du processus, de l'idée au code fonctionnel.

Quel modèle faut-il utiliser dans le code Claude ?

Commençons par définir la nature du travail. Une fonction simple dont les résultats attendus sont clairs nécessite une évaluation différente de celle d'un bug sporadique touchant plusieurs services. La question pertinente est de savoir si le modèle permet d'aboutir à un changement acceptable après un nombre raisonnable de révisions et d'itérations.

Modèle actuelÀ quoi sert-il ?Un exercice pratique pour commencer
Claude Haïku 4.5Un travail rapide et efficace dans le cadre d'un contrat restreintUne petite refactorisation, une transformation répétitive ou un cas de test simple
Claude Sonnet 5Un juste équilibre entre intelligence et rapidité pour le codage au quotidienUne fonctionnalité reposant sur des schémas bien établis, une correction de bug de routine ou une explication du code
Claude Opus 5,5Raisonnement complexe et travail de codage agentique plus longUne défaillance ambiguë, un compromis architectural ou un changement impliquant des contraintes interdépendantes
Claude Fable 5.1Les enquêtes les plus difficiles et les plus longuesUne escalade lorsque des tâches difficiles l'emportent encore sur un niveau d'effort Opus plus intense
Familles de modèles actuelles et adéquation aux tâches, d'après la documentation Anthropic ; les suggestions de tâches constituent des points de départ concrets.

Il vaut la peine d'envisager Haiku lorsque les erreurs sont faciles à détecter et que les modifications sont mineures. Sonnet est un choix judicieux lorsque la mise en œuvre au quotidien nécessite davantage de contexte et de discernement. Opus mérite d'être pris en considération lorsque la compréhension du problème constitue une part importante de la tâche. Fable mérite une décision à part pour les travaux exigeants ; il ne faisait pas partie de notre test de codage portant sur ces trois modèles.

Il s'agit là de points de départ, et non de garanties automatiques de qualité. Une solution moins coûteuse peut s'avérer onéreuse si elle nécessite plusieurs révisions ou une relecture approfondie. À l'inverse, un modèle plus volumineux peut n'apporter que peu de valeur ajoutée à une simple transformation qui satisfait déjà à vos critères de contrôle. Pour une utilisation plus large, au-delà du code Claude, Comparer les gammes de modèles Claude.

Comparaison des modèles actuelle de Anthropic pour Fable 5.1, Opus 5.5, Sonnet 5 et Haiku 4.5.
Gamme de modèles Anthropic, consultée le 28 septembre 2026. La disponibilité du code Claude dépend de votre opérateur et de votre compte.

Le présentation officielle de la gamme répertorie des fenêtres de contexte de 1 million de tokens pour Fable 5.1, Opus 5.5 et Sonnet 5, et de 200 000 pour Haiku 4.5. Il s'agit de spécifications d'API. Votre fournisseur de code Claude, votre compte et votre configuration déterminent les options de contexte disponibles au cours d’une session. Une grande fenêtre est utile pour le contexte nécessaire ; la remplir avec des éléments sans rapport avec le sujet génère tout de même une charge de travail supplémentaire pour le modèle.

Comment changer de modèle au cours d'une même session

Dans le cadre d'une session interactive, lancez /modèle pour ouvrir la fenêtre de sélection des modèles. Sélectionnez la ligne correspondant au modèle, puis appuyez sur s s'il s'agit d'une modification ponctuelle. Appuyez sur Entrer enregistre la sélection comme valeur par défaut pour votre compte utilisateur. En tapant /sonnet type Cela permet également de l'enregistrer directement dans les versions actuelles.

Choisissez la commande en fonction de la durée souhaitée pour la modification

Comportement du code Claude documenté, vérifié le 28 septembre 2026
Votre objectifQue faireLes modifications apportées aux paramètres par défaut ont-elles été enregistrées ?
Basculer uniquement cette session interactiveOuvrir /modèle, sélectionnez un modèle, puis appuyez sur s.Non.
Enregistrer un modèle favoriOuvrir /modèle et appuyez sur Entrer, ou tapez /sonnet type.Oui, lorsque les paramètres utilisateur sont accessibles en écriture.
Lancer une session unique sur OpusLancement claude --modèle opus.Non.
Revenir à la valeur par défaut d'exécution du compteUtilisation /modèle par défaut.Annule la modification du modèle ; la valeur par défaut retenue peut dépendre des paramètres du compte et de l'organisation.
Vérifier le modèle actifCourir /statut.Non.

Les paramètres de projet ou gérés, les modifications d'environnement et les sessions reprises peuvent avoir une incidence sur le prochain lancement. Vérifiez le modèle actif lorsque la reproductibilité est essentielle.

Source : Configuration du modèle de code Claude.

Pour une nouvelle session de terminal, sélectionnez le modèle au démarrage. Ces commandes s’appliquent également lorsque Claude Code s’exécute dans le terminal intégré de VS Code. Les exemples sélectionnent explicitement une version :

claude --model claude-opus-5-5
claude --model claude-sonnet-5

Exécutez la commande correspondant à la session souhaitée ; les exemples proposés constituent des alternatives. Vous pouvez ouvrir différents terminaux avec différentes --modèle valeurs sans avoir à modifier à plusieurs reprises une valeur par défaut enregistrée et partagée. Utilisez /statut pour vérifier le modèle actif.

Des noms de famille tels que opus et sonnet peuvent passer à des versions plus récentes. Leur résolution dépend également du fournisseur : l'API Anthropic les associe actuellement à Opus 5.5 et Sonnet 5, tandis que les correspondances proposées par les fournisseurs de services cloud peuvent varier. Utilisez un identifiant de modèle complet lorsque vous avez besoin d’une exécution reproductible, et vérifiez le nom spécifique au fournisseur si votre environnement l’exige.

Opus 5.5 nécessite la version 2.1.280 ou une version ultérieure du Code Claude ; Sonnet 5 nécessite la version 2.1.197 ou une version ultérieure. Le documentation relative à la configuration du modèle Constitue la référence en matière d'alias actuels, d'exigences de version et de mappages de fournisseurs. Les commandes présentées ici sont des exemples tirés de la documentation ; notre section pratique évalue le code généré séparément.

Documentation relative au code Claude expliquant le choix du modèle et l'utilisation des touches « Entrée » et « s ».
La touche Entrée enregistre le modèle comme valeur par défaut ; la touche s ne modifie que la session en cours.

Comment définir un modèle par défaut

Pour un paramètre par défaut personnel, sélectionnez un modèle dans /modèle et appuyez sur Entrer, ou saisissez une sélection telle que /sonnet type. Le code Claude écrit le modèle dans vos paramètres utilisateur lorsque ce fichier est accessible en écriture. Ouvrez une nouvelle session et vérifiez son modèle actif pour confirmer le résultat.

Une préférence enregistrée ne remplace pas tous les autres paramètres. Un indicateur de démarrage, MODÈLE_ANTHROPIE Dans le shell, les paramètres du projet ou les paramètres gérés, ou encore si les paramètres par défaut d'une organisation sont configurés pour remplacer les choix de l'utilisateur, un autre élément peut être sélectionné au démarrage. Une session reprise peut également conserver le modèle issu de son historique enregistré. Vérifiez les informations de démarrage et /statut avant de considérer que le choix était perdu.

/modèle par défaut efface votre redéfinition de modèle et rétablit les valeurs par défaut d'exécution de votre compte et de votre configuration. Le mot par défaut Il s'agit d'une valeur de réinitialisation, et non du nom d'un modèle Claude spécifique. Elle ne doit pas être utilisée comme identifiant de modèle reproductible.

Pour une configuration avancée, MODÈLE_ANTHROPIC_PAR_DÉFAUT fournit une option de secours par défaut à partir de la version 2.1.236. Elle apparaît sous les sélections explicites relatives au lancement, à l'environnement, aux paramètres et à l'organisation, et ignore les valeurs telles que haïku et opusplan. En règle générale, le sélecteur est plus facile à vérifier. Voir Paramètres du code Claude lorsqu'il s'agit d'une configuration partagée.

Ce que fait réellement Opusplan

opusplan utilise Opus en mode planification et Sonnet pour l'exécution. L'intérêt est simple : utiliser un modèle de raisonnement plus puissant pour élaborer l'approche, puis un modèle de codage courant pour mettre ce plan en œuvre.

Opusplan suit ce mode de fonctionnement

Mode planificationOpusAppliquez cette méthode.
ExécutionSonnetMettez le plan à exécution.

Comportement de routage documenté. Les versions associées à ces noms de famille dépendent de votre fournisseur d'accès et de votre configuration.

Source : Configuration du modèle de code Claude

Il s'agit d'une règle qui dépend du mode. Elle n'analyse pas chaque invite et ne choisit pas automatiquement la famille la moins chère parmi celles qui conviennent. Les mappages des fournisseurs et les restrictions d'accès s'appliquent toujours aux modèles sous-jacents. De plus, ce nom ne garantit pas de économies : le coût total dépend de l'ampleur de la planification, de la mise en œuvre, de la révision et du traitement du contexte que nécessite la tâche.

Envisagez plutôt une approche où la conception bénéficie d'une analyse plus approfondie et où la mise en œuvre suit un plan clair. Pour une petite modification, cette planification supplémentaire n'est peut-être pas nécessaire.

Quelles sont les deux tâches de programmation qui ont mis en évidence

Afin d'illustrer concrètement le processus de sélection de modèles, nous avons testé une refonte partielle d'Unicode et une correction du sélecteur d'itinéraires, dont les exigences étaient interdépendantes. La distinction entre une fonction correcte et un résultat complet conforme aux exigences s'est avérée plus importante qu'un simple score.

Comment nous avons testé les résultats du codage

Le 28 septembre 2026, nous avons soumis les modèles Claude Haiku 4.5, Claude Sonnet 5 et Claude Opus 5.5 aux deux mêmes tâches JavaScript, jusqu’au modèle GlobalGPT. Chaque modèle a renvoyé du code source ; nous avons inspecté ce code et effectué des vérifications locales fixes. Les instructions de changement de modèle de Claude figurant ailleurs dans ce guide proviennent de sa documentation officielle.

Chaque requête était soumise à une limite de sortie de 4 096 tokens, sans outils ni historique de conversation, et avec les paramètres par défaut du modèle. Nous avons conservé la première réponse évaluable, y compris les réponses incomplètes. Une erreur de service sur la tâche de routage de Sonnet a donné lieu à une seule nouvelle tentative autorisée : sept requêtes ont généré six résultats.

Ces deux petites tâches évaluent le code renvoyé et l'exhaustivité de la livraison. Elles ne mesurent pas l'exécution par les agents de Claude Code, l'exploration du référentiel ni une session de développement dans son intégralité. Les coûts réels en dollars n'ont pas été communiqués. Une seule observation par tâche ne permet pas d'établir un classement général en termes de rapidité ou de fiabilité.

Afficher les identifiants exacts des modèles et les conditions d'évaluation

Identifiants demandés : claude-haiku-4-5-20251001, claude-sonnet-5, et claude-opus-5-5. Les tests locaux ont été effectués avec Node.js v24.19.0. Les invites de commande sont destinées à Node.js 18 et versions ultérieures ; Node.js 18 n'a pas été testé en lui-même.

Huit groupes de comportements ont été définis pour la tâche « slug » et douze pour le « route selector ». Le respect du format et l'exécution complète sont indiqués séparément. Une vérification ultérieure du code est mentionnée séparément et ne modifie pas ces chiffres. Nous n'avons pas modifié le code source renvoyé pour qu'il soit validé.

Tâche 1 : Préserver l'Unicode lors d'une refonte d'un petit « slug »

La fonction devait conserver les lettres non latines et les signes combinatoires, normaliser les accents décomposés et préserver son comportement ASCII existant.

Premiers résultats renvoyés, avec des vérifications du comportement local corrigées
ModèleContrôles de comportementFormat de réponseEffectuer la livraison demandée
Claude Haïku 4.58/8Échec : balise MarkdownOui : fonction complète
Claude Sonnet 58/8Données : JSON brutOui : fonction complète
Claude Opus 5,58/8Données : JSON brutOui : fonction complète

Résultat observé : Ces trois fonctions ont toutes traité les mêmes huit groupes. Sonnet et Opus ont également respecté l'exigence relative à la réponse au format JSON brut. Haiku a encapsulé sa réponse dans du Markdown, et sa note d'évaluation décrivait à tort la fonction NFC comme une décomposition de caractères ; son code appelait toutefois correctement la fonction NFC.

Dans le cadre de cette refonte ponctuelle, la mise à jour n'a pas permis d'améliorer le comportement de la fonction testée. Les exigences de mise en forme et la précision de l'explication qui l'accompagne devaient encore faire l'objet d'une révision.

Consultez l'invite partagée complète et le code de saisie
SYSTEM
You are completing a bounded JavaScript coding task. Return only the requested JSON object with complete source files and brief review notes. Do not claim to have edited files, run commands, or used tools. You have no tool access. Do not include hidden reasoning.

USER
Implement the following Node.js 18+ CommonJS function from its current source. Return complete replacement source for src/slugify.js. Do not add dependencies, imports, I/O, or other files.

Required behavior:
1. slugify(value) returns an empty string for every non-string input.
2. Normalize string input to Unicode NFC, then lowercase it using JavaScript toLowerCase().
3. Preserve Unicode letters, numbers, and combining marks. Replace each consecutive run of all other characters with one ASCII hyphen.
4. Trim leading and trailing hyphens. Preserve the CommonJS export { slugify }.
5. Existing ASCII behavior must continue to work. Examples: Hello, World! -> hello-world; two   words -> two-words; null -> empty string.
6. Additional examples: Café déjà vu -> café-déjà-vu; decomposed Cafe\u0301 -> café; 中文 测试 42 -> 中文-测试-42; **hi___there** -> hi-there; only punctuation or whitespace -> empty string.

Current src/slugify.js:
```javascript
function slugify(value) {
  if (typeof value !== "string") return "";
  return value
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-+|-+$/g, "");
}

module.exports = { slugify };


```

Return exactly one JSON object with this shape:
{"files":{"src/slugify.js":"complete JavaScript source as a JSON string"},"review_notes":["at most four brief implementation or assumption notes"]}
No Markdown wrapper. You are returning code for someone else to evaluate locally; do not say that tests passed or that you ran them.
Consultez la réponse complète de Claude Haiku 4.5 telle qu'elle a été renvoyée
```json
{
  "files": {
    "src/slugify.js": "function slugify(value) {\n  if (typeof value !== \"string\") return \"\";\n  \n  // Normalize to NFC and lowercase\n  const normalized = value.normalize(\"NFC\").toLowerCase();\n  \n  // Replace consecutive runs of non-letter, non-number, non-combining-mark characters with a single hyphen\n  // Unicode letter categories: L* (Lu, Ll, Lt, Lm, Lo)\n  // Unicode number categories: N* (Nd, Nl, No)\n  // Unicode combining mark categories: M* (Mn, Mc, Me)\n  const slugged = normalized.replace(/[^\\p{L}\\p{N}\\p{M}]+/gu, \"-\");\n  \n  // Trim leading and trailing hyphens\n  return slugged.replace(/^-+|-+$/g, \"\");\n}\n\nmodule.exports = { slugify };"
  },
  "review_notes": [
    "Uses String.prototype.normalize(\"NFC\") to decompose composed characters like é into base+combining mark, then toLowerCase() applies to normalized string",
    "Unicode property escapes (\\p{L}, \\p{N}, \\p{M}) with /u flag preserve all Unicode letters, numbers, and combining marks while replacing other consecutive characters with single hyphens",
    "Existing ASCII behavior is preserved: punctuation and whitespace are replaced with hyphens, then leading/trailing hyphens are trimmed",
    "Non-string inputs return empty string as required; normalized decomposed characters (e.g., Cafe\\u0301) are handled correctly by NFC normalization"
  ]
}
```
Consultez la réponse complète de Claude Sonnet 5 telle qu'elle a été renvoyée
{"files":{"src/slugify.js":"function slugify(value) {\n  if (typeof value !== \"string\") return \"\";\n  return value\n    .normalize(\"NFC\")\n    .toLowerCase()\n    .replace(/[^\\p{L}\\p{N}\\p{M}]+/gu, \"-\")\n    .replace(/^-+|-+$/g, \"\");\n}\n\nmodule.exports = { slugify };\n"},"review_notes":["Used Unicode property escapes (\\p{L}, \\p{N}, \\p{M}) with the u flag to preserve letters, numbers, and combining marks across scripts.","NFC normalization ensures decomposed sequences like Cafe + combining acute compose to café before lowercasing.","Runs of any other characters (punctuation, whitespace, symbols) collapse to a single hyphen, then leading/trailing hyphens are trimmed."]}
Consultez la réponse complète de Claude Opus 5.5 telle qu’elle a été renvoyée
{"files":{"src/slugify.js":"function slugify(value) {\n  if (typeof value !== \"string\") return \"\";\n  return value\n    .normalize(\"NFC\")\n    .toLowerCase()\n    .replace(/[^\\p{L}\\p{N}\\p{M}]+/gu, \"-\")\n    .replace(/^-+|-+$/g, \"\");\n}\n\nmodule.exports = { slugify };\n"},"review_notes":["The input is normalized to NFC before lowercasing, so decomposed input like Cafe\\u0301 composes to café.","A Unicode property regex with the u flag keeps letters (\\p{L}), numbers (\\p{N}) and combining marks (\\p{M}). Each run of any other characters, including underscores and whitespace, becomes one hyphen.","The explicit trim() was removed because the final step strips leading and trailing hyphens, which covers surrounding whitespace.","Lowercasing happens after NFC normalization, as the spec orders. toLowerCase can occasionally produce non-NFC output for rare characters, and it is not re-normalized here."]}

Exercice 2 : Réparer un sélecteur d'itinéraire et renvoyer ses tests

La fonction devait gérer les correspondances de préfixes les plus longs, la priorité des méthodes, les limites de segments, les règles mal formées et les entrées immuables. La réponse demandée comprenait également un nouveau fichier de test.

Premiers résultats renvoyés, avec des vérifications du comportement local corrigées
ModèleContrôles de comportementFormat de réponseEffectuer la livraison demandée
Claude Haïku 4.512/12 uniquement pour la fonction complèteÉchec : JSON tronquéNon : fichier de test tronqué
Claude Sonnet 512/12 uniquement pour la fonction complèteÉchec : JSON tronquéNon : fichier de test tronqué
Claude Opus 5,512/12 uniquement pour la fonction complèteÉchec : JSON tronquéNon : fichier de test tronqué

Résultat observé : Aucun modèle n'a réussi à fournir la réponse demandée (deux fichiers) dans la limite de sortie partagée. Les trois se sont arrêtés à 4 096 tokens de fin de traitement. Chacun avait déjà renvoyé une fonction de routage complète, qui avait satisfait aux douze groupes de comportements fixes ; les nouveaux fichiers de test étaient incomplets et n'ont pas été exécutés.

Nous n'avons extrait que les chaînes de code source entièrement fermées. Les réponses tronquées d'origine sont conservées telles quelles ci-dessous. Un score de fonction satisfaisant ne signifie donc pas que la requête complète a abouti.

Autre constat issu de la revue de code : une égalité entre deux racines

Après les vérifications de routine, nous avons remarqué que Haiku s'était stabilisé /// à une chaîne vide plutôt que /. Une vérification locale distincte utilisait deux règles GET, avec les préfixes /// et /, en ciblant premier et deuxième dans cet ordre. Pour /n'importe quoi, le contrat stipule que premier.

Haïku est de retour deuxième; Sonnet et Opus sont de retour premier. Ce contrôle supplémentaire a été déclenché par la révision du code et est distinct des douze contrôles définis avant les tests. Il n'a aucune incidence sur leurs notes.

Sonnet a rencontré une erreur de service avant que sa nouvelle tentative autorisée ne renvoie la réponse tronquée affichée. Cette défaillance du service est indépendante de la qualité du code. Aucun modèle n'a reçu de nouvelle tentative visant à améliorer ou à compléter sa réponse.

Consultez l'invite partagée complète et le code de saisie
SYSTEM
You are completing a bounded JavaScript coding task. Return only the requested JSON object with complete source files and brief review notes. Do not claim to have edited files, run commands, or used tools. You have no tool access. Do not include hidden reasoning.

USER
Repair a Node.js 18+ CommonJS route selector. Return complete replacement src/router.js and an additional test/router-added.test.js using node:test and node:assert/strict. Do not add dependencies or edit existing tests. Do not use I/O other than the test runner, network, child processes, timers, or any files beyond the two requested outputs.

Contract for chooseRoute(request, rules):
1. Invalid request or rules returns null, without throwing. Request must be a non-null, non-array object; method must be a nonempty ASCII-letter string; path must be a string beginning with /. Rules must be an array.
2. Before matching, remove the request path's query and fragment starting at the earliest ? or #. Do not URL-decode or collapse interior slashes.
3. Ignore invalid rules. A valid rule is a non-null, non-array object with method '*' or a nonempty ASCII-letter string, prefix a string starting with / containing neither ? nor #, and target a nonempty string.
4. Compare methods case-insensitively. Prefer any matching exact-method rule over all wildcard-method '*' rules, even if a wildcard has a longer prefix. Use wildcard rules only if no exact-method route matches the path.
5. Remove trailing slashes from a rule prefix, except that an all-slash prefix normalizes to /. A prefix matches the path only at a segment boundary: /api matches /api and /api/users, never /apix. Root / matches every valid request path.
6. Within the chosen method group, choose the longest normalized matching prefix. On equal prefix length, choose the earlier rule in the original input array. Return its target. Return null if none matches.
7. Do not mutate the request, rules array, or rule objects, including order or prefix strings. Preserve module.exports = { chooseRoute }.
8. Add tests for segment boundaries, query/fragment removal, method precedence, ties, malformed inputs and immutability. Existing simple GET /api/users and POST-no-match cases must still pass.

Current src/router.js:
```javascript
function chooseRoute(request, rules) {
  if (!request || !Array.isArray(rules)) return null;
  for (const rule of rules) {
    if (request.method === rule.method && request.path.startsWith(rule.prefix)) {
      return rule.target;
    }
  }
  return null;
}

module.exports = { chooseRoute };


```

Existing regression cases (unchanged): rules [{method:'GET',prefix:'/api',target:'api'},{method:'GET',prefix:'/api/admin',target:'admin'}]; GET /api/users returns 'api'; POST /api/users returns null.

Return exactly one JSON object with this shape:
{"files":{"src/router.js":"complete JavaScript source as a JSON string","test/router-added.test.js":"complete JavaScript tests as a JSON string"},"review_notes":["at most four brief implementation or assumption notes"]}
No Markdown wrapper. You are returning code for someone else to evaluate locally; do not say you edited files, ran tests, or observed passing results.
Consultez la réponse complète de Claude Haiku 4.5 telle qu'elle a été renvoyée

Voici l'intégralité de la réponse reçue. Elle s'arrête en cours de sortie car la limite a été atteinte ; le contenu manquant n'a pas été complété.

```json
{
  "files": {
    "src/router.js": "function chooseRoute(request, rules) {\n  // Validate request\n  if (request === null || Array.isArray(request) || typeof request !== 'object') {\n    return null;\n  }\n  if (typeof request.method !== 'string' || request.method.length === 0 || !/^[a-zA-Z]+$/.test(request.method)) {\n    return null;\n  }\n  if (typeof request.path !== 'string' || !request.path.startsWith('/')) {\n    return null;\n  }\n\n  // Validate rules\n  if (!Array.isArray(rules)) {\n    return null;\n  }\n\n  // Remove query and fragment from path\n  const cleanPath = request.path.split(/[?#]/)[0];\n\n  // Filter and validate rules\n  const validRules = [];\n  for (const rule of rules) {\n    if (rule === null || Array.isArray(rule) || typeof rule !== 'object') {\n      continue;\n    }\n    \n    const method = rule.method;\n    if (typeof method !== 'string' || (method !== '*' && (method.length === 0 || !/^[a-zA-Z]+$/.test(method)))) {\n      continue;\n    }\n    \n    const prefix = rule.prefix;\n    if (typeof prefix !== 'string' || !prefix.startsWith('/') || prefix.includes('?') || prefix.includes('#')) {\n      continue;\n    }\n    \n    const target = rule.target;\n    if (typeof target !== 'string' || target.length === 0) {\n      continue;\n    }\n    \n    validRules.push(rule);\n  }\n\n  // Normalize prefixes and partition into exact-method and wildcard groups\n  const exactMethodRules = [];\n  const wildcardRules = [];\n  \n  for (const rule of validRules) {\n    const normalizedPrefix = rule.prefix === '/' ? '/' : rule.prefix.replace(/\/+$/, '');\n    \n    const entry = {\n      originalRule: rule,\n      normalizedPrefix: normalizedPrefix,\n      method: rule.method\n    };\n    \n    if (rule.method === '*') {\n      wildcardRules.push(entry);\n    } else {\n      exactMethodRules.push(entry);\n    }\n  }\n\n  const requestMethod = request.method.toUpperCase();\n\n  // Helper to check if a prefix matches the path at a segment boundary\n  function prefixMatches(prefix, path) {\n    if (prefix === '/') {\n      return true;\n    }\n    if (!path.startsWith(prefix)) {\n      return false;\n    }\n    // Check segment boundary: next character must be '/' or end of string\n    const nextChar = path[prefix.length];\n    return nextChar === undefined || nextChar === '/';\n  }\n\n  // Try exact-method rules first\n  let bestMatch = null;\n  let bestLength = -1;\n  \n  for (const entry of exactMethodRules) {\n    if (entry.method.toUpperCase() === requestMethod) {\n      if (prefixMatches(entry.normalizedPrefix, cleanPath)) {\n        if (entry.normalizedPrefix.length > bestLength) {\n          bestMatch = entry.originalRule;\n          bestLength = entry.normalizedPrefix.length;\n        }\n      }\n    }\n  }\n  \n  if (bestMatch !== null) {\n    return bestMatch.target;\n  }\n\n  // Try wildcard rules only if no exact-method match\n  bestMatch = null;\n  bestLength = -1;\n  \n  for (const entry of wildcardRules) {\n    if (prefixMatches(entry.normalizedPrefix, cleanPath)) {\n      if (entry.normalizedPrefix.length > bestLength) {\n        bestMatch = entry.originalRule;\n        bestLength = entry.normalizedPrefix.length;\n      }\n    }\n  }\n  \n  if (bestMatch !== null) {\n    return bestMatch.target;\n  }\n\n  return null;\n}\n\nmodule.exports = { chooseRoute };",
    "test/router-added.test.js": "const test = require('node:test');\nconst assert = require('node:assert/strict');\nconst { chooseRoute } = require('../src/router.js');\n\n// Regression: existing test cases\ntest('GET /api/users matches /api prefix (exact method)', () => {\n  const rules = [\n    { method: 'GET', prefix: '/api', target: 'api' },\n    { method: 'GET', prefix: '/api/admin', target: 'admin' }\n  ];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, rules), 'api');\n});\n\ntest('POST /api/users returns null (no matching method)', () => {\n  const rules = [\n    { method: 'GET', prefix: '/api', target: 'api' },\n    { method: 'GET', prefix: '/api/admin', target: 'admin' }\n  ];\n  assert.equal(chooseRoute({ method: 'POST', path: '/api/users' }, rules), null);\n});\n\n// Invalid request handling\ntest('null request returns null', () => {\n  assert.equal(chooseRoute(null, []), null);\n});\n\ntest('array request returns null', () => {\n  assert.equal(chooseRoute([], []), null);\n});\n\ntest('request with missing method returns null', () => {\n  assert.equal(chooseRoute({ path: '/api' }, []), null);\n});\n\ntest('request with empty method returns null', () => {\n  assert.equal(chooseRoute({ method: '', path: '/api' }, []), null);\n});\n\ntest('request with non-letter method returns null', () => {\n  assert.equal(chooseRoute({ method: 'GET-POST', path: '/api' }, []), null);\n});\n\ntest('request with numeric method returns null', () => {\n  assert.equal(chooseRoute({ method: '123', path: '/api' }, []), null);\n});\n\ntest('request with missing path returns null', () => {\n  assert.equal(chooseRoute({ method: 'GET' }, []), null);\n});\n\ntest('request with path not starting with / returns null', () => {\n  assert.equal(chooseRoute({ method: 'GET', path: 'api' }, []), null);\n});\n\ntest('null rules returns null', () => {\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, null), null);\n});\n\ntest('non-array rules returns null', () => {\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, { method: 'GET' }), null);\n});\n\n// Query and fragment removal\ntest('query string is removed before matching', () => {\n  const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/users?foo=bar' }, rules), 'found');\n});\n\ntest('fragment is removed before matching', () => {\n  const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/users#section' }, rules), 'found');\n});\n\ntest('earliest ? or # is used as boundary', () => {\n  const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/users?foo#bar' }, rules), 'found');\n});\n\ntest('fragment before query is used as boundary', () => {\n  const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/users#foo?bar' }, rules), 'found');\n});\n\n// Segment boundary matching\ntest('prefix /api matches /api/users at segment boundary', () => {\n  const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, rules), 'found');\n});\n\ntest('prefix /api does not match /apix (no segment boundary)', () => {\n  const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/apix' }, rules), null);\n});\n\ntest('prefix /api matches /api exactly at segment boundary', () => {\n  const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'found');\n});\n\ntest('root / matches any valid path', () => {\n  const rules = [{ method: 'GET', prefix: '/', target: 'root' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/anything/here' }, rules), 'root');\n});\n\ntest('root / matches /', () => {\n  const rules = [{ method: 'GET', prefix: '/', target: 'root' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/' }, rules), 'root');\n});\n\n// Prefix normalization (trailing slash removal)\ntest('trailing slash is removed from prefix', () => {\n  const rules = [{ method: 'GET', prefix: '/api/', target: 'found' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, rules), 'found');\n});\n\ntest('multiple trailing slashes are removed', () => {\n  const rules = [{ method: 'GET', prefix: '/api///', target: 'found' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, rules), 'found');\n});\n\ntest('all-slash prefix normalizes to single /', () => {\n  const rules = [{ method: 'GET', prefix: '///', target: 'found' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'found');\n});\n\n// Method case-insensitivity\ntest('method comparison is case-insensitive', () => {\n  const rules = [{ method: 'get', prefix: '/api', target: 'found' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'found');\n});\n\ntest('request method case does not affect matching', () => {\n  const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n  assert.equal(chooseRoute({ method: 'get', path: '/api' }, rules), 'found');\n});\n\n// Exact-method vs wildcard precedence\ntest('exact-method rule preferred over wildcard with longer prefix', () => {\n  const rules = [\n    { method: '*', prefix: '/api/admin/users', target: 'wildcard' },\n    { method: 'GET', prefix: '/api', target: 'exact' }\n  ];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/test' }, rules), 'exact');\n});\n\ntest('wildcard rule used when no exact-method matches', () => {\n  const rules = [\n    { method: '*', prefix: '/api', target: 'wildcard' },\n    { method: 'GET', prefix: '/other', target: 'exact' }\n  ];\n  assert.equal(chooseRoute({ method: 'POST', path: '/api/users' }, rules), 'wildcard');\n});\n\n// Longest prefix matching\ntest('longest matching prefix is chosen', () => {\n  const rules = [\n    { method: 'GET', prefix: '/api', target: 'short' },\n    { method: 'GET', prefix: '/api/admin', target: 'long' }\n  ];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/admin/users' }, rules), 'long');\n});\n\n// Tie-breaking (first rule wins on equal prefix length)\ntest('first rule wins on equal prefix length', () => {\n  const rules = [\n    { method: 'GET', prefix: '/api', target: 'first' },\n    { method: 'GET', prefix: '/api', target: 'second' }\n  ];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, rules), 'first');\n});\n\n// Invalid rules are ignored\ntest('null rule is ignored', () => {\n  const rules = [\n    null,\n    { method: 'GET', prefix: '/api', target: 'found' }\n  ];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'found');\n});\n\ntest('array rule is ignored', () => {\n  const rules = [\n    [],\n    { method: 'GET', prefix: '/api', target: 'found' }\n  ];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'found');\n});\n\ntest('rule with invalid method is ignored', () => {\n  const rules = [\n    { method: '', prefix: '/api', target: 'invalid' },\n    { method: 'GET', prefix: '/api', target: 'valid' }\n  ];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'valid');\n});\n\ntest('rule with non-letter method (if not wildcard) is ignored', () => {\n  const rules = [\n    { method: 'G3T', prefix: '/api', target: 'invalid' },\n    { method: 'GET', prefix: '/api', target: 'valid' }\n  ];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'valid');\n});\n\ntest('rule with prefix not starting with / is ignored', () => {\n  const rules = [\n    { method: 'GET', prefix: 'api', target: 'invalid' },\n    { method: 'GET', prefix: '/api', target: 'valid' }\n  ];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'valid');\n});\n\ntest('rule with query in prefix is ignored', () => {\n  const rules = [\n    { method: 'GET', prefix: '/api?foo=bar', target: 'invalid' },\n    { method: 'GET', prefix: '/api', target: 'valid' }\n  ];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'valid');\n});\n\ntest('rule with fragment in prefix is ignored', () => {\n  const rules = [\n    { method: 'GET', prefix: '/api#section', target: 
Consultez la réponse complète de Claude Sonnet 5 telle qu'elle a été renvoyée

Voici l'intégralité de la réponse reçue. Elle s'arrête en cours de sortie car la limite a été atteinte ; le contenu manquant n'a pas été complété.

{"files":{"src/router.js":"function isValidRequest(request) {\n  return (\n    request !== null &&\n    typeof request === 'object' &&\n    !Array.isArray(request) &&\n    typeof request.method === 'string' &&\n    request.method.length > 0 &&\n    /^[A-Za-z]+$/.test(request.method) &&\n    typeof request.path === 'string' &&\n    request.path.startsWith('/')\n  );\n}\n\nfunction isValidRule(rule) {\n  if (rule === null || typeof rule !== 'object' || Array.isArray(rule)) {\n    return false;\n  }\n  const { method, prefix, target } = rule;\n\n  if (typeof method !== 'string') return false;\n  if (method !== '*' && !(method.length > 0 && /^[A-Za-z]+$/.test(method))) {\n    return false;\n  }\n\n  if (\n    typeof prefix !== 'string' ||\n    !prefix.startsWith('/') ||\n    prefix.includes('?') ||\n    prefix.includes('#')\n  ) {\n    return false;\n  }\n\n  if (typeof target !== 'string' || target.length === 0) return false;\n\n  return true;\n}\n\nfunction normalizePrefix(prefix) {\n  if (/^\/+$/.test(prefix)) return '/';\n  return prefix.replace(/\/+$/, '');\n}\n\nfunction prefixMatches(normalizedPrefix, path) {\n  if (normalizedPrefix === '/') return true;\n  if (path === normalizedPrefix) return true;\n  return path.startsWith(normalizedPrefix + '/');\n}\n\nfunction stripQueryAndFragment(path) {\n  let cutIndex = path.length;\n  for (let i = 0; i < path.length; i++) {\n    const c = path[i];\n    if (c === '?' || c === '#') {\n      cutIndex = i;\n      break;\n    }\n  }\n  return path.slice(0, cutIndex);\n}\n\nfunction chooseRoute(request, rules) {\n  if (!isValidRequest(request) || !Array.isArray(rules)) return null;\n\n  const path = stripQueryAndFragment(request.path);\n  const reqMethodLower = request.method.toLowerCase();\n\n  let bestExact = null; // { len, idx, target }\n  let bestWildcard = null;\n\n  for (let idx = 0; idx < rules.length; idx++) {\n    const rule = rules[idx];\n    if (!isValidRule(rule)) continue;\n\n    const normPrefix = normalizePrefix(rule.prefix);\n    if (!prefixMatches(normPrefix, path)) continue;\n\n    const len = normPrefix.length;\n\n    if (rule.method === '*') {\n      if (\n        bestWildcard === null ||\n        len > bestWildcard.len ||\n        (len === bestWildcard.len && idx < bestWildcard.idx)\n      ) {\n        bestWildcard = { len, idx, target: rule.target };\n      }\n    } else if (rule.method.toLowerCase() === reqMethodLower) {\n      if (\n        bestExact === null ||\n        len > bestExact.len ||\n        (len === bestExact.len && idx < bestExact.idx)\n      ) {\n        bestExact = { len, idx, target: rule.target };\n      }\n    }\n  }\n\n  if (bestExact !== null) return bestExact.target;\n  if (bestWildcard !== null) return bestWildcard.target;\n  return null;\n}\n\nmodule.exports = { chooseRoute };\n","test/router-added.test.js":"const test = require('node:test');\nconst assert = require('node:assert/strict');\nconst { chooseRoute } = require('../src/router');\n\ntest('segment boundary: prefix matches at slash boundary only', () => {\n  const rules = [\n    { method: 'GET', prefix: '/api', target: 'api' },\n  ];\n  assert.equal(\n    chooseRoute({ method: 'GET', path: '/api/users' }, rules),\n    'api'\n  );\n  assert.equal(\n    chooseRoute({ method: 'GET', path: '/api' }, rules),\n    'api'\n  );\n  assert.equal(\n    chooseRoute({ method: 'GET', path: '/apix' }, rules),\n    null\n  );\n});\n\ntest('root prefix matches every valid path', () => {\n  const rules = [{ method: 'GET', prefix: '/', target: 'root' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/' }, rules), 'root');\n  assert.equal(\n    chooseRoute({ method: 'GET', path: '/anything/here' }, rules),\n    'root'\n  );\n});\n\ntest('trailing slashes in rule prefix are normalized', () => {\n  const rules = [{ method: 'GET', prefix: '/api///', target: 'api' }];\n  assert.equal(\n    chooseRoute({ method: 'GET', path: '/api/users' }, rules),\n    'api'\n  );\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'api');\n});\n\ntest('all-slash prefix normalizes to /', () => {\n  const rules = [{ method: 'GET', prefix: '////', target: 'root' }];\n  assert.equal(\n    chooseRoute({ method: 'GET', path: '/whatever' }, rules),\n    'root'\n  );\n});\n\ntest('query and fragment are stripped before matching', () => {\n  const rules = [{ method: 'GET', prefix: '/api', target: 'api' }];\n  assert.equal(\n    chooseRoute({ method: 'GET', path: '/api/users?x=1&y=2' }, rules),\n    'api'\n  );\n  assert.equal(\n    chooseRoute({ method: 'GET', path: '/api/users#section' }, rules),\n    'api'\n  );\n  assert.equal(\n    chooseRoute({ method: 'GET', path: '/api/users?x=1#frag' }, rules),\n    'api'\n  );\n});\n\ntest('query/fragment stripping does not decode or collapse slashes', () => {\n  const rules = [{ method: 'GET', prefix: '/a//b', target: 'weird' }];\n  assert.equal(\n    chooseRoute({ method: 'GET', path: '/a//b?x=1' }, rules),
Consultez la réponse complète de Claude Opus 5.5 telle qu’elle a été renvoyée

Voici l'intégralité de la réponse reçue. Elle s'arrête en cours de sortie car la limite a été atteinte ; le contenu manquant n'a pas été complété.

{"files":{"src/router.js":"'use strict';\n\nconst LETTERS = /^[A-Za-z]+$/;\n\nfunction isPlainObject(value) {\n  return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isMethodName(value) {\n  return typeof value === 'string' && value.length > 0 && LETTERS.test(value);\n}\n\nfunction stripQueryAndFragment(path) {\n  const q = path.indexOf('?');\n  const h = path.indexOf('#');\n  let cut = -1;\n  if (q !== -1 && h !== -1) cut = Math.min(q, h);\n  else if (q !== -1) cut = q;\n  else if (h !== -1) cut = h;\n  return cut === -1 ? path : path.slice(0, cut);\n}\n\nfunction normalizePrefix(prefix) {\n  let end = prefix.length;\n  while (end > 0 && prefix[end - 1] === '/') end -= 1;\n  return end === 0 ? '/' : prefix.slice(0, end);\n}\n\nfunction prefixMatches(path, prefix) {\n  if (prefix === '/') return true;\n  return path === prefix || path.startsWith(prefix + '/');\n}\n\nfunction chooseRoute(request, rules) {\n  try {\n    if (!isPlainObject(request) || !Array.isArray(rules)) return null;\n    const method = request.method;\n    const rawPath = request.path;\n    if (!isMethodName(method)) return null;\n    if (typeof rawPath !== 'string' || rawPath[0] !== '/') return null;\n\n    const path = stripQueryAndFragment(rawPath);\n    const upperMethod = method.toUpperCase();\n\n    let bestExact = null;\n    let bestWildcard = null;\n\n    const count = rules.length;\n    for (let i = 0; i < count; i += 1) {\n      const rule = rules[i];\n      if (!isPlainObject(rule)) continue;\n      const ruleMethod = rule.method;\n      const prefix = rule.prefix;\n      const target = rule.target;\n      const isWildcard = ruleMethod === '*';\n      if (!isWildcard && !isMethodName(ruleMethod)) continue;\n      if (typeof prefix !== 'string' || prefix[0] !== '/') continue;\n      if (prefix.indexOf('?') !== -1 || prefix.indexOf('#') !== -1) continue;\n      if (typeof target !== 'string' || target.length === 0) continue;\n\n      const normalized = normalizePrefix(prefix);\n      if (!prefixMatches(path, normalized)) continue;\n\n      const candidate = { length: normalized.length, target };\n      if (isWildcard) {\n        if (bestWildcard === null || candidate.length > bestWildcard.length) bestWildcard = candidate;\n      } else if (ruleMethod.toUpperCase() === upperMethod) {\n        if (bestExact === null || candidate.length > bestExact.length) bestExact = candidate;\n      }\n    }\n\n    if (bestExact !== null) return bestExact.target;\n    if (bestWildcard !== null) return bestWildcard.target;\n    return null;\n  } catch (err) {\n    return null;\n  }\n}\n\nmodule.exports = { chooseRoute };\n","test/router-added.test.js":"'use strict';\n\nconst test = require('node:test');\nconst assert = require('node:assert/strict');\nconst { chooseRoute } = require('../src/router.js');\n\nconst baseRules = [\n  { method: 'GET', prefix: '/api', target: 'api' },\n  { method: 'GET', prefix: '/api/admin', target: 'admin' }\n];\n\ntest('existing regression cases still hold', () => {\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, baseRules), 'api');\n  assert.equal(chooseRoute({ method: 'POST', path: '/api/users' }, baseRules), null);\n});\n\ntest('longest prefix wins within method group', () => {\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/admin/x' }, baseRules), 'admin');\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/admin' }, baseRules), 'admin');\n});\n\ntest('segment boundaries', () => {\n  const rules = [{ method: 'GET', prefix: '/api', target: 'api' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'api');\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/' }, rules), 'api');\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, rules), 'api');\n  assert.equal(chooseRoute({ method: 'GET', path: '/apix' }, rules), null);\n  assert.equal(chooseRoute({ method: 'GET', path: '/ap' }, rules), null);\n});\n\ntest('trailing slashes in prefixes are normalized, all-slash is root', () => {\n  const rules = [{ method: 'GET', prefix: '/api///', target: 'api' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'api');\n  assert.equal(chooseRoute({ method: 'GET', path: '/apix' }, rules), null);\n  const rootRules = [{ method: 'GET', prefix: '///', target: 'root' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/' }, rootRules), 'root');\n  assert.equal(chooseRoute({ method: 'GET', path: '/anything/else' }, rootRules), 'root');\n  assert.equal(chooseRoute({ method: 'GET', path: '/x' }, [{ method: 'GET', prefix: '/', target: 'r' }]), 'r');\n});\n\ntest('query and fragment are removed at earliest ? or #', () => {\n  assert.equal(chooseRoute({ method: 'GET', path: '/api?next=/api/admin' }, baseRules), 'api');\n  assert.equal(chooseRoute({ method: 'GET', path: '/api#/admin' }, baseRules), 'api');\n  assert.equal(chooseRoute({ method: 'GET', path: '/apix?/api' }, baseRules), null);\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/admin#frag?q' }, baseRules), 'admin');\n  assert.equal(chooseRoute({ method: 'GET', path: '/api?q#/admin' }, baseRules), 'api');\n});\n\ntest('no URL decoding and no slash collapsing', () => {\n  const rules = [{ method: 'GET', prefix: '/api/admin', target: 'admin' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api%2Fadmin' }, rules), null);\n  assert.equal(chooseRoute({ method: 'GET', path: '/api//admin' }, rules), null);\n  assert.equal(chooseRoute({ method: 'GET', path: '/api//admin' }, baseRules), 'api');\n});\n\ntest('methods compare case-insensitively', () => {\n  const rules = [{ method: 'get', prefix: '/a', target: 'lower' }];\n  assert.equal(chooseRoute({ method: 'GET', path: '/a' }, rules), 'lower');\n  assert.equal(chooseRoute({ method: 'gEt', path: '/a/b' }, rules), 'lower');\n});\n\ntest('exact method beats longer wildcard; wildcard used only as fallback', () => {\n  const rules = [\n    { method: '*', prefix: '/api/admin', target: 'wild-admin' },\n    { method: 'GET', prefix: '/', target: 'get-root' },\n    { method: 'POST', prefix: '/other', target: 'post-other' },\n    { method: '*', prefix: '/api', target: 'wild-api' }\n  ];\n  assert.equal(chooseRoute({ method: 'GET', path: '/api/admin/x' }, rules), 'get-root');\n  assert.equal(chooseRoute({ method: 'POST', path: '/api/admin/x' }, rules), 'wild-admin');\n  assert.equal(chooseRoute({ method: 'POST', path: '/api/x' }, rules), 'wild-api');\n  assert.equal(chooseRoute({ method: 'POST', path: '/other/1' }, rules), 'post-other');\n  assert.equal(chooseRoute({ method: 'PUT', path: '/nothing' }, rules), null);\n});\n\ntest('ties go to earlier rule in input order', () => {\n  const rules = [\n    { method: 'GET', pref

La limite de 4 096 tokens correspondait au budget de sortie choisi pour ce test, et non à la capacité maximale documentée d’un modèle quelconque. Le résultat du routage suggère de procéder à une vérification pratique avant d’envoyer une requête plus volumineuse : estimez la taille de l’implémentation, des tests et de l’explication que vous vous attendez à recevoir en retour. Il peut être judicieux de fractionner les livrables ou d’allouer davantage d’espace de sortie ; nous n’avons pas réexécuté ces sorties avec des paramètres différents.

Pour votre propre travail, évaluez trois éléments séparément : si la réponse est complète, si le code passe avec succès des tests pertinents, et si la révision met en évidence des lacunes que ces tests n'ont pas détectées. Le fait d'avoir réussi douze tests de fonction ne suffisait pas à faire de l'une de ces réponses tronquées, composées de deux fichiers, une livraison complète.

En quoi le choix du modèle influe-t-il sur le coût ?

« Claude Code » est l'outil de codage ; Haiku, Sonnet, Opus et Fable sont des modèles disponibles dans l'environnement pris en charge. Le mode d'authentification et de paiement constitue un autre choix. Changer de modèle ne transforme pas un abonnement en compte API et ne supprime pas toutes les limites d'utilisation.

Mode de facturationÀ ne pas manquerComment interpréter le choix du modèle
Accès par abonnement ClaudeUtilisation comprise dans le forfait et crédits d'utilisation activés séparémentLa consommation des quotas peut varier selon les modèles ; consultez les modèles disponibles et les limites du forfait.
Accès à l'API AnthropicJetons d'entrée/sortie, mise en cache et frais d'API applicablesUtilisez la grille tarifaire en vigueur et les données de consommation réelles, puis validez les frais dans la console de facturation
Fournisseur de services cloud ou passerelleIdentifiants, tarifs et relevés de facturation pris en charge par votre prestataireLe prix catalogue indiqué pour un produit Anthropic peut ne pas correspondre au montant facturé par votre fournisseur.
Le choix du forfait et la facturation sont liés, mais c'est le mode de facturation qui détermine la manière dont la consommation est facturée.

Dans le code Claude, /usage affiche les informations relatives au jeton de session et, le cas échéant, les barres d'utilisation de l'abonnement. Le montant indiqué en dollars est une estimation calculée localement sur la base des tarifs indiqués dans la liste ou configurés ; il ne s'agit pas d'une facture. Pour les abonnés aux formules Pro et Max, ce montant lié à la session ne signifie pas qu'une facturation supplémentaire liée à l'API a été ajoutée à l'abonnement. En savoir plus sur Claude : tarification et modes de facturation si vous êtes en train de choisir le mode de paiement.

Cours actuels de l'API et exemple de jeton équivalent

Les taux de base habituels de Anthropic par million de jetons d'entrée/sortie sont les suivants : Haiku 4.5 : $1/$5 ; Sonnet 5 : $2/$10 ; Opus 5.5 : $4/$20 ; Fable 5.1 : $10/$50, consulté le 28 septembre 2026. Le graphique ci-dessous présente un volume de jetons constant, ce qui facilite la comparaison des chiffres.

Même volume de jetons, quatre coûts d'API différents

Exemple : 100 000 jetons d'entrée non mis en cache + 10 000 jetons de sortie, sur la base des prix catalogue normaux de l'API de Anthropic, vérifiés le 28 septembre 2026.

Haiku 4.5$0.15
Sonnet n° 5$0.30
Opus 5.5$0.60
Fable 5.1$1.50
Consultez les tarifs et le mode de calcul
USD par million de jetons et l'exemple de calcul
ModèleEntrée / 1MSortie / 1MExemple de total
Haiku 4.5$1$5$0.15
Sonnet n° 5$2$10$0.30
Opus 5.5$4$20$0.60
Fable 5.1$10$50$1.50

Calcul : 0,1 × débit d'entrée + 0,01 × débit de sortie. Pour Opus 5.5 : 0,1 × $4 + 0,01 × $20 = $0,60.

Il s'agit d'un exemple illustratif basé sur un nombre fixe de jetons. Les tâches réelles consomment des nombres de jetons variables ; les écritures et lectures en cache, les tentatives de réessai, les outils, la facturation des fournisseurs et les limites d'abonnement doivent faire l'objet d'une comptabilisation distincte.

Source : Tarification de l'API Anthropic.

Tableau des tarifs de l'API Anthropic indiquant les tarifs d'entrée, de sortie et de mise en cache des invites.
Les prix catalogue normaux de l'API sont indiqués par million de jetons ; les écritures et les lectures en cache font l'objet de tarifs distincts.

Une véritable tâche de type « Claude Code » est une séquence de requêtes. Le contexte antérieur peut être renvoyé, un modèle peut approfondir son analyse, et des outils ou des agents supplémentaires peuvent générer davantage de travail. La mise en cache des invites présente également des taux d'écriture et de lecture distincts. La barre la plus basse de l'exemple ne correspond donc pas au coût le plus bas par modification acceptée.

Cinq façons de maîtriser les coûts sans compromettre la qualité du travail

  1. Définissez les critères d'acceptation avant de choisir un modèle. Précisez le comportement attendu, les cas limites et les contraintes existantes. Une petite tâche clairement définie est plus facile à déléguer de manière économique et plus simple à vérifier.
  2. Donnez le contexte nécessaire. Joignez les fichiers et les détails de l'erreur dont le modèle a besoin. Pour les sujets sans rapport avec ce sujet, lancez une nouvelle discussion plutôt que de reprendre un historique trop long simplement parce qu'il existe déjà.
  3. Il faut distinguer la capacité de l'effort. Anthropic recommande de commencer par le niveau d'effort par défaut du modèle. Si celui-ci a correctement analysé le contexte mais n'a toujours pas réussi à résoudre le problème, un modèle plus puissant pourrait s'avérer utile. S'il a ignoré l'étape de lecture ou de vérification, vérifiez la configuration de la tâche et le choix du niveau d'effort.
  4. Budget pour l'ensemble de la réponse. Le code source, les tests et les explications nécessitent tous de l'espace. Notre deuxième exercice montre pourquoi le fait de disposer d'une fonction complète ne suffit pas à lui seul à prouver que la livraison demandée respecte le budget.
  5. Comparer les travaux acceptés. Enregistrez les résultats utiles, les tentatives de reprise, le temps de révision et les données de facturation réelles pour des tâches représentatives. Distinguez les erreurs des prestataires des défaillances liées à la qualité du modèle.

Ces stratégies s'inscrivent dans le cadre de la guide officiel des tarifs et Recommandations de Anthropic concernant le modèle et l'effort. Une limite hebdomadaire ou par session applicable à l'ensemble du forfait peut rester en vigueur après un changement de forfait ; il en va autrement pour une limite spécifique à un forfait. Vérifiez la limite effective avant d'effectuer des changements répétés.

Si votre travail mêle programmation, recherche, rédaction et médias, comparez également le flux de travail pour lequel vous payez. L'espace de travail multimodèle de GlobalGPT rassemble ces fonctionnalités au sein d'un tableau de bord unique, avec un accès via l'interface de ligne de commande (CLI) pour les tâches de développement. Il est utile de prendre en compte cette valeur ajoutée au niveau du flux de travail global, parallèlement à une route API facturée à l'utilisation ; le tableau des API ci-dessus ne constitue pas une liste de prix des abonnements GlobalGPT.

Pourquoi le modèle que vous avez choisi pourrait ne pas convenir

SymptômeVérification probableProchaine étape concrète
La session suivante utilise l'ancien modèleIl se peut que ce commutateur n'ait été actif que pendant la session.Utilisez l'action « Entrée » du sélecteur pour enregistrer, puis sélectionnez une nouvelle session
La préférence est enregistrée, mais c'est un autre modèle qui démarreUn paramètre de lancement, de shell, de projet, de gestion ou d'organisation peut prévaloirLisez les informations relatives au démarrage et vérifiez la configuration correspondante avec son responsable
Une conversation reprise suit un modèle différentLa session enregistrée peut rétablir son modèle précédentVérifiez /status ; choisissez soigneusement le modèle souhaité pour la reprise du travail
Le modèle est manquant ou a été rejetéDisponibilité des prestataires, règles organisationnelles ou ancienne version du code ClaudeVérifiez les identifiants des modèles pris en charge, les droits d'accès au compte et les exigences en matière de version
Un même nom de famille peut se traduire différemment selon les paysLes alias et les correspondances de fournisseurs diffèrentUtilisez l'identifiant complet correct du fournisseur lorsqu'une version doit rester inchangée
Le changement d'opérateur n'entraîne pas la réinitialisation d'une limite d'utilisationLa limite peut s'appliquer à l'ensemble du planLisez le message d'utilisation et réinitialisez la période plutôt que de supposer qu'un changement de forfait familial suffit
Évaluez l'ampleur du problème avant de modifier un autre paramètre.

Si le code Claude ne parvient pas à enregistrer vos paramètres utilisateur, une option interactive peut s'appliquer à la session en cours sans être conservée. Un fichier de paramètres géré ou généré peut nécessiter une modification de la part de son propriétaire plutôt qu'une nouvelle tentative de sélection. Évitez de supprimer les restrictions organisationnelles dans le seul but de faire apparaître une option.

Développez un flux de travail plus complet avec GlobalGPT

La sélection de modèles ne constitue qu'une partie du flux de travail. Vous pouvez être amené à effectuer des recherches dans une bibliothèque, à rédiger une ébauche de fonction, à expliquer une modification à un collègue, à créer de la documentation et à préparer des ressources visuelles. GlobalGPT regroupe plusieurs modèles de pointe et fonctions d'IA au sein d'un espace de travail abordable, ce qui permet de regrouper toutes ces tâches dans un même tableau de bord.

Pour les opérations en terminal, le GlobalGPT CLI relie les outils de la plateforme aux environnements de développement. La procédure de configuration décrite dans la documentation utilise les identifiants de votre compte GlobalGPT et le solde habituel de ce compte. L'interface en ligne de commande (CLI) permet de prendre en charge un flux de travail plus large en matière de codage et de création, tandis que le modèle propre à Claude Code reste un choix délibéré grâce à ses commandes de gestion du modèle.

Par exemple, rédigez et relisez un plan de mise en œuvre, utilisez un autre modèle pour fournir une explication concise, puis créez la documentation ou les ressources qui accompagnent la version. Suivez le guide pour Relier l'interface CLI du GlobalGPT au code du Claude lorsque vous souhaitez intégrer cet outil. Configurez votre modèle de raisonnement Claude séparément, en fonction de la tâche de codage.

Choisissez un modèle de départ, définissez les critères qui permettront de déterminer si la tâche est réussie, puis passez à l'étape suivante lorsque la tâche l'exige. Vérifiez l'ensemble du résultat avant de considérer le travail comme terminé. Créez votre flux de travail avec GlobalGPT lorsque vous souhaitez disposer d'outils de sélection de modèles, de recherche, de programmation et de création interconnectés dans le cadre de votre travail quotidien.

FAQ

Comment puis-je modifier le modèle dans le code Claude ?

Exécutez la commande /model pour ouvrir la fenêtre de sélection. Appuyez sur la touche « s » pour modifier uniquement la session en cours, ou sur Entrée pour enregistrer le modèle sélectionné comme modèle par défaut. Pour choisir un modèle pour une seule nouvelle session, lancez la commande claude –model suivie du nom du modèle.

La commande /model modifie-t-elle mes paramètres par défaut enregistrés ?

Dans les versions interactives actuelles, sélectionner un modèle à l'aide de la touche Entrée ou saisir /model sonnet permet d'enregistrer ce choix dans les paramètres utilisateur si ceux-ci sont accessibles en écriture. Appuyer sur la touche « s » dans le sélecteur n'a qu'une validité limitée à la session en cours. D'autres paramètres peuvent toutefois prévaloir lors du prochain démarrage.

Quel est le modèle par défaut du code Claude ?

La valeur par défaut d'exécution dépend de votre compte, de votre fournisseur et de votre configuration. La valeur par défaut efface toute redéfinition de modèle ; il ne s'agit pas d'un identifiant de modèle fixe. Vérifiez le modèle actif à l'aide de la commande /status et consultez le sélecteur plutôt que de partir du principe que tous les comptes démarrent avec la même version.

Que fait Opusplan ?

Il utilise Opus en mode planification et Sonnet en mode exécution. Il suit la transition entre les modes plutôt que de choisir automatiquement le modèle le moins coûteux pour chaque requête. Le coût réel dépend du travail effectué à chaque phase.

Pourquoi un modèle n'apparaît-il pas dans la liste de sélection ?

La disponibilité peut dépendre de votre fournisseur, de votre compte, des restrictions de votre organisation et de la version du code Claude. Consultez la documentation du modèle actuel et la liste des modèles autorisés dans votre environnement. Un alias de famille peut également renvoyer à une version différente chez un autre fournisseur.

Est-ce qu'un modèle moins cher permet toujours de réduire le coût d'une tâche ?

Non. Le prix du jeton n'est qu'un facteur parmi d'autres. L'utilisation totale, les réponses incomplètes, les nouvelles tentatives et le travail de révision ont tous une incidence sur le coût d'une modification acceptable. Lors de notre petite refactorisation, les trois fonctions ont été validées, tandis que les trois réponses plus volumineuses, composées de deux fichiers, ont été tronquées en raison de la limite de sortie commune.

Sources

Partager l'article :

Articles connexes