Claude-codemodelselectie: standaardinstellingen, omschakelen en kosten

Claude-codemodelselectie: standaardinstellingen, omschakelen en kosten

Gebruik bij de modelkeuze volgens de Claude-code Haiku voor kleine, testbare wijzigingen, Sonnet voor routinematige implementaties en Opus voor werkzaamheden die meer onduidelijkheid of redeneringsinspanning vereisen. Anthropic raadt momenteel Opus 5.5 aan als algemeen uitgangspunt; uw takenpakket, de omvang van de beoordelingswerkzaamheden en uw budget bepalen of een kleiner model beter bij u past.

Om van model te wisselen, open je /model. Druk op Ga naar om een standaardinstelling op te slaan, of s om alleen de huidige sessie om te schakelen. Gebruik voor een eenmalige start claude --model opus. Die keuzes hebben een verschillende duurzaamheid, dus het is net zo belangrijk om te bepalen hoe lang de schakelaar mee moet gaan als het kiezen van het model.

Programmeerwerk omvat ook onderzoek, uitleg, documentatie en creatieve middelen. GlobalGPT brengt toonaangevende AI-modellen en diverse AI-tools samen in één betaalbaar dashboard, terwijl de CLI deze tools koppelt aan terminal- en ontwikkelingsworkflows. Stel je workflow samen met GlobalGPT om die gerelateerde taken bij elkaar te houden terwijl je van een idee naar werkende code gaat.

Welk model moet je gebruiken in de Claude-code?

Begin bij de opzet van het werk. Een korte functie met duidelijke verwachte resultaten vereist een andere beoordeling dan een sporadisch voorkomende bug die zich over meerdere diensten uitstrekt. De relevante vraag is of het model met een redelijke mate van controle en iteratie tot een aanvaardbare wijziging kan leiden.

Huidig modelWaarvoor het bedoeld isEen praktische opstartopdracht
Claude Haiku 4.5Snel en efficiënt werken met een beperkt contractEen kleine refactor, een repetitieve transformatie of een eenvoudige testcase
Claude Sonnet 5Een evenwicht tussen intelligentie en snelheid voor het dagelijkse programmeerwerkEen functie met vaste patronen, een routinematige bugfix of een uitleg van de code
Claude Opus 5,5Complexe redeneringen en langdurige agentische coderingEen dubbelzinnige mislukking, een architecturale afweging of een verandering waarbij verschillende beperkingen op elkaar inwerken
Claude Fable 5.1De moeilijkste en langstlopende onderzoekenEen escalatie waarbij moeilijk werk nog steeds de overhand heeft op een grotere inspanning van Opus
Huidige modelfamilies en geschiktheid voor taken, op basis van de Anthropic-documentatie; de voorgestelde taken vormen praktische uitgangspunten.

Haiku is het overwegen waard wanneer fouten gemakkelijk op te sporen zijn en de wijziging klein is. Sonnet is een verstandige keuze wanneer de dagelijkse implementatie meer context en inzicht vereist. Opus verdient aandacht wanneer het begrijpen van het probleem een aanzienlijk deel van de taak uitmaakt. Fable verdient een aparte afweging voor veeleisend werk; het viel buiten onze coderingstest met drie modellen.

Dit zijn uitgangspunten, geen automatische kwaliteitsgaranties. Een goedkopere oplossing kan duur uitvallen als er meerdere herzieningen of een uitgebreide beoordeling nodig zijn. Omgekeerd kan een groter model weinig toevoegen aan een eenvoudige transformatie die al aan je controles voldoet. Voor breder gebruik buiten Claude Code, vergelijk de Claude-modelfamilies.

De huidige modelvergelijking van Anthropic voor Fable 5.1, Opus 5.5, Sonnet 5 en Haiku 4.5.
Het modelaanbod van Anthropic, gecontroleerd op 28 september 2026. De beschikbaarheid in Claude Code is afhankelijk van je provider en je account.

