Selección de modelos de código Claude: valores por defecto, cambio de modelo y coste

Selección de modelos de código Claude: valores por defecto, cambio de modelo y coste

Para la selección de modelos según el código Claude, utiliza Haiku para cambios pequeños y comprobables, Sonnet para la implementación rutinaria y Opus para trabajos que requieran mayor ambigüedad o razonamiento. Actualmente, Anthropic recomienda el Opus 5.5 como punto de partida general; tu tarea, la carga de trabajo de revisión y tu presupuesto determinarán si un modelo más pequeño se adapta mejor a tus necesidades.

Para cambiar de modelo, abre /modelo. Prensa Entre en para guardar una configuración predeterminada, o s para cambiar únicamente la sesión actual. Para una sola ejecución, utiliza claude --modelo opus. Esas opciones tienen distintos niveles de persistencia, por lo que decidir cuánto tiempo debe durar el cambio es tan importante como elegir el modelo.

El trabajo de programación también implica investigación, explicaciones, documentación y recursos creativos. GlobalGPT reúne los principales modelos de IA y múltiples herramientas de IA en un único panel de control asequible, mientras que su interfaz de línea de comandos (CLI) conecta esas herramientas con la terminal y los flujos de trabajo de desarrollo. Crea tu flujo de trabajo con GlobalGPT para mantener agrupadas esas tareas relacionadas a medida que pasas de una idea a un código funcional.

¿Qué modelo debes utilizar en el código Claude?

Empieza por la naturaleza del trabajo. Una función breve con resultados esperados claros requiere una evaluación diferente a la de un error intermitente que afecta a varios servicios. Lo que conviene preguntarse es si el modelo puede generar un cambio aceptable con un proceso razonable de revisión e iteración.

Modelo actualPara qué está pensadoUna tarea práctica para empezar
Claude Haiku 4.5Trabajo rápido y eficaz con un contrato de alcance limitadoUna pequeña refactorización, una transformación repetitiva o un caso de prueba sencillo
Claude Soneto 5Un equilibrio entre inteligencia y rapidez para la programación diariaUna función con patrones establecidos, una corrección rutinaria de errores o una explicación del código
Claude Opus 5,5Razonamiento complejo y trabajo de codificación agencial más extensoUn fallo ambiguo, una disyuntiva arquitectónica o un cambio con restricciones que interactúan entre sí
Claude Fable 5.1Las investigaciones más difíciles y de mayor duraciónUna escalada en la que el trabajo difícil sigue superando a un mayor nivel de esfuerzo de Opus
Familias de modelos actuales y adecuación a las tareas, según la documentación de Anthropic; las sugerencias de tareas son puntos de partida prácticos.

Vale la pena considerar Haiku cuando los errores son fáciles de detectar y el cambio es pequeño. Sonnet es una opción sensata cuando la implementación diaria requiere más contexto y criterio. Opus merece tenerse en cuenta cuando la comprensión del problema constituye una parte sustancial de la tarea. Fable merece una decisión aparte para trabajos exigentes; quedó fuera de nuestra prueba de programación con los tres modelos.

Se trata de puntos de partida, no de garantías automáticas de calidad. Una respuesta más económica puede acabar resultando cara si requiere varias revisiones o una revisión exhaustiva. Por el contrario, un modelo más grande puede aportar poco a una transformación sencilla que ya supera tus comprobaciones. Para un uso más amplio más allá del código Claude, Comparar las familias de modelos Claude.

Comparativa actual de modelos de Anthropic con Fable 5.1, Opus 5.5, Sonnet 5 y Haiku 4.5.
Gama de modelos de Anthropic, consultada el 28 de septiembre de 2026. La disponibilidad en el código Claude depende de tu proveedor y de tu cuenta.

En Resumen oficial de los modelos Enumera ventanas de contexto de 1 millón de tokens para Fable 5.1, Opus 5.5 y Sonnet 5, y de 200 000 para Haiku 4.5. Se trata de especificaciones de la API. Tu proveedor de código Claude, tu cuenta y tu configuración determinan las opciones de contexto disponibles en una sesión. Una ventana grande resulta útil para el contexto necesario; sin embargo, llenarla con material no relacionado sigue suponiendo trabajo para el modelo.

