Modelos y providers¶
¿Decidir en vez de configurar?
Esta página es la maquinaria. Elegir un modelo responde a qué modelo debería usar un agent — pesos abiertos o cerrados, qué es lo que de verdad dispara la factura, y por qué la elección es reversible.
La plantilla de la que creció esta plataforma construía un modelo a partir de variables de entorno.
Eso deja de funcionar en cuanto varias organizaciones comparten un despliegue: cada una necesita su propia clave, su propio valor por defecto y poder rotar cualquiera de los dos sin volver a desplegar.
Así que un modelo se construye por run, a partir de la base de datos:
Nada sobre qué modelo usa un agent vive en .env.
Un perfil de modelo¶
Una fila dentro de una organización: una etiqueta, un provider, un id de modelo, ajustes por defecto y con qué secreto del vault autenticarse.
El spec de un agent nombra uno con model_profile_id, y el run lo resuelve.
El id de modelo es texto libre a propósito
Hay un selector que ayuda, pero el campo acepta cualquier cosa que escribas.
Un provider publica algo la mañana siguiente a que se calentara cualquier lista de aquí, y un campo que no puede expresar «ese» es un campo que la gente esquiva editando el spec a mano.
Un perfil en el que un agent no puede ejecutarse¶
Un perfil puede nombrar un modelo que no mantiene una conversación: un modelo de
texto a voz como gpt-4o-mini-tts, un modelo de voz en tiempo real, un modelo de
transcripción, un modelo de
imágenes, un modelo de vídeo o un modelo de embeddings. Un agent en uno de ellos
falla en su primer turno. El texto a voz, la generación de imágenes y la
transcripción no usan perfiles: cada función nombra su propio provider y su propio
modelo.
La plataforma conoce estos modelos por sus propios catálogos (las listas de texto
a voz, voz en tiempo real, transcripción, imágenes, vídeo y embeddings).
GET /providers/model-profiles marca cada perfil con chat_capable y
output_modalities, y GET /providers/{provider}/models marca cada sugerencia
con chat_capable. Esos dos campos también leen el listado de un provider que
indica qué emite cada modelo (OpenRouter y el router de Hugging Face lo hacen),
cuando el proceso de la API ya lo ha obtenido. Ese listado solo cambia lo que
ofrece un selector. Nunca rechaza una publicación, porque otro proceso de la API
puede no haberlo obtenido.
La consola también actualiza los perfiles guardados
cuando el listado llega después. Los alias de provider como openai_codex y
google_cloud usan los catálogos del proveedor correspondiente. Entonces:
- El panel de modelo del Builder, los selectores de revisor y de especialista, los campos del modelo de los pasos siguientes y del modelo de pasos del navegador y el selector del modelo que describe imágenes en la base de conocimiento no ofrecen un modelo así. Si un agent ya nombra uno, el Builder lo sigue mostrando, marcado como un modelo que no es de chat. A un miembro que gestiona conexiones, el Builder también le lista los demás, marcados y sin poder elegirse, para que se puedan eliminar.
- El selector de modelo del chat y el formulario para añadir un modelo del Builder no sugieren un modelo así y rechazan uno escrito a mano.
- La publicación rechaza un modelo así como modelo del agent, de un revisor o de un especialista, como modelo de pasos del navegador o como modelo de los pasos siguientes, y lo nombra. También rechaza un perfil cuyos fallbacks incluyen un modelo así. La misma comprobación se ejecuta al volver a una versión, en la comprobación del borrador, en la prueba del Builder y al activar una capability desde el chat. Una versión guardada en un modelo así sigue cargándose, pero no se puede restaurar mientras su perfil nombre un modelo que no es de chat o lo liste como fallback. Apunta ese perfil a un modelo de chat o publica una versión nueva.
- Un run deja un fallback así fuera de su cadena, igual que uno eliminado, cuando se añadió al perfil después de publicar.
- Una conversación que nombra un modelo así en lugar del modelo del agent, desde el chat o a través de la API, se rechaza antes de que empiece el run.
- Un modelo que los catálogos no clasifican, como un modelo de Ollama o un modelo
publicado esta mañana, está permitido. Un modelo desconocido no es un rechazo.
Un perfil con su propio endpoint (
base_url) nunca se clasifica, sea cual sea el id del modelo: los catálogos describen la API propia del provider, y un servidor compatible con OpenAI puede servir un modelo de chat con cualquier nombre.
El selector de modelo de proactividad sigue la misma regla. La comprobación de los catálogos estables también se aplica al configurar la proactividad y al resolver un perfil para una petición de texto normal, incluida la descripción de imágenes al configurar y en el worker. Las configuraciones antiguas siguen cargándose; hay que sustituir un modelo incompatible antes de usarlas. Sigue siendo posible desactivar la función. Los nombres de despliegues de Azure no se clasifican a partir de los identificadores de modelos base del catálogo.
Cambiar la clave o el endpoint de un perfil¶
Los agents nombran un perfil por su id. Para mover un modelo a otra clave, otro
endpoint u otra API de OpenAI, edita el perfil en su sitio:
PATCH /providers/model-profiles/{id} cambia secret_id, base_url, api,
params o label y conserva el id. Cada agent sobre el perfil usa el cambio
desde su siguiente run. Nadie tiene que volver a publicar nada.
En el panel de modelo del Builder, cambia la clave, el endpoint o la API del modelo en uso. El botón pasa a decir Update model, y el panel indica que cada agent sobre este modelo cambia con él. Add model sigue siendo el botón para otro provider u otro modelo, y para un modelo al que das un nombre propio. Usa Name it something else para dar solo a este agent un modelo aparte.
Una edición sigue las mismas reglas que la creación y necesita el mismo permiso
connections:manage. Una clave nueva debe estar en el vault de esta
organización, ser visible para ti y estar guardada para el provider del perfil.
Un endpoint nuevo comprueba la clave guardada del mismo modo, porque el endpoint
es adonde se envía la clave. Un provider que necesita clave no puede perderla. Un
provider keyless sin clave conserva un endpoint. La etiqueta debe seguir siendo
única.
Si cambias el endpoint y no envías api, una API que solo era la
predeterminada del endpoint anterior pasa a la predeterminada del nuevo, y una
API elegida a propósito se mantiene. El registro de auditoría guarda
model_profile.updated con los nombres de los campos cambiados, no sus valores.
No puedes cambiar el provider ni el id del modelo. Otro modelo es otro perfil.
connections:manage es, por tanto, la autoridad para mover a cada agent sobre un
modelo, incluidos los agents que no puedes editar: un endpoint nuevo recibe sus
peticiones desde el siguiente run. Concédelo a quienes gestionan los modelos de
la organización.
Un perfil sin clave¶
Si borras una clave del vault, sus perfiles se quedan con secret_id a null.
GET /providers/model-profiles los marca con missing_key: true. Un perfil
guardado sin clave a propósito es otra cosa: un modelo autoalojado o un gateway
que no autentica nada, en un provider keyless con endpoint, como un Ollama en tu
red o un servidor compatible con OpenAI. Lo registra como runs_without_key y
sigue ejecutándose. Un perfil de gateway que tenía clave y la perdió se marca con
missing_key como cualquier otro. Entonces:
- Los selectores de modelo del agent y del chat no lo ofrecen. Si un agent ya lo nombra, el Builder lo muestra con la marca no key y te pide que le des una clave.
- La publicación lo rechaza y nombra el modelo. También rechaza un perfil cuyos tres primeros fallbacks incluyen uno sin clave, porque un run lo saltaría. Un run sobre él falla con "no key configured".
- Para repararlo, abre un agent sobre ese modelo, elige una clave en Model y pulsa Update model. Cada agent sobre el modelo vuelve a funcionar.
Borrar un perfil¶
DELETE /providers/model-profiles/{id} rechaza un perfil que usan agents. La
respuesta es 409 IN_USE, y details.agents lista cada agent que puedes ver, con
id y nombre. details.hidden_agents cuenta los agents que no puedes ver. Un
perfil está en uso cuando el borrador de un agent, su versión servida o una
versión que fija un entorno nombra el perfil en cualquier lugar: como modelo del
agent, de un revisor o de un especialista, o en una capability, como el modelo de
follow-ups o el modelo de los pasos del navegador.
Los agents archivados cuentan,
porque desarchivarlos restaura la versión que servían. El rechazo los marca con
archived: un agent archivado no puede publicar, así que bórralo, o
restáuralo y muévelo. Las versiones antiguas que nada sirve no cuentan. Mueve
primero los demás agents a otro modelo. Para cambiar solo la clave, edita el perfil.
Fallbacks¶
El runner usa solo los tres primeros fallbacks; solo esos se comprueban al publicar.
Un perfil puede listar perfiles de fallback, probados en orden. La caída de un provider no debería tumbar los agents de una organización cuando esta tiene una segunda clave o un segundo provider configurados.
Un fallback es invisible en el registro del run
La fila del run se escribe antes de la primera petición, y lleva la etiqueta, el provider y el id de secreto del perfil primario. Si un fallback atendió el turno, el run sigue nombrando al primario.
Así que «cuánto gastamos en OpenAI» y «qué clave está costando más» responden con el perfil al que se preguntó primero, no con el que respondió. Conviene saberlo antes de fiarte de esos números durante una caída.
Ajustes del modelo¶
Por perfil, y sobrescribibles por agent a través de model_settings del spec:
temperature, top_p, max_tokens, parallel_tool_calls, timeout. Ver
la referencia del spec.
El caché de prompts está activado por defecto, mediante el ajuste cache de
Pydantic AI, neutral respecto al proveedor. Cada petición de un run vuelve a enviar
las instrucciones, los esquemas de herramientas y todo el historial, así que el
prefijo estable y la conversación se marcan como puntos de caché y cada petición
lee la anterior desde la caché. Se aplica donde un proveedor admite caché
explícita - Anthropic, los modelos Anthropic y Nova de Bedrock, los modelos
Anthropic y Gemini de OpenRouter, OpenAI desde GPT-5.6 - y no cambia nada en uno
que cachea por su cuenta, como Google. Un perfil lo apaga con cache: false, o
cachea solo instrucciones y herramientas con cache: {"messages": false}. Un
indicador propio de un proveedor que el perfil ya fija, como
anthropic_cache_messages, sigue mandando para ese perfil.
El esfuerzo de razonamiento no está aquí. Es
la capability thinking, porque «razona
más» es una decisión sobre para qué sirve el agent y no un mando de una
conexión — y porque un spec que lo fija como ajuste del modelo deja de ser
portable al cambiar de modelo.
Responses o Chat Completions¶
OpenAI publica dos APIs para los mismos modelos, y un perfil en openai o
azure guarda a cuál de ellas van sus peticiones: responses o chat. Cualquier
otro provider sirve una sola API y aquí no guarda nada.
Ninguna es la correcta en todas partes. Los modelos más nuevos de OpenAI solo se sirven por Responses y a Chat Completions responden con un 400, mientras que la mayoría de los servidores compatibles con OpenAI - vLLM, LM Studio, un proxy de LiteLLM - solo implementan Chat Completions. Por eso la elección es un campo del perfil, en Agents → add a model → API, y el formulario empieza en el valor por defecto:
| Perfil | Por defecto |
|---|---|
openai, sin endpoint (la API propia de OpenAI) |
responses |
openai con endpoint |
chat |
azure |
chat, porque la Responses API necesita una api_version reciente y el secreto puede fijar una anterior |
El valor por defecto es solo donde empieza el formulario. Un endpoint regional de
OpenAI como https://eu.api.openai.com/v1 es un endpoint que sirve Responses, así
que elígela ahí. Una elección omitida se guarda como su valor por defecto, así que
cambiar el valor por defecto más adelante no mueve un perfil existente.
Un perfil en Responses no deja conversaciones en OpenAI. La Responses API
guarda cada respuesta del lado de OpenAI salvo que se le indique lo contrario,
mientras que Chat Completions no guarda nada por defecto, así que un modelo en
Responses envía store: false. El razonamiento pasa entre turnos como contenido
cifrado y no mediante un id de respuesta guardado, así que nada necesita la copia
guardada. Un perfil que la quiera pone openai_store a true en sus ajustes.
Providers¶
Veintiocho, que es todo lo que trae Pydantic AI a lo que un perfil de chat puede apuntar.
Aquí no hay un constructor por provider. Pydantic AI infiere la clase del provider y el envoltorio del modelo a partir del id, y lo que esta plataforma sigue teniendo que saber es la parte que la inferencia no puede: qué forma de credencial quiere un provider.
Qué significa Custom URL
El SDK del provider nombra un parámetro de endpoint, así que puedes apuntar un perfil a un gateway, a un proxy de LiteLLM o a un servidor de modelos en tu propia red en vez de a la API pública del proveedor.
Es un campo del perfil, no de la clave. Una clave dice qué autentica, un endpoint dice adónde va la petición — así que la misma clave puede estar delante de un proxy de staging y de uno de producción como dos perfiles.
Configúralo en Agents → add a model → Endpoint, que aparece solo para los providers marcados abajo. Guardar uno para un provider que no tiene ninguno se rechaza, en vez de aceptarse y descartarse.
Alojados¶
| Provider | id | Credencial | Custom URL |
|---|---|---|---|
| OpenAI | openai |
clave de API, o ninguna | ✓ |
| Anthropic | anthropic |
clave de API | ✓ |
| Google Gemini | google |
clave de API | ✓ |
| OpenRouter | openrouter |
clave de API | ✓ |
| Alibaba Cloud | alibaba |
clave de API | ✓ |
| Cerebras | cerebras |
clave de API | |
| DeepSeek | deepseek |
clave de API | |
| Fireworks AI | fireworks |
clave de API | |
| GitHub Models | github |
clave de API | |
| Groq | groq |
clave de API | |
| Heroku AI | heroku |
clave de API | ✓ |
| Mistral | mistral |
clave de API | |
| Moonshot AI | moonshotai |
clave de API | |
| Nebius AI Studio | nebius |
clave de API | |
| OVHcloud AI Endpoints | ovhcloud |
clave de API | |
| SambaNova | sambanova |
clave de API | ✓ |
| Together AI | together |
clave de API | |
| Vercel AI Gateway | vercel |
clave de API | |
| Z.AI | zai |
clave de API | |
| xAI (Grok) | xai |
clave de API | ✓ (api_host) |
| Cohere | cohere |
clave de API | |
| Hugging Face | huggingface |
clave de API | ✓ |
Autoalojados¶
| Provider | id | Credencial | Custom URL |
|---|---|---|---|
| Ollama | ollama |
ninguna | ✓ |
| LiteLLM proxy | litellm |
ninguna | ✓ (api_base) |
Estos dos son la razón de que «sin credencial» sea un kind almacenado y no una cadena vacía. Un servidor de modelos en tu propia red no suele tener nada contra lo que autenticarse, y el vault rechaza un secreto vacío — así que el resolver decide sobre un conjunto completo en vez de tratar un valor ausente como un caso especial.
Un perfil sin clave necesita su endpoint, y eso es lo único que necesita. El campo de la clave pasa a ser opcional en cuanto se rellena uno. Sin endpoint el perfil se rechaza: no hay una API pública a la que recurrir ni nada con lo que autenticarse.
Lo que marca un perfil como autoalojado es el endpoint, no keyless
keyless también es cierto de openai. Los servidores compatibles con
OpenAI (vLLM, LM Studio, un proxy de LiteLLM) hablan su API de Chat
Completions, que es por lo que un perfil openai con endpoint la usa por
defecto - ver Responses o Chat Completions.
Así que «sin clave» por sí solo no distingue un modelo local deliberado de un
perfil al que le borraron la clave — y la clave ajena del secreto es
ON DELETE SET NULL, lo que hace que el segundo caso sea corriente. Un run
resuelve un perfil sin clave solo cuando este lleva un endpoint; si no, se
rechaza con el mismo mensaje de «no key configured» que ha tenido siempre.
Cuando la credencial no es una clave de API¶
| Provider | id | Credencial |
|---|---|---|
| Azure OpenAI | azure |
clave + endpoint + versión de API fijada |
| AWS Bedrock | bedrock |
id de clave de acceso, clave secreta, región, token de sesión opcional |
| Google Vertex AI | google_cloud |
JSON de la cuenta de servicio |
| ChatGPT (suscripción de Codex) | openai_codex |
un inicio de sesión de ChatGPT, guardado iniciando sesión en vez de tecleado |
Los tres primeros son la razón de que un secreto tenga un kind. Un formulario que recogiera un único token opaco para Azure recogería algo que se puede rellenar correctamente y que aun así falla en el primer run. Ver los kinds de secreto.
Dos ids se reescriben de camino al SDK
Un perfil openai se construye como openai-chat u openai-responses, y uno
azure como azure o azure-responses, según la API que guarda, en lugar de
tomar la Responses API que infiere openai a secas. Ver
Responses o Chat Completions.
google_cloud se construye como google-cloud. Ninguno de los dos cambia lo
que guardas.
Ejecutar con una suscripción de ChatGPT¶
openai_codex ejecuta modelos de OpenAI en el backend de Codex a través de una
suscripción de ChatGPT, en vez de a través de una clave de API medida. Está
pensado para uso personal: el inicio de sesión es la suscripción de una
persona, y cada run en un perfil que lo usa cuenta contra los límites de ChatGPT
de esa persona.
Nadie puede teclear la credencial. En su lugar inicias sesión con ChatGPT:
- En Vault → add a secret (o en Agents → add a model, con ChatGPT (Codex subscription) seleccionado), elige Sign in with ChatGPT. Se abre un inicio de sesión de ChatGPT en una pestaña nueva.
- Apruébalo. ChatGPT envía entonces el navegador a
http://localhost:1455/..., que no carga. Es lo esperado: OpenAI fija esa dirección para todos los clientes salvo el suyo propio. - Copia la dirección completa de la barra de direcciones y pégala de vuelta en el formulario.
El código de esa dirección funciona una sola vez, solo para la persona que inició el inicio de sesión, y solo durante 15 minutos.
El access token caduca y se renueva mientras los modelos se ejecutan. Cada renovación gasta el refresh token, así que el par nuevo se vuelve a sellar en el mismo secreto del vault de inmediato. Dos runs que renuevan en el mismo momento aún pueden competir. El que pierde se rechaza, y la solución es Reconnect en el secreto, que vuelve a iniciar sesión sin cambiar su id.
Lo que no es un inicio de sesión de ChatGPT
- No es el
auth.jsonde la CLI de Codex. Ese archivo guarda la concesión propia de la CLI. Los refresh tokens son de un solo uso, así que cualquiera de los dos que renovara primero cerraría la sesión del otro. - No es una credencial de la organización. Un secreto se crea privado por defecto, y un modelo sobre él solo funciona para quien puede ver ese inicio de sesión en el vault y pregunta él mismo. Para cualquier otro - un visitante de una página pública o un embed, quien llama con una clave API - y para el trabajo en segundo plano como describir las imágenes de un documento, el modelo se rechaza y un fallback sobre él se omite.
- El gasto mostrado no es tu factura. Un run se tarifa a los precios de la API de OpenAI para el mismo modelo, que es lo que un budget puede razonar. La suscripción en sí la factura OpenAI a tarifa plana.
- Algunos ajustes se ignoran. El backend de Codex descarta
max_tokens,temperatureytop_p.
Ausentes a propósito¶
Cuatro nombres que Pydantic AI conoce no están aquí. sentence-transformers y
voyageai son modelos de embeddings, bedrock-mantle no es un provider de chat
al que un perfil pueda apuntar, y gateway no resuelve a una clase de provider —
es un prefijo de enrutado sobre los demás.
tests/test_model_profiles.py construye cada entrada del catálogo, así que un
provider no puede ser seleccionable en el Builder sin ser construible en tiempo
de ejecución.
Qué lista responde a qué pregunta¶
Seis cosas de este repositorio saben algo sobre modelos y providers, y no son seis copias de una misma lista. Cada una responde a una pregunta distinta, y aquella de la que se derivan todas es la primera:
| Pregunta | Respondida por |
|---|---|
| ¿A qué providers puede apuntar un perfil, y qué credencial quiere cada uno? | PROVIDERS en backend/app/agents/model_resolver.py — la fuente de verdad, y solo para la parte que la inferencia del modelo no puede saber |
| ¿Cómo construyo el cliente? | El propio infer_provider_class / infer_model de pydantic_ai. No es asunto de esta plataforma, y deliberadamente no se repite aquí |
| ¿Cómo leo la lista de modelos en vivo de este provider? | backend/app/core/catalog/model_listings.json |
| ¿Qué sugiero cuando no se le puede preguntar al provider? | backend/app/core/catalog/curated_models.json |
| ¿Cuánto cuesta este modelo, y cuánto contexto acepta? | el snapshot de genai-prices, a través de model_catalog.priced_model |
| ¿Qué modelos dibujan imágenes? | backend/app/core/catalog/image_models.json, más la propia respuesta del SDK sobre qué providers pueden dibujar |
Todo lo que hay bajo la primera fila se deriva de ella
Una copia derivada que se desvía no falla nada en tiempo de ejecución. Muestra un selector para un provider que no existe, o deja fuera uno que sí.
tests/test_model_catalog.py::TestOneAnswerPerQuestion es lo que en su lugar
convierte eso en una build fallida — y exige que cada provider aparezca en
esta página.
Cada clave de cualquiera de los dos archivos de catálogo tiene que nombrar un
provider que PROVIDERS tenga. También cada entrada del catálogo de imágenes. Y
cada provider tiene que aparecer en esta página. Añadir el vigesimonoveno es una
edición más lo que ese test pida después.
Merece la pena conocer dos cruces, porque son búsquedas que pueden no responder nada:
- El snapshot de precios escribe cuatro providers de otra forma —
xaiesx-ai,bedrockesaws,google_cloudesgoogle, yopenai_codexse tarifa comoopenai— y_PRICE_PROVIDER_ALIASESlos puentea. - El catálogo de imágenes lleva su propio par
provideryprefix, que es un tercer vocabulario otra vez.
Qué modelos ofrece un provider¶
El campo del id de modelo se rellena desde dos fuentes, en este orden — y desde ninguna de las dos para siete providers, cosa que la respuesta dice en voz alta.
En vivo¶
Veinte providers publican un endpoint de listado, y es la única fuente que sabe de un modelo publicado esta mañana:
anthropic, openai, google, openrouter, groq, mistral, together,
cohere, deepseek, xai, sambanova, vercel, ovhcloud, huggingface,
cerebras, fireworks, nebius, moonshotai, zai, alibaba.
Las formas de la respuesta no coinciden — el array está en data, en models o
en la raíz del documento; el id es id, name o model; Gemini lo prefija con
models/ — así que cada uno se describe con datos y no con una rama. Se cachean
en el proceso durante una hora; estas listas se mueven en el orden de semanas.
Cinco de ellos no necesitan credencial alguna — openrouter, sambanova,
vercel, ovhcloud y huggingface — que es lo que los hace valiosos: el
selector se rellena antes de que nadie haya guardado una clave para ese provider.
A los otros quince se les pregunta con la clave propia de la organización cuando
la hay.
Seis providers siguen sin publicar nada que esto pueda leer: github (su ruta de
catálogo ya no está), heroku, azure, bedrock, google_cloud, y un proxy
litellm cuya lista es lo que el despliegue haya puesto detrás. ollama
responde en la red del propio despliegue y no en un host fijo, así que tampoco
está en la lista.
Una lista de modalidades vacía significa no declarado, nunca «solo texto»
openrouter y el router de Hugging Face llevan los dos
architecture.output_modalities, y una entrada de listado puede nombrar esa
ruta. Nadie más lo declara.
Un cliente que filtre por ella tiene que tratar la ausencia como desconocido, o esconderá modelos que funcionan. Son metadatos por los que un cliente puede estrechar; no son cómo la capability de imagen elige sus modelos, que es un archivo de catálogo más la propia respuesta del SDK sobre qué providers pueden dibujar — ver Generación de imágenes.
Curados¶
Una lista corta por provider, usada cuando el provider no publica nada, cuando la llamada falla, o cuando no hay clave con la que hacerla.
Vive en backend/app/core/catalog/curated_models.json junto a los demás
catálogos del despliegue, así que añadir un modelo es una entrada y no una
edición de Python — y los propios listados son model_listings.json en el mismo
directorio, que es lo que convierte también en datos el endpoint de un provider
nuevo.
Es deliberadamente corta y deliberadamente no se toma de genai-prices, que
ya es una dependencia y sí lista modelos.
Ese es un conjunto de datos de precios. Lleva ada y babbage bajo OpenAI,
claude-2 bajo Anthropic, 690 filas bajo OpenRouter, y no marca casi nada como
obsoleto — ordenado alfabéticamente, lo primero que un selector ofrecería para
OpenAI es ada. Una lista corta y actual gana a una larga y engañosa.
Para lo que la biblioteca sí se usa es para la mitad que se pudre. Cada longitud de contexto sale del snapshot en el momento de leerla, así que aquí no se escribe ninguna ventana; dos que lo estaban ya se habían quedado obsoletas, una de ellas anotada dos veces con dos cifras distintas.
Y un id curado del que el snapshot nunca ha oído hablar hace fallar la suite de tests, que es como se pilla una errata o un modelo retirado en vez de publicarlo como un desplegable al que el provider responde 404. Un modelo que el snapshot conoce pero no tarifa simplemente no tiene ventana, que es el nulo descrito más abajo.
| Provider | ids curados |
|---|---|
anthropic |
claude-opus-5, claude-sonnet-5, claude-fable-5, claude-haiku-4-5 |
openai |
gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.3-codex |
openai_codex |
los mismos cuatro que openai |
google |
gemini-3.6-flash, gemini-3.5-flash, gemini-3.5-flash-lite, gemini-3.1-pro-preview |
deepseek |
deepseek-v4-pro, deepseek-v4-flash |
xai |
grok-4.5, grok-4.3 |
groq |
openai/gpt-oss-120b, llama-3.3-70b-versatile |
openrouter |
cinco ids comunes entre providers |
Ninguna de las dos, y lo dice¶
Siete providers no publican ningún listado que esta plataforma pueda leer y no
tienen entrada curada — github, heroku, ollama, litellm, azure,
bedrock y google_cloud.
Para esos, el source de la respuesta es unlisted, no curated. Una lista
corta vacía no es una lista corta, y afirmar que lo es es lo que convierte «esta
plataforma no puede enumerar este provider» en «este provider no tiene modelos»
(#923). El selector pide el id en su lugar.
ollama y litellm son los que merece la pena conectar — los dos publican un
/v1/models con forma de OpenAI en el endpoint que el perfil ya guarda — y eso
exige decirle al listado una URL base, cosa que un ListingSpec.url fijo no
puede ser.
Ninguna de las dos fuentes es autoritativa, que es por lo que el campo sigue siendo texto libre.
La ventana que acepta un modelo se lee una vez y se guarda¶
Un listado suele llevar cuántos tokens acepta el modelo, y el perfil lo anota
como context_length cuando se crea.
Ese número es sobre el que se dispara la gestión de contexto: compactar a una fracción de la ventana es el único ajuste que sigue siendo correcto cuando un agent se muda a otro modelo.
Por qué se guarda en vez de resolverse por run
El camino de la petición no debe llamar a un provider, y lo único que podría consultar si no es el snapshot de precios incluido — que aquí se equivoca en la dirección que rompe un run.
Ese snapshot registra 1.000.000 para anthropic:claude-sonnet-4-5 frente a
unos 200.000 reales, así que un disparo al 90 % cae por encima del techo real
y la compactación nunca se activa antes de que el provider rechace la
petición. Un perfil con fallbacks es peor: construye un FallbackModel cuyo
id compuesto no resuelve a nada en absoluto.
Nulo significa no anotado, no cero: un perfil más antiguo que la columna, un provider que no publica longitud, una lista curada, o un listado al que no se pudo llegar. La capability resuelve entonces la ventana ella misma, exactamente como hacía antes.
Si sabes más que las dos, pon context_window en el binding. Un provider publica
el máximo que se puede hacer aceptar a un modelo, y un despliegue limitado por
beta o por nivel obtiene menos.
Una cadena de fallbacks lleva el número del primario. Un FallbackModel no
tiene ventana propia, y a qué modelo llega un run no se sabe hasta que uno ha
rechazado.
Lo que cuesta un run¶
Los precios vienen de un snapshot de
genai-prices incluido en el
paquete. Nada llama a casa para conseguirlos, lo que significa dos cosas que
conviene saber:
- Un modelo demasiado nuevo para el snapshot está sin tarifar, y un run que contenga uno se registra como parcialmente tarifado y no como si no costara nada. Un budget que tratara en silencio un modelo desconocido como gratis sería un budget con un agujero.
- Un
vendor/modelde un agregador que el snapshot no conoce se tarifa como el modelo propio del proveedor, y una versión con punto se prueba también con guiones: elanthropic/claude-sonnet-5.5de OpenRouter cuesta lo que elclaude-sonnet-5-5de Anthropic. - Actualizar los precios es subir una dependencia.
Un provider sin clave no registra gasto
El gasto se atribuye al secreto del vault al que resolvió el run, y un provider sin clave no tiene ninguno al que atribuirlo.
El coste se comprueba antes de cada petición al modelo y se registra incluso cuando el run falla. Ver Budgets.
Una delegación resuelve su propio perfil¶
Un run puede implicar varios modelos.
Un delegate funciona sobre el perfil que nombra su propio spec, resuelto cuando el runner recorre el árbol de delegación. Un especialista inline que no nombra ninguno funciona sobre el perfil del agent que lo llamó — a la vez la respuesta menos sorprendente y la única que funciona cuando el del padre es el único perfil que eligió el autor.
Esas peticiones se miden contra el único libro de cuentas del run padre, pero se tarifan por provider: el guard del delegate comparte el libro, los topes y las líneas base del mes, y toma su propio provider.
Compartir directamente el del padre tarifaría un delegate de Anthropic contra el catálogo de OpenAI — en silencio, y normalmente como sin tarifar, lo que subestima el run y marca uno perfectamente tarifable como un suelo.
La fila del run hijo que escribe una delegación nombra el modelo que la respondió, así que el panel de costes agrupa un turno delegado bajo el modelo que realmente se ejecutó y no bajo el del padre.
Modelos realtime¶
Una llamada de voz no usa un perfil de modelo. Su modelo se elige de
un catálogo que trae este despliegue, backend/app/core/catalog/realtime_models.json,
que lee backend/app/services/realtime_models.py y que se sirve al Builder en
GET /api/v1/providers/realtime-models. La clave se nombra en la sección Voice
del agent como un secreto del vault, no en un perfil. Es la misma forma que usa
la voz a texto: una elección del catálogo más una clave explícita.
Cada entrada nombra un provider, sus modelos y, para cada modelo:
| Campo | Qué decide |
|---|---|
transport |
webrtc (el navegador habla con el provider; este despliegue mantiene un sideband) o relay (el audio pasa por el socket de este despliegue) |
input_sample_rate, output_sample_rate |
Las frecuencias PCM a las que el navegador graba y reproduce, a través del relay |
voices, default_voice |
Lo que ofrece la sección Voice, y lo que significa no elegir nada |
supports_thinking |
Si la capability de thinking del agent se traduce al ajuste de razonamiento propio del modelo |
context_window |
Dónde una reconexión pasa de reproducir los turnos a un resumen de los más antiguos |
pricing |
kind (tokens o per_minute), worst_case_usd_per_minute para la comprobación previa a una llamada, y usd_per_minute para un modelo por minuto |
La familia gpt-realtime de OpenAI funciona sobre WebRTC y es el único provider
probado en vivo. Azure OpenAI, Gemini Live y xAI funcionan sobre el relay y no
están verificados en vivo. GPT-Live no está en el catálogo: no puede recibir
historial después de que empiece una sesión, así que una reconexión no podría
continuar una llamada.
Azure es un deployment más un modelo base. El nombre de un deployment lo
elige su propietario y no dice nada de lo que ejecuta, así que la sección Voice
toma el nombre del deployment como texto libre y el modelo del catálogo que
ejecuta como base_model. El modelo base decide las voces y el precio: los
tokens de una llamada de Azure se tarifan como ese modelo en OpenAI. El endpoint
y la versión de la API vienen del secreto azure_openai.
Precios. Los modelos tarifados por token se tarifan por respuesta a partir
del mismo snapshot incluido que los runs de texto. Un modelo por minuto (xAI) se
factura por el tiempo conectado al usd_per_minute del catálogo. Un modelo
tarifado por token que el snapshot no sabe tarifar se rechaza cuando empieza una
llamada, en vez de funcionar con un budget que no puede hacer cumplir.
Resumen¶
- Un perfil es un modelo con nombre más una clave con nombre, y los agents apuntan a perfiles para que rotar cualquiera de los dos toque una sola fila.
- 28 providers, y lo único que esta plataforma sabe de cada uno es la forma de la credencial — construirlo es trabajo de Pydantic AI.
- El id de modelo es texto libre, porque ninguna lista es autoritativa.
- La longitud de contexto se lee una vez y se guarda, porque el snapshot de precios se equivoca sobre ella en la dirección que rompe un run.
- El coste viene de un snapshot incluido, un modelo desconocido se registra como sin tarifar y no como gratis, y un provider sin clave no registra gasto alguno.
- Los modelos realtime de las llamadas de voz son una elección del catálogo más una clave con nombre, sin perfil; solo los de OpenAI están verificados en vivo.
Configurar uno¶
El recorrido del primer agent hace esto de principio a fin. En resumen: guarda una clave de provider en Settings → Secrets, añade un perfil de modelo que la nombre y después apunta el spec de un agent a ese perfil.
hace las tres cosas para un despliegue nuevo.
Para usar la API regional de OpenRouter, conserva OpenRouter como proveedor e introduce https://eu.openrouter.ai/api/v1 en Endpoint del formulario del modelo. El perfil mantiene su clave OpenRouter del Vault, el tratamiento nativo de modelos y los controles de caché. Las sugerencias se obtienen de ese endpoint y se almacenan en una caché independiente; si falla la lista regional, no se utiliza el catálogo global. En las bases de conocimiento, configura el endpoint de embeddings por separado al crear la colección o editar su proveedor de embeddings. Las configuraciones existentes mantienen el endpoint global. El enrutamiento EU requiere un plan OpenRouter Business o Enterprise adecuado; el enrutamiento regional y ZDR son políticas distintas de OpenRouter.
Un endpoint regional sirve menos modelos de embeddings que el global: eu.openrouter.ai sirve mistralai/mistral-embed-2312, mistralai/codestral-embed-2505 y google/gemini-embedding-2, y ninguno de OpenAI. El formulario de creación ofrece solo los modelos que lista el endpoint, y se rechaza una colección cuyo modelo el endpoint no sirve, tanto al crearla como al moverla a él. Los dos modelos de Mistral no están en la instantánea de precios incluida, así que su gasto se registra como valorado en parte.
Actualiza todos los procesos de API y workers antes de configurar endpoints regionales. Las versiones anteriores ignoran el endpoint personalizado de OpenRouter.
Para otro endpoint con el mismo proveedor y modelo, elija Añadir modelo separado en el Builder. El nombre automático incluye el endpoint y el perfil existente no cambia. Actualizar modelo modifica el perfil compartido. Los catálogos personalizados están limitados a 2 MiB, 2048 modelos y longitudes de campos acotadas, con un máximo de 16 catálogos en caché por proceso. Una respuesta comprimida o demasiado grande permite escribir el ID del modelo sin recurrir a una lista global.