De officieel modeloverzicht geeft een overzicht van contextvensters van 1 miljoen tokens voor Fable 5.1, Opus 5.5 en Sonnet 5, en van 200.000 tokens voor Haiku 4.5. Dit zijn API-specificaties. Uw Claude-codeprovider, account en configuratie bepalen welke contextopties beschikbaar zijn in een sessie. Een groot venster is handig voor noodzakelijke context; het vullen ervan met niet-gerelateerd materiaal zorgt nog steeds voor extra werk voor het model.

Hoe je tijdens één sessie van model kunt wisselen

Tijdens een interactieve sessie, uitgevoerd /model om de modelkiezer te openen. Selecteer de modelrij en druk op s als dit een eenmalige wijziging is. Druk op Ga naar slaat de selectie op als je standaardinstelling. Door te typen /model sonnet In de huidige versies wordt het ook direct opgeslagen.

Kies de opdracht op basis van hoe lang de wijziging moet duren

Gedocumenteerd gedrag van de Claude-code, gecontroleerd op 28 september 2026
Je doelWat te doenZijn de standaardwijzigingen opgeslagen?
Alleen deze interactieve sessie omschakelenOpen /model, kies een model, druk op s.Nee.
Een favoriet model opslaanOpen /model en druk op Ga naar, of typ /model sonnet.Ja, wanneer de gebruikersinstellingen kunnen worden bewerkt.
Start een enkele sessie op OpusLancering claude --model opus.Nee.
Terug naar de standaardinstelling voor de uitvoeringstijd van het accountGebruik /standaardmodel.Hiermee wordt de modeloverschrijving ongedaan gemaakt; de vastgestelde standaardinstelling kan afhankelijk zijn van account- en organisatie-instellingen.
Controleer het actieve modelUitvoeren /status.Nee.

Project- of beheerde instellingen, omgevingsoverschrijvingen en hervatte sessies kunnen van invloed zijn op de volgende start. Controleer het actieve model wanneer reproduceerbaarheid van belang is.

Bron: Claude-codeconfiguratie.

Kies bij het opstarten van een nieuwe terminalsessie het model. Deze opdrachten gelden ook wanneer Claude Code in de geïntegreerde terminal van VS Code wordt uitgevoerd. In de voorbeelden wordt expliciet een release geselecteerd:

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

Voer de opdracht uit voor de gewenste sessie; de voorbeelden zijn alternatieven. Je kunt verschillende terminals starten met verschillende -model waarden zonder een gedeelde, opgeslagen standaardinstelling steeds opnieuw te wijzigen. Gebruik /status om het actieve model te controleren.

Achternamen zoals opus en sonnet kunnen worden omgezet naar nieuwere versies. De resolutie hangt ook af van de provider: de Anthropic-API koppelt ze momenteel aan Opus 5.5 en Sonnet 5, terwijl de koppelingen van cloudproviders kunnen verschillen. Gebruik een volledige model-ID wanneer je een reproduceerbare run nodig hebt, en controleer de providerspecifieke naam als je omgeving dat vereist.

Voor Opus 5.5 is Claude Code v2.1.280 of hoger vereist; voor Sonnet 5 is v2.1.197 of hoger vereist. De documentatie over modelconfiguratie is de bron voor actuele aliassen, versievereisten en toewijzingen aan providers. De opdrachten hier zijn voorbeelden uit de documentatie; in ons praktijkgedeelte wordt de gegenereerde code apart geëvalueerd.

Claude-codedocumentatie met uitleg over de modelkeuze en de toetsen Enter en s.
Met Enter wordt het model als standaard opgeslagen; met s worden alleen de instellingen voor de huidige sessie gewijzigd.

Hoe stel je een standaardmodel in?

Als je een standaardinstelling wilt kiezen, selecteer dan een model in /model en druk op Ga naar, of typ een selectie zoals /model sonnet. De code Claude schrijft de model veld in je gebruikersinstellingen wanneer dat bestand beschrijfbaar is. Open een nieuwe sessie en controleer het actieve model om het resultaat te bevestigen.