Cómo cambiar de modelo en una sesión

En el marco de una sesión interactiva, se llevó a cabo /modelo para abrir el selector de modelos. Selecciona la fila del modelo y pulsa s si se trata de un cambio puntual. Al pulsar Entre en guarda la selección como predeterminada para tu usuario. Al escribir /soneto modelo Además, en las versiones actuales, lo guarda directamente.

Elige el comando en función de cuánto tiempo debe durar el cambio

Comportamiento documentado del código Claude, comprobado el 28 de septiembre de 2026
Tu objetivoQué hacer¿Se han guardado los cambios en la configuración predeterminada?
Activar solo esta sesión interactivaAbrir /modelo, selecciona un modelo, pulsa s.No.
Guardar un modelo favoritoAbrir /modelo y pulse Entre en, o escribe /soneto modelo.Sí, cuando se puede escribir en la configuración del usuario.
Iniciar una sesión individual en OpusLanzamiento claude --modelo opus.No.
Volver a la configuración predeterminada de tiempo de ejecución de la cuentaUtilice /modelo por defecto.Borra la personalización del modelo; el valor predeterminado establecido puede depender de la configuración de la cuenta y de la organización.
Comprueba el modelo activoCorrer /estado.No.

La configuración del proyecto o la configuración gestionada, las modificaciones del entorno y las sesiones reanudadas pueden afectar al siguiente inicio. Comprueba el modelo activo cuando la reproducibilidad sea importante.

Fuente: Configuración del modelo con código Claude.

En una sesión nueva del terminal, selecciona el modelo al iniciar. Estos comandos también son válidos cuando Claude Code se ejecuta en el terminal integrado de VS Code. En los ejemplos se selecciona una versión de forma explícita:

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

Ejecuta el comando correspondiente a la sesión que desees; los ejemplos son alternativas. Puedes abrir diferentes terminales con diferentes --modelo valores sin tener que modificar repetidamente un valor predeterminado guardado y compartido. Utiliza /estado para comprobar el modelo activo.

Apellidos como opus y soneto pueden migrarse a versiones más recientes. Su resolución también depende del proveedor: la API Anthropic actualmente las asigna a Opus 5.5 y Sonnet 5, mientras que las asignaciones de los proveedores de servicios en la nube pueden variar. Utiliza un identificador de modelo completo cuando necesites una ejecución reproducible y comprueba el nombre específico del proveedor si tu entorno lo requiere.

Opus 5.5 requiere el código Claude v2.1.280 o posterior; Sonnet 5 requiere la versión v2.1.197 o posterior. El documentación sobre la configuración del modelo Es la referencia para los alias actuales, los requisitos de versión y las asignaciones de proveedores. Los comandos que aparecen aquí son ejemplos de la documentación; en nuestra sección práctica se evalúa por separado el código generado.

Documentación sobre el código Claude en la que se explica la selección de modelos y el uso de las teclas «Enter» y «s».
Al pulsar «Intro» se guarda el modelo como predeterminado; al pulsar «s» solo se modifica la sesión actual.

Cómo establecer un modelo predeterminado

Para una configuración predeterminada personal, selecciona un modelo en /modelo y pulse Entre en, o escribe una selección como, por ejemplo: /soneto modelo. El código Claude escribe el modelo campo de tu configuración de usuario cuando ese archivo sea modificable. Abre una nueva sesión y comprueba su modelo activo para confirmar el resultado.

Una preferencia guardada no anula el resto de ajustes. Una opción de inicio, MODELO_ANTROPICO En el shell, en la configuración del proyecto o gestionada, o si la configuración predeterminada de una organización está configurada para anular las opciones del usuario, se puede seleccionar otra opción al iniciar el programa. Una sesión reanudada también puede conservar el modelo de su transcripción guardada. Comprueba la información de inicio y /estado antes de dar por hecho que la oportunidad se había perdido.

/modelo por defecto borra la personalización de tu modelo y vuelve a los valores predeterminados de tiempo de ejecución correspondientes a tu cuenta y configuración. La palabra por defecto Es un valor de reinicio, no el nombre de un modelo fijo Claude. No debe utilizarse como identificador reproducible del modelo.