Een opgeslagen voorkeur heeft geen voorrang op alle andere instellingen. Een opstartvlag, ANTHROPIC_MODEL in de shell, project- of beheerde instellingen, of een organisatiestandaard die is geconfigureerd om gebruikerskeuzes te overschrijven, kan bij het opstarten iets anders selecteren. Een hervatte sessie kan ook het model uit het opgeslagen transcript behouden. Controleer de opstartinformatie en /status voordat hij ervan uitging dat de kans verkeken was.

/standaardmodel wist de aanpassingen aan je model en keert terug naar de standaardinstellingen van de runtime voor je account en configuratie. Het woord standaard is een resetwaarde, niet de naam van één vast Claude-model. Het mag niet worden gebruikt als een reproduceerbare modelidentificatie.

Voor geavanceerde instellingen, ANTHROPIC_DEFAULT_MODEL biedt vanaf versie 2.1.236 een standaardstartoptie. Deze staat onder de expliciete keuzes voor ‘lancering’, ‘omgeving’, ‘instellingen’ en ‘organisatie’, en negeert waarden zoals haiku en opusplan. Bij een normale persoonlijke voorkeur is de picker gemakkelijker te controleren. Zie Claude Code-instellingen wanneer er sprake is van een gedeelde configuratie.

Wat Opusplan eigenlijk doet

opusplan maakt gebruik van Opus in de planningsfase en van Sonnet voor de uitvoering. Het voordeel is duidelijk: gebruik een krachtiger redeneermodel om de aanpak uit te werken, en vervolgens een alledaags programmeermodel om dat plan uit te voeren.

Opusplan volgt de werkwijze

PlanningsmodusOpusWerk de aanpak stap voor stap door.
UitvoeringSonnetVoer het plan uit.

Gedocumenteerd routeringsgedrag. De versies die achter deze familienamen schuilgaan, zijn afhankelijk van uw provider en configuratie.

Bron: Claude-codeconfiguratie

Dit is een modusafhankelijke regel. Niet elke prompt wordt gecontroleerd en er wordt niet automatisch de goedkoopste geschikte familie gekozen. De toewijzingen van de provider en de toegangsbeperkingen blijven van toepassing op de onderliggende modellen. Ook garandeert de naam geen kostenbesparing: de totale kosten hangen af van de hoeveelheid planning, implementatie, herziening en contextverwerking die de taak vereist.

Overweeg het eens voor de verandering: een situatie waarin het ontwerp profiteert van een grondigere analyse en de uitvoering volgens een duidelijk plan verloopt. Voor een kleine aanpassing is die extra planning wellicht overbodig.

Welke twee programmeertaken kwamen aan bod?

Om een concreet beeld te geven van modelselectie, hebben we een beperkte Unicode-refactor en een aanpassing aan de routeselector getest, waarbij de vereisten op elkaar inwerkten. Het bleek dat het onderscheid tussen een correcte functie en een volledige, gevraagde uitvoer belangrijker was dan een simpele score.

Hoe we de programmeerresultaten hebben getest

Op 28 september 2026 hebben we Claude Haiku 4,5, Claude Sonnet 5 en Claude Opus 5,5 dezelfde twee JavaScript-opdrachten gegeven via GlobalGPT. Elk model leverde broncode op; we hebben de code geïnspecteerd en vaste lokale controles uitgevoerd. De instructies voor het wisselen van model in Claude Code elders in deze handleiding zijn afkomstig uit de officiële documentatie ervan.

Elk verzoek had een uitvoerlimiet van 4.096 tokens, er waren geen hulpmiddelen of gespreksgeschiedenis beschikbaar en de modelinstellingen waren standaard. We hebben het eerste beoordeelbare antwoord behouden, inclusief onvolledige antwoorden. Bij één servicefout tijdens de routeringstaak van Sonnet was één herpoging toegestaan: zeven verzoeken leverden zes uitvoerresultaten op.

Deze twee kleine taken meten de geretourneerde code en de volledigheid van de levering. Ze meten niet de uitvoering door de agent van de Claude-code, het doorzoeken van de repository of een volledige ontwikkelingssessie. Er zijn geen daadwerkelijke kosten in dollars gerapporteerd. Eén enkele waarneming per taak is onvoldoende om een algemene ranglijst op basis van snelheid of betrouwbaarheid op te stellen.

Bekijk de exacte model-ID's en evaluatievoorwaarden

Gevraagde ID's: claude-haiku-4-5-20251001, claude-sonnet-5, en claude-opus-5-5. De lokale tests zijn uitgevoerd met Node.js v24.19.0. De instructies zijn bedoeld voor Node.js 18 en hoger; Node.js 18 zelf is niet getest.

Voor de slak-taak werden acht gedragsgroepen vastgesteld en voor de route-selector twaalf. Naleving van het formaat en volledige levering worden afzonderlijk gerapporteerd. Een latere controle in het kader van de codereview wordt apart vermeld en heeft geen invloed op deze aantallen. We hebben de teruggestuurde broncode niet aangepast om deze goedgekeurd te krijgen.

Opdracht 1: Unicode behouden bij het herstructureren van een kleine slug

De functie moest niet-Latijnse letters en combinatie-tekens behouden, ontleedde accenten normaliseren en het bestaande ASCII-gedrag behouden.

Eerste retourwaarden, met controles op vastgelegd lokaal gedrag
ModelGedragscontrolesFormaat van het antwoordDe gevraagde levering voltooien
Claude Haiku 4.58/8Fout: Markdown-wrapperJa: volledige functie
Claude Sonnet 58/8Pass: onbewerkte JSONJa: volledige functie
Claude Opus 5,58/8Pass: onbewerkte JSONJa: volledige functie

Waargenomen resultaat: Alle drie de functies verwerkten dezelfde acht groepen. Sonnet en Opus voldeden ook aan de vereiste voor een ruwe JSON-respons. Haiku verpakte zijn respons in Markdown, en in de toelichting bij de beoordeling werd NFC ten onrechte omschreven als het ontleden van tekens; in de code werd NFC echter correct aangeroepen.

Bij deze beperkte herstructurering leidde de upgrade niet tot een verbetering van het gedrag van de geteste functie. De opmaakvereisten en de nauwkeurigheid van de bijbehorende uitleg moesten nog worden gecontroleerd.

Bekijk de volledige gedeelde prompt en de invoercode
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.
Bekijk het volledige antwoord van Claude Haiku 4.5 zoals het is teruggestuurd
```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"
  ]
}
```
Bekijk het volledige antwoord van Claude Sonnet 5 zoals het is teruggestuurd
{"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."]}
Bekijk het volledige antwoord van Claude Opus 5.5 zoals het is teruggestuurd
{"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."]}

Opdracht 2: Een routeschakelaar repareren en de bijbehorende tests uitvoeren

De functie moest problemen oplossen met betrekking tot overeenkomsten met de langste prefix, de prioriteit van methoden, segmentgrenzen, onjuist opgebouwde regels en onveranderlijke invoer. Het gevraagde antwoord bevatte ook een nieuw testbestand.

Eerste retourwaarden, met controles op vastgelegd lokaal gedrag
ModelGedragscontrolesFormaat van het antwoordDe gevraagde levering voltooien
Claude Haiku 4.512/12 alleen voor de volledige functieFout: afgekorte JSONNee: testbestand is afgekapt
Claude Sonnet 512/12 alleen voor de volledige functieFout: afgekorte JSONNee: testbestand is afgekapt
Claude Opus 5,512/12 alleen voor de volledige functieFout: afgekorte JSONNee: testbestand is afgekapt

Waargenomen resultaat: Geen enkel model voltooide de gevraagde respons met twee bestanden binnen de gedeelde uitvoerlimiet. Alle drie stopten bij 4.096 gerapporteerde voltooiingstokens. Elk model had al een volledige routerfunctie geretourneerd, die de twaalf vaste gedragsgroepen had doorlopen; de nieuwe testbestanden waren onvolledig en werden niet uitgevoerd.