Para una configuración avanzada, MODELO_ANTROPICO_POR_DEFECTO Ofrece una opción inicial alternativa a partir de la versión v2.1.236. Aparece debajo de las selecciones explícitas de inicio, entorno, configuración y organización, e ignora valores como haiku y opusplan. En el caso de una preferencia personal normal, el selector es más fácil de comprobar. Véase Configuración del código Claude cuando se trata de una configuración compartida.

Qué hace realmente Opusplan

opusplan Utiliza Opus en la fase de planificación y Sonnet para la ejecución. El atractivo es evidente: se utiliza un modelo de razonamiento más sólido para desarrollar el enfoque y, a continuación, un modelo de programación cotidiano para llevar a cabo ese plan.

Opusplan sigue este modo de trabajo

Modo de planificaciónOpusAnaliza el enfoque paso a paso.
EjecuciónSonetoPon en práctica el plan.

Comportamiento de enrutamiento documentado. Las versiones que se esconden tras estos nombres de familia dependen de tu proveedor y de tu configuración.

Fuente: Configuración del modelo con código Claude

Esta es una regla que depende del modo. No analiza cada solicitud ni elige automáticamente la familia adecuada más económica. Las asignaciones de proveedores y las restricciones de acceso siguen aplicándose a los modelos subyacentes. El nombre tampoco garantiza un ahorro: el coste total depende del nivel de planificación, implementación, revisión y procesamiento del contexto que requiera la tarea.

Piénsalo, para variar, en un caso en el que el diseño se beneficie de un análisis más profundo y la implementación siga un plan claro. Para una pequeña corrección, esa planificación adicional puede resultar innecesaria.

¿Qué revelaron las dos tareas de programación?

Para ofrecer una visión concreta de la selección de modelos, probamos una refactorización limitada de Unicode y una corrección del selector de rutas con requisitos que interactuaban entre sí. La distinción entre una función correcta y una entrega completa según lo solicitado resultó ser más importante que una simple puntuación.

Cómo evaluamos los resultados de la programación

El 28 de septiembre de 2026, asignamos a Claude Haiku 4,5; a Claude Sonnet 5; y a Claude Opus 5,5 las mismas dos tareas de JavaScript a través de GlobalGPT. Cada modelo devolvió código fuente; inspeccionamos el código y ejecutamos comprobaciones locales fijas. Las instrucciones de cambio de modelo de Claude que aparecen en otras secciones de esta guía proceden de su documentación oficial.

Cada solicitud tenía un límite de salida de 4.096 tokens, sin herramientas ni historial de conversación, y con la configuración predeterminada del modelo. Conservamos la primera respuesta evaluable, incluidas las respuestas incompletas. Se permitió un único reintento tras un error del servicio en la tarea de enrutamiento de Sonnet: siete solicitudes generaron seis resultados.

Estas dos pequeñas tareas miden el código devuelto y la integridad de la entrega. No miden la ejecución de los agentes de Claude Code, la exploración del repositorio ni una sesión de desarrollo completa. No se han facilitado los costes reales en dólares. Una única observación por tarea no permite establecer una clasificación general en cuanto a velocidad o fiabilidad.

Ver los ID exactos de los modelos y las condiciones de evaluación

Documentos de identidad solicitados: claude-haiku-4-5-20251001, claude-soneto-5, y claude-opus-5-5. Las comprobaciones locales se realizaron con Node.js v24.19.0. Las instrucciones están pensadas para Node.js 18 o superior; Node.js 18 en sí no se ha probado.

Se establecieron ocho grupos de comportamiento para la tarea de la babosa y doce para el selector de rutas. El cumplimiento del formato y la entrega completa se indican por separado. Una comprobación posterior de revisión del código se indica por separado y no modifica esos recuentos. No modificamos el código fuente devuelto para que superara la comprobación.

Tarea 1: Mantener el Unicode en una pequeña refactorización del slug

La función debía conservar las letras no latinas y los signos combinados, normalizar los acentos descompuestos y mantener su comportamiento ASCII actual.

Primeras salidas devueltas, con comprobaciones de comportamiento local corregidas
ModeloControles de comportamientoFormato de respuestaRealizar la entrega solicitada
Claude Haiku 4.58/8Error: etiqueta de MarkdownSí: función completa
Claude Soneto 58/8Datos: JSON sin procesarSí: función completa
Claude Opus 5,58/8Datos: JSON sin procesarSí: función completa