We hebben uitsluitend de volledig gesloten broncodestrings eruit gehaald. De oorspronkelijke, ingekorte antwoorden blijven hieronder intact. Een geslaagde functiescore betekent daarom niet dat het volledige verzoek is geslaagd.

Extra bevinding bij de codereview: een gelijkspel met gelijke wortels

Na de vaste controles merkten we dat Haiku zich had genormaliseerd /// naar een lege tekenreeks in plaats van /. Bij een afzonderlijke lokale controle werden twee GET-regels gebruikt, met voorvoegsels /// en /, gericht op eerste en tweede in die volgorde. Voor /wat dan ook, het contract schrijft voor dat eerste.

Haiku is terug tweede; Sonnet en Opus zijn teruggekeerd eerste. Deze extra controle vloeide voort uit de codereview en staat los van de twaalf controles die voorafgaand aan het testen zijn vastgesteld. Het heeft geen invloed op de scores daarvan.

Sonnet kreeg één servicefout te verwerken voordat de toegestane herpoging het weergegeven, ingekorte antwoord opleverde. Die servicefout staat los van de kwaliteit van de code. Geen enkel model kreeg nog een kans om zijn antwoord te verbeteren of af te ronden.

Bekijk de volledige gedeelde prompt en de invoercode
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.
Bekijk het volledige antwoord van Claude Haiku 4.5 zoals het is teruggestuurd

Dit is het volledige antwoord dat we hebben ontvangen. Het stopt halverwege omdat de limiet is bereikt; de ontbrekende inhoud is niet aangevuld.

```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: 
Bekijk het volledige antwoord van Claude Sonnet 5 zoals het is teruggestuurd

Dit is het volledige antwoord dat we hebben ontvangen. Het stopt halverwege omdat de limiet is bereikt; de ontbrekende inhoud is niet aangevuld.

{"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),
Bekijk het volledige antwoord van Claude Opus 5.5 zoals het is teruggestuurd

Dit is het volledige antwoord dat we hebben ontvangen. Het stopt halverwege omdat de limiet is bereikt; de ontbrekende inhoud is niet aangevuld.

{"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

Het maximum van 4.096 tokens was het voor deze test gekozen outputbudget, en niet het gedocumenteerde maximum van een bepaald model. Het routingresultaat wijst erop dat het raadzaam is om, voordat je een grotere aanvraag indient, de omvang van de implementatie, de tests en de uitleg die je terugverwacht, in te schatten. Het kan zinvol zijn om de te leveren resultaten op te splitsen of meer uitvoerruimte toe te staan; we hebben deze uitvoer niet opnieuw uitgevoerd met andere instellingen.

Beoordeel voor je eigen opdracht drie zaken afzonderlijk: of het antwoord volledig is, of de code nuttige controles doorstaat en of er bij de beoordeling hiaten worden ontdekt die bij die controles over het hoofd zijn gezien. Het doorstaan van twaalf functiecontroles maakte geen van de onvolledige antwoorden met twee bestanden tot een voltooide oplevering.

Welke invloed heeft de modelkeuze op de kosten?

Claude Code is de coderingstool; Haiku, Sonnet, Opus en Fable zijn beschikbare modellen binnen de ondersteunde omgeving. De manier waarop je je aanmeldt en betaalt, is een andere keuze. Het wijzigen van het model zorgt er niet voor dat een abonnement wordt omgezet in een API-account of dat alle gebruikslimieten verdwijnen.

FactureringsrouteWat je kunt bekijkenHoe moet de modelkeuze worden geïnterpreteerd?
Toegang via een Claude-abonnementHet inbegrepen gebruik volgens het abonnement en eventuele afzonderlijk geactiveerde gebruikskredietenVerschillende modellen kunnen op verschillende manieren verbruiksquota verbruiken; bekijk de beschikbare modellen en limieten van het abonnement
Toegang tot de Anthropic-APIInput-/output-tokens, caching en eventuele API-kostenGebruik de huidige tarieflijst en het daadwerkelijke verbruik, en bevestig vervolgens de kosten in de factureringsconsole
Cloudprovider of gatewayDe door uw provider ondersteunde ID’s, tarieven en factuurgegevensEen voorbeeld van de catalogusprijs voor Anthropic komt mogelijk niet overeen met het bedrag dat uw provider in rekening brengt
Modelselectie en facturering hangen met elkaar samen, maar de factureringswijze bepaalt hoe het verbruik in rekening wordt gebracht.

In de Claude-code, /gebruik toont informatie over het sessietoken en, indien van toepassing, balkjes die het verbruik van het abonnement aangeven. Het bedrag in dollars is een lokaal berekende schatting op basis van lijsttarieven of geconfigureerde tarieven; het is geen factuur. Voor Pro- en Max-abonnees betekent dit sessiebedrag niet dat er een extra API-factuur aan het abonnement is toegevoegd. Lees meer over Claude: Prijsstelling en factureringsroutes als u nog aan het bedenken bent hoe u wilt betalen.

Huidige API-prijzen en een voorbeeld met gelijke tokens

De standaard basistarieven van Anthropic per miljoen input-/output-tokens bedragen Haiku 4.5: $1/$5; Sonnet 5: $2/$10; Opus 5.5: $4/$20; Fable 5.1: $10/$50, geraadpleegd op 28 september 2026. In de onderstaande grafiek wordt het tokenvolume constant gehouden, zodat de cijfers gemakkelijk te vergelijken zijn.

Hetzelfde tokenvolume, vier verschillende API-kosten

Voorbeeld: 100.000 niet-gecachete invoertokens + 10.000 uitvoertokens, op basis van de normale API-catalogusprijzen van Anthropic, gecontroleerd op 28 september 2026.

Haiku 4.5$0.15
Sonnet 5$0.30
Opus 5.5$0.60
Fable 5.1$1.50
Bekijk de tarieven en de berekening
USD per miljoen tokens en het rekenvoorbeeld
ModelIngang / 1MUitgang / 1MVoorbeeld totaal
Haiku 4.5$1$5$0.15
Sonnet 5$2$10$0.30
Opus 5.5$4$20$0.60
Fable 5.1$10$50$1.50

Berekening: 0,1 × invoersnelheid + 0,01 × uitvoersnelheid. Voor Opus 5.5: 0,1 × $4 + 0,01 × $20 = $0,60.

Dit is een illustratie met een vast aantal tokens. Daadwerkelijke taken verbruiken een verschillend totaal aantal tokens; schrijf- en leesbewerkingen in de cache, herpogingen, tools, facturering door de provider en abonnementslimieten moeten apart worden bijgehouden.

Bron: Prijzen voor de Anthropic API.

De tarieftabel van de API van Anthropic met tarieven voor invoer, uitvoer en prompt-cache.
De normale API-lijstprijzen worden per miljoen tokens aangegeven; voor het schrijven naar en lezen uit de cache gelden aparte tarieven.

Een echte Claude Code-taak bestaat uit een reeks verzoeken. Eerdere context kan opnieuw worden verzonden, een model kan verder onderzoek doen en tools of extra agents kunnen meer werk genereren. Bij het cachen van prompts gelden bovendien verschillende schrijf- en leessnelheden. De laagste balk in het voorbeeld geeft daarom niet de laagste kosten per geaccepteerde wijziging weer.

Vijf manieren om de kosten te beheersen zonder dat dit ten koste gaat van de kwaliteit van de opdracht

  1. Bepaal eerst wat je onder ‘acceptatie’ verstaat, voordat je een model kiest. Geef aan welk gedrag wordt verwacht, welke randgevallen er zijn en welke beperkingen er gelden. Een duidelijke, kleine taak is gemakkelijker op een kostenefficiënte manier uit te besteden en gemakkelijker te controleren.
  2. Geef relevante achtergrondinformatie. Voeg de bestanden en foutdetails toe die het model nodig heeft. Start voor niet-gerelateerde zaken een nieuwe conversatie, in plaats van een lange geschiedenis mee te nemen alleen maar omdat die bestaat.
  3. Maak onderscheid tussen bekwaamheid en inspanning. Anthropic raadt aan om te beginnen met de standaardinspanning van het model. Als het de juiste context heeft gelezen en het probleem toch niet kon oplossen, kan een krachtiger model wellicht helpen. Als het het lezen of de verificatie heeft overgeslagen, controleer dan de taakinstellingen en de keuze van de inspanning.
  4. Begroting voor het volledige antwoord. Broncode, tests en uitleg hebben allemaal ruimte nodig om weergegeven te worden. Onze tweede opdracht laat zien waarom een complete functie op zich nog geen bewijs is dat de gevraagde oplevering binnen het budget paste.
  5. Vergelijk goedgekeurd werk. Registreer nuttige output, het aantal herpogingen, de beoordelingstijd en de daadwerkelijke factuurgegevens voor representatieve taken. Maak een onderscheid tussen fouten van de dienstverlener en fouten die te wijten zijn aan de kwaliteit van het model.

Deze strategieën sluiten aan bij de officiële kostenoverzicht en De richtlijnen van Anthropic inzake modellen en inspanningen. Een wekelijkse of sessiegebonden limiet die voor het hele plan geldt, kan na een modelwisseling van kracht blijven; bij een modelspecifieke limiet ligt dat anders. Controleer de daadwerkelijke limiet voordat je herhaaldelijk van model wisselt.

Als je werk zowel code, onderzoek, schrijven als media omvat, vergelijk dan ook de workflow waarvoor je betaalt. De multi-model-werkruimte van GlobalGPT brengt die functies samen in één dashboard met CLI-toegang voor ontwikkelingstaken. Die bredere meerwaarde voor de workflow is nuttig om af te wegen tegen een API-route op basis van verbruik; de bovenstaande API-grafiek is geen prijslijst voor GlobalGPT-abonnementen.

Waarom het door u gekozen model misschien niet blijft zitten

SymptoomWaarschijnlijk controlerenPraktische volgende stap
In de volgende sessie wordt het oude model gebruiktDe schakelaar was mogelijk alleen voor deze sessie geldigGebruik de ‘Enter’-actie van de picker om op te slaan en start vervolgens een nieuwe sessie
De voorkeur wordt opgeslagen, maar er wordt een ander model gestartEen instelling voor ‘lancering’, ‘shell’, ‘project’, ‘beheerd’ of ‘organisatie’ kan voorrang hebbenLees de opstartinformatie door en controleer de betreffende configuratie samen met de eigenaar
Een hervat gesprek heeft een ander modelDe opgeslagen sessie kan het vorige model herstellenControleer /status; kies bewust het gewenste model voor de hervatting van het werk
Het model ontbreekt of is afgewezenBeschikbaarheid van zorgverleners, organisatieregels of een verouderde versie van de Claude-codeControleer de ondersteunde model-ID’s, de accounttoegang en de versievereisten
Dezelfde achternaam wordt elders anders uitgesprokenAliassen en providertoewijzingen verschillenGebruik de juiste volledige ID van de aanbieder wanneer een release vast moet blijven staan
Bij het wisselen van tarief wordt de gebruikslimiet niet gewistDe limiet kan van toepassing zijn op het gehele planLees de gebruiksaanwijzing en stel de resetperiode opnieuw in, in plaats van ervan uit te gaan dat het voldoende is om de schakelaar op ‘familie’ te zetten
Stel vast hoe groot het probleem is voordat u een andere instelling wijzigt.

Als de Claude-code uw gebruikersinstellingen niet kan opslaan, kan een interactieve keuze alleen voor de huidige sessie gelden, zonder dat deze wordt opgeslagen. Bij een beheerd of gegenereerd instellingenbestand is wellicht een wijziging door de eigenaar nodig, in plaats van een nieuwe poging om een keuze te maken. Vermijd het opheffen van organisatorische beperkingen louter om een optie zichtbaar te maken.

Creëer een bredere workflow met GlobalGPT

Modelselectie is een onderdeel van de workflow. Het kan zijn dat je onderzoek moet doen in een bibliotheek, een functie moet ontwerpen, een wijziging aan een teamgenoot moet uitleggen, documentatie moet opstellen en visuele elementen moet voorbereiden. GlobalGPT combineert meerdere toonaangevende modellen en AI-functies in één betaalbare werkruimte, zodat al deze taken in hetzelfde dashboard kunnen worden uitgevoerd.

Voor werkzaamheden aan de terminal geldt dat de GlobalGPT CLI koppelt de tools van het platform aan ontwikkelomgevingen. De gedocumenteerde installatieprocedure maakt gebruik van de inloggegevens van je GlobalGPT-account en je normale accountsaldo. De CLI biedt ondersteuning voor een bredere workflow op het gebied van coderen en ontwerpen, terwijl het eigen model van Claude Code een bewuste keuze blijft dankzij de modelinstellingen.

Stel bijvoorbeeld een implementatieplan op en evalueer dit, gebruik een ander model voor een beknopte uitleg en stel vervolgens de documentatie of hulpmiddelen op die bij de release horen. Volg de handleiding om GlobalGPT CLI koppelen aan Claude-code wanneer je die toolintegratie wilt. Stel je Claude-code-redeneringsmodel afzonderlijk in, afhankelijk van de codeertaak.

Kies een startmodel, leg vast aan de hand van welke criteria het succes wordt bepaald, en schakel over naar een hoger niveau wanneer de taak dat vereist. Controleer de volledige oplevering voordat je het werk als voltooid beschouwt. Stel je workflow samen met GlobalGPT wanneer je wilt dat modelkeuze, onderzoek, programmeren en creatieve tools naadloos in je dagelijkse werk zijn geïntegreerd.

FAQ

Hoe wijzig ik het model in Claude Code?

Voer /model uit om de keuzelijst te openen. Druk op s om alleen de huidige sessie te wijzigen, of op Enter om het geselecteerde model als standaard op te slaan. Om een model voor een enkele nieuwe sessie te kiezen, start je het programma met claude –model gevolgd door de modelnaam.

Verandert /model mijn opgeslagen standaardinstelling?

In de huidige interactieve versies wordt de keuze voor een model – door op Enter te drukken of /model sonnet in te typen – opgeslagen in de gebruikersinstellingen, mits deze bewerkbaar zijn. Het indrukken van de toets s in de keuzelijst geldt alleen voor de huidige sessie. Bij de volgende keer opstarten kunnen andere instellingen nog steeds voorrang krijgen.

Wat is het standaardmodel met de Claude-code?

De standaardinstelling voor de looptijd hangt af van je account, provider en configuratie. De standaardwaarde wist een modeloverschrijving; het is geen vaste model-ID. Controleer het actieve model met /status en bekijk de keuzelijst, in plaats van ervan uit te gaan dat elk account met dezelfde release begint.

Wat doet Opusplan?

Het maakt gebruik van Opus in de planningsmodus en van Sonnet tijdens de uitvoering. Het volgt de modusovergang in plaats van voor elke opdracht automatisch het goedkoopste model te kiezen. De werkelijke kosten zijn afhankelijk van het werk dat in elke fase wordt verricht.

Waarom ontbreekt er een model in de keuzelijst?

De beschikbaarheid kan afhangen van uw provider, account, beperkingen binnen uw organisatie en de versie van de Claude-code. Raadpleeg de documentatie van het huidige model en kijk welke modellen in uw omgeving zijn toegestaan. Een familie-alias kan bij een andere provider ook naar een andere release verwijzen.

Maakt een goedkoper model een taak altijd goedkoper?

Nee. De prijs van het token is slechts één factor. Het totale gebruik, onvolledige antwoorden, herpogingen en de inspanning die nodig is voor de beoordeling zijn allemaal van invloed op de kosten van een aanvaardbare wijziging. Bij onze kleine refactor zijn alle drie de functies geslaagd, terwijl alle drie de grotere reacties met twee bestanden werden afgekapt vanwege de gemeenschappelijke uitvoerlimiet.

Bronnen

Deel de post:

Verwante berichten