Resultado observado: Las tres funciones procesaron los mismos ocho grupos. Sonnet y Opus también cumplieron con el requisito de respuesta en formato JSON sin procesar. Haiku envolvió su respuesta en Markdown, y en su nota de revisión describía erróneamente la NFC como una descomposición de caracteres; sin embargo, su código aplicaba correctamente la NFC.

En esta refactorización específica, la actualización no mejoró el comportamiento de la función sometida a prueba. Aún era necesario revisar los requisitos de formato y la precisión de la explicación adjunta.

Ver el mensaje completo compartido y el código de entrada
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.
Ver la respuesta completa de Claude Haiku 4.5 tal y como se ha enviado
```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"
  ]
}
```
Ver la respuesta completa de Claude Sonnet 5 tal y como se ha recibido
{"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."]}
Ver la respuesta completa de Claude Opus 5.5 tal y como se ha enviado
{"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."]}

Tarea 2: Reparar un selector de rutas y realizar las pruebas correspondientes

La función tenía que resolver coincidencias de prefijos más largos, la precedencia de métodos, los límites de segmentos, las reglas con formato incorrecto y las entradas inmutables. La respuesta solicitada también incluía un nuevo archivo de prueba.

Primeras salidas devueltas, con comprobaciones de comportamiento local corregidas
ModeloControles de comportamientoFormato de respuestaRealizar la entrega solicitada
Claude Haiku 4.512/12 solo en la función completaError: JSON truncadoNo: el archivo de prueba está truncado
Claude Soneto 512/12 solo en la función completaError: JSON truncadoNo: el archivo de prueba está truncado
Claude Opus 5,512/12 solo en la función completaError: JSON truncadoNo: el archivo de prueba está truncado

Resultado observado: Ningún modelo completó la respuesta solicitada de dos archivos dentro del límite de salida compartido. Los tres se detuvieron en 4.096 tokens de finalización notificados. Cada uno de ellos ya había devuelto una función de enrutador completa, que superó los doce grupos de comportamiento fijos; los nuevos archivos de prueba estaban incompletos y no se ejecutaron.

Solo hemos extraído las cadenas de código fuente que estaban completamente cerradas. Las respuestas truncadas originales se mantienen intactas a continuación. Por lo tanto, una puntuación satisfactoria en la función no significa que la solicitud completa se haya realizado con éxito.

Hallazgo adicional de la revisión del código: un empate por tener la misma raíz

Tras las comprobaciones rutinarias, observamos que Haiku se había normalizado /// a una cadena vacía en lugar de /. Una comprobación local independiente utilizaba dos reglas GET, con los prefijos /// y /, dirigido a primero y segundo en ese orden. Para /cualquier cosa, el contrato establece que primero.

Haiku ha vuelto segundo; Sonnet y Opus han vuelto primero. Esta comprobación adicional se realizó a raíz de una revisión del código y es independiente de las doce comprobaciones establecidas antes de las pruebas. No afecta a sus puntuaciones.

Sonnet detectó un error de servicio antes de que su reintento permitido devolviera la respuesta truncada que se muestra. Ese fallo del servicio es independiente de la calidad del código. Ningún modelo recibió otra oportunidad para mejorar o completar su respuesta.

Ver el mensaje completo compartido y el código de entrada
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.
Ver la respuesta completa de Claude Haiku 4.5 tal y como se ha enviado

Esta es la respuesta completa que se ha recibido. Termina a mitad de la salida porque ha alcanzado el límite; el contenido que falta no se ha completado.

```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: 
Ver la respuesta completa de Claude Sonnet 5 tal y como se ha recibido

Esta es la respuesta completa que se ha recibido. Termina a mitad de la salida porque ha alcanzado el límite; el contenido que falta no se ha completado.

{"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),
Ver la respuesta completa de Claude Opus 5.5 tal y como se ha enviado

Esta es la respuesta completa que se ha recibido. Termina a mitad de la salida porque ha alcanzado el límite; el contenido que falta no se ha completado.

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

El límite máximo de 4.096 tokens fue el presupuesto de salida elegido para esta prueba, no el máximo documentado de ningún modelo. El resultado del enrutamiento sugiere realizar una comprobación práctica antes de enviar una solicitud más grande: calcular el tamaño de la implementación, las pruebas y la explicación que esperas recibir. Puede ser conveniente dividir los resultados o dejar más margen de salida; no volvimos a ejecutar estas salidas con configuraciones diferentes.

Para tu propia tarea, evalúa tres aspectos por separado: si la respuesta está completa, si el código supera las comprobaciones pertinentes y si la revisión detecta lagunas que esas comprobaciones no hayan detectado. El hecho de superar doce comprobaciones de funciones no convertía a ninguna de las respuestas truncadas de dos archivos en una entrega completa.

¿Qué cambios en la elección del modelo afectan al coste?

Claude Code es la herramienta de codificación; Haiku, Sonnet, Opus y Fable son las opciones de modelo disponibles dentro del entorno compatible. La forma de autenticarse y pagar es otra opción. Cambiar de modelo no convierte una suscripción en una cuenta API ni hace que desaparezcan todos los límites de uso.

Ruta de facturaciónQué verCómo interpretar la elección del modelo
Acceso mediante suscripción a ClaudeEl uso incluido en el plan y cualquier crédito de uso activado por separadoLos distintos modelos pueden consumir los derechos de emisión de forma diferente; consulta los modelos disponibles y los límites del plan.
Acceso a la API AnthropicTokens de entrada/salida, almacenamiento en caché y tarifas de API aplicablesUtiliza la tarifa vigente y el consumo real, y a continuación confirma los cargos en la consola de facturación.
Proveedor de servicios en la nube o pasarelaIdentificadores, tarifas y registros de facturación compatibles con tu proveedorEs posible que un ejemplo de precio de catálogo Anthropic no coincida con el importe que te cobra tu proveedor.
La selección del modelo y la facturación están relacionadas, pero la modalidad de facturación determina cómo se cobra el consumo.

En el código Claude, /uso Muestra información sobre el token de sesión y, cuando procede, las barras de uso de la suscripción. La cifra en dólares es una estimación calculada localmente a partir de las tarifas de la lista o de las configuradas; no se trata de una factura. Para los suscriptores de los planes Pro y Max, esa cifra de la sesión no significa que se haya añadido un cargo adicional por el uso de la API a la suscripción. Más información sobre Claude: Rutas de fijación de precios y facturación si estás pensando en cómo pagar.

Precios actuales de la API y un ejemplo de «equal-token»

Las tarifas base normales de Anthropic por cada millón de tokens de entrada/salida son Haiku 4.5: $1/$5; Sonnet 5: $2/$10; Opus 5.5: $4/$20; Fable 5.1: $10/$50, consultado el 28 de septiembre de 2026. En el siguiente gráfico se mantiene constante el volumen de tokens para facilitar la comparación de las cifras.

El mismo volumen de tokens, cuatro costes de API diferentes

Ejemplo: 100 000 tokens de entrada no almacenados en caché + 10 000 tokens de salida, utilizando los precios de catálogo habituales de la API de Anthropic consultados el 28 de septiembre de 2026.

Haiku 4,5$0.15
Soneto 5$0.30
Opus 5.5$0.60
Fable 5.1$1.50
Consulta las tarifas y el cálculo
USD por cada millón de tokens y el ejemplo de cálculo
ModeloEntrada / 1MSalida / 1MTotal del ejemplo
Haiku 4,5$1$5$0.15
Soneto 5$2$10$0.30
Opus 5.5$4$20$0.60
Fable 5.1$10$50$1.50

Cálculo: 0,1 × tasa de entrada + 0,01 × tasa de salida. Para Opus 5.5: 0,1 × $4 + 0,01 × $20 = $0,60.

Esta es una ilustración basada en un número fijo de tokens. Las tareas reales consumen un número total de tokens diferente; las operaciones de lectura y escritura en la caché, los reintentos, las herramientas, la facturación del proveedor y los límites de suscripción deben contabilizarse por separado.

Fuente: Precios de la API Anthropic.

Tabla de precios de la API de Anthropic con tarifas de entrada, salida y caché de indicaciones.
Los precios de catálogo normales de la API se indican por millón de tokens; las operaciones de lectura y escritura en caché tienen tarifas distintas.

Una tarea real del código Claude consiste en una secuencia de solicitudes. Es posible que se vuelva a enviar el contexto anterior, que un modelo profundice en el análisis o que algunas herramientas o agentes adicionales generen más trabajo. El almacenamiento en caché de las indicaciones también presenta tasas de escritura y lectura distintas. Por lo tanto, la barra más baja del ejemplo no representa el coste más bajo por cambio aceptado.

Cinco formas de controlar los costes sin mermar la calidad del trabajo

  1. Define el concepto de «aceptación» antes de elegir un modelo. Indica el comportamiento esperado, los casos extremos y las limitaciones existentes. Una tarea breve y clara es más fácil de delegar de forma rentable y más fácil de revisar.
  2. Proporciona el contexto pertinente. Incluye los archivos y los detalles del error que necesita el modelo. Para asuntos no relacionados, inicia una conversación nueva en lugar de seguir con un historial extenso simplemente porque ya existe.
  3. Hay que diferenciar entre la capacidad y el esfuerzo. Anthropic recomienda empezar con el nivel de esfuerzo predeterminado del modelo. Si ha interpretado correctamente el contexto y aun así no ha podido resolver el problema, podría ser útil utilizar un modelo más potente. Si se ha saltado la lectura o la verificación, comprueba la configuración de la tarea y la opción de esfuerzo seleccionada.
  4. Presupuesto para toda la respuesta. El código fuente, las pruebas y las explicaciones necesitan espacio para mostrarse. Nuestra segunda tarea demuestra por qué una función completa por sí sola no es prueba suficiente de que la entrega solicitada se ajusta al presupuesto.
  5. Compara los trabajos aceptados. Registra los resultados útiles, los reintentos, el tiempo de revisión y los datos de facturación reales en tareas representativas. Mantén separados los errores de los proveedores de los fallos relacionados con la calidad del modelo.

Estas estrategias están en consonancia con la guía oficial de precios y Orientaciones sobre modelos y esfuerzo de Anthropic. Un límite semanal o por sesión aplicable a todo el plan puede seguir vigente tras un cambio de modelo; un límite específico de un modelo es un caso distinto. Comprueba el límite real antes de realizar cambios repetidos.

Si tu trabajo abarca la programación, la investigación, la redacción y los medios de comunicación, compara también el flujo de trabajo por el que estás pagando. El espacio de trabajo multimodelo de GlobalGPT reúne esas funciones en un único panel de control con acceso mediante la interfaz de línea de comandos (CLI) para tareas de desarrollo. Es conveniente sopesar ese valor añadido del flujo de trabajo junto con una ruta de API con tarificación por uso; el gráfico de la API anterior no es una lista de precios de la suscripción GlobalGPT.

Por qué es posible que el modelo que has elegido no se mantenga

SíntomaProbable comprobaciónPróximo paso práctico
En la siguiente sesión se utiliza el modelo antiguoEs posible que el cambio se haya aplicado únicamente a la sesión.Utiliza la acción «Intro» del selector para guardar y, a continuación, selecciona una nueva sesión
La preferencia se guarda, pero se inicia un modelo diferentePuede tener prioridad una configuración de lanzamiento, de shell, de proyecto, de gestión u organización.Lee la información de inicio e inspecciona la configuración correspondiente junto con su responsable.
Una conversación reanudada sigue un modelo diferenteLa sesión guardada puede recuperar su estado anteriorComprueba /status; elige con cuidado el modelo adecuado para reanudar el trabajo
El modelo no existe o ha sido rechazadoDisponibilidad de los proveedores, normas de la organización o una versión antigua del código ClaudeComprueba los ID de los modelos compatibles, el acceso a la cuenta y los requisitos de versión
El mismo apellido se escribe de forma diferente en otros lugaresLos alias y las asignaciones de proveedores difierenUtiliza el identificador completo correcto del proveedor cuando sea necesario que una versión se mantenga fija
El cambio de plan no borra el límite de usoEl límite puede aplicarse a todo el planLee el mensaje de instrucciones y restablece el periodo, en lugar de dar por sentado que basta con cambiar el interruptor familiar.
Determina el alcance del problema antes de modificar cualquier otro ajuste.

Si el código Claude no puede guardar tu configuración de usuario, una opción interactiva puede mantenerse durante la sesión actual sin que se guarde. Es posible que un archivo de configuración gestionado o generado requiera que su propietario realice los cambios necesarios, en lugar de volver a intentar seleccionarlo. Evita eliminar las restricciones de la organización simplemente para que aparezca una opción.

Crea un flujo de trabajo más amplio con GlobalGPT

La selección de modelos es solo una parte del flujo de trabajo. Es posible que tengas que investigar una biblioteca, redactar una función, explicar un cambio a un compañero de equipo, crear documentación y preparar recursos visuales. GlobalGPT combina varios modelos punteros y funciones de IA en un único espacio de trabajo asequible, de modo que todas esas tareas pueden realizarse desde el mismo panel de control.

Para trabajar en la terminal, el GlobalGPT CLI Conecta las herramientas de la plataforma con los entornos de desarrollo. Su configuración documentada utiliza los datos de inicio de sesión de tu cuenta GlobalGPT y el saldo habitual de la cuenta. La CLI permite un flujo de trabajo más amplio de programación y creación, mientras que el modelo propio de Claude Code sigue siendo una elección deliberada gracias a sus controles de modelo.

Por ejemplo, redacta y revisa un plan de implementación, utiliza otro modelo para ofrecer una explicación concisa y, a continuación, elabora la documentación o los recursos que acompañan al lanzamiento. Sigue la guía para Conectar la CLI del GlobalGPT al código del Claude cuando desees integrar esa herramienta. Configura tu modelo de razonamiento de código Claude por separado, según la tarea de codificación.

Elige un modelo inicial, anota los criterios que definen el éxito y pasa al siguiente nivel cuando la tarea lo requiera. Revisa el resultado final antes de dar el trabajo por terminado. Crea tu flujo de trabajo con GlobalGPT cuando quieras disponer de herramientas de selección de modelos, investigación, programación y creatividad integradas en tu trabajo diario.

PREGUNTAS FRECUENTES

¿Cómo se cambia el modelo en el código Claude?

Ejecuta /model para abrir el selector. Pulsa «s» para cambiar solo la sesión actual, o «Intro» para guardar el modelo seleccionado como predeterminado. Para elegir un modelo para una sola sesión nueva, ejecútalo con claude –model seguido del nombre del modelo.

¿El modelo cambia mi configuración predeterminada guardada?

En las versiones interactivas actuales, al seleccionar un modelo con la tecla Intro o al escribir /model sonnet, la elección se guarda en la configuración del usuario si esta es editable. Al pulsar «s» en el selector, el cambio solo es válido para la sesión actual. Es posible que otros ajustes tengan prioridad en el siguiente inicio.

¿Cuál es el modelo predeterminado del código Claude?

El valor predeterminado en tiempo de ejecución depende de tu cuenta, tu proveedor y tu configuración. El valor predeterminado borra cualquier sustitución del modelo; no se trata de un identificador de modelo fijo. Comprueba el modelo activo con /status y revisa el selector, en lugar de dar por hecho que todas las cuentas comienzan con la misma versión.

¿Qué hace Opusplan?

Utiliza Opus en el modo de planificación y Sonnet durante la ejecución. Se rige por la transición entre modos, en lugar de elegir automáticamente el modelo más económico para cada solicitud. El coste real depende del trabajo realizado en cada fase.

¿Por qué falta un modelo en el selector?

La disponibilidad puede depender de tu proveedor, tu cuenta, las restricciones de tu organización y la versión del código Claude. Consulta la documentación del modelo actual y los modelos permitidos en tu entorno. Un alias de familia también puede corresponder a una versión diferente en otro proveedor.

¿Un modelo más barato siempre hace que una tarea resulte más barata?

No. El precio del token es solo un factor. El uso total, las respuestas incompletas, los reintentos y el esfuerzo de revisión influyen en el coste de un cambio aceptable. En nuestra pequeña refactorización, las tres funciones superaron la prueba, mientras que las tres respuestas más extensas, de dos archivos, se truncaron al alcanzar el límite de salida compartido.

Fuentes

Comparte el post:

Entradas relacionadas