Saltearse al contenido

Modelos personalizados

Añade proveedores y modelos personalizados (Ollama, vLLM, LM Studio, proxies) mediante ~/.pi/agent/models.json.

Para modelos locales (Ollama, LM Studio, vLLM), solo se requiere id por modelo:

{
"providers": {
"ollama": {
"baseUrl": "http://localhost:11434/v1",
"api": "openai-completions",
"apiKey": "ollama",
"models": [
{ "id": "llama3.1:8b" },
{ "id": "qwen2.5-coder:7b" }
]
}
}
}

apiKey es obligatorio, pero Ollama lo ignora, así que cualquier valor sirve.

Algunos servidores compatibles con OpenAI no entienden el rol developer usado para modelos con capacidad de razonamiento. Para esos proveedores, establece compat.supportsDeveloperRole en false para que pi envíe el prompt del sistema como mensaje system. Si el servidor tampoco admite reasoning_effort, establece también compat.supportsReasoningEffort en false.

Puedes configurar compat a nivel de proveedor para aplicarlo a todos los modelos, o a nivel de modelo para sobrescribir uno concreto. Esto es habitual en Ollama, vLLM, SGLang y servidores similares compatibles con OpenAI.

{
"providers": {
"ollama": {
"baseUrl": "http://localhost:11434/v1",
"api": "openai-completions",
"apiKey": "ollama",
"compat": {
"supportsDeveloperRole": false,
"supportsReasoningEffort": false
},
"models": [
{
"id": "gpt-oss:20b",
"reasoning": true
}
]
}
}
}

Sobrescribe los valores predeterminados cuando necesites valores específicos:

{
"providers": {
"ollama": {
"baseUrl": "http://localhost:11434/v1",
"api": "openai-completions",
"apiKey": "ollama",
"models": [
{
"id": "llama3.1:8b",
"name": "Llama 3.1 8B (Local)",
"reasoning": false,
"input": ["text"],
"contextWindow": 128000,
"maxTokens": 32000,
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
}
]
}
}
}

El archivo se recarga cada vez que abres /model. Edita durante la sesión; no hace falta reiniciar.

Usa google-generative-ai con un baseUrl para añadir modelos de Google AI Studio, incluidas entradas personalizadas de Gemma 4:

{
"providers": {
"my-google": {
"baseUrl": "https://generativelanguage.googleapis.com/v1beta",
"api": "google-generative-ai",
"apiKey": "$GEMINI_API_KEY",
"models": [
{
"id": "gemma-4-31b-it",
"name": "Gemma 4 31B",
"input": ["text", "image"],
"contextWindow": 262144,
"reasoning": true
}
]
}
}
}

baseUrl es obligatorio al añadir modelos personalizados al tipo de API google-generative-ai.

APIDescripción
openai-completionsOpenAI Chat Completions (mayor compatibilidad)
openai-responsesOpenAI Responses API
anthropic-messagesAnthropic Messages API
google-generative-aiGoogle Generative AI

Establece api a nivel de proveedor (predeterminado para todos los modelos) o a nivel de modelo (sobrescritura por modelo).

CampoDescripción
baseUrlURL del endpoint de la API
apiTipo de API (ver arriba)
apiKeyClave API (ver resolución de valores abajo)
headersCabeceras personalizadas (ver resolución de valores abajo)
authHeaderEstablece true para añadir automáticamente Authorization: Bearer <apiKey>
modelsArray de configuraciones de modelos
modelOverridesSobrescrituras por modelo para modelos integrados en este proveedor

Los campos apiKey y headers admiten ejecución de comandos, interpolación de entorno y literales:

  • Comando shell: "!command" al inicio ejecuta todo el valor como comando y usa stdout
    "apiKey": "!security find-generic-password -ws 'anthropic'"
    "apiKey": "!op read 'op://vault/item/credential'"
  • Interpolación de entorno: "$ENV_VAR" o "${ENV_VAR}" usa el valor de la variable nombrada. La interpolación funciona dentro de literales más grandes.
    "apiKey": "$MY_API_KEY"
    "apiKey": "${KEY_PREFIX}_${KEY_SUFFIX}"
    $FOO_BAR es la variable FOO_BAR; usa ${FOO}_BAR cuando BAR es texto literal. Las variables de entorno faltantes dejan el valor sin resolver.
  • Escapes: "$$" emite un "$" literal; "$!" emite un "!" literal sin activar la ejecución del comando.
    "apiKey": "$$literal-dollar-prefix"
    "apiKey": "$!literal-bang-prefix"
  • Valor literal: Se usa directamente. Cadenas en mayúsculas como MY_API_KEY son literales; usa $MY_API_KEY para variables de entorno.
    "apiKey": "sk-..."

Para models.json, los comandos shell se resuelven en el momento de la solicitud. pi no aplica intencionalmente TTL integrado, reutilización obsoleta ni lógica de recuperación para comandos arbitrarios. Diferentes comandos necesitan distintas estrategias de caché y fallo, y pi no puede inferir la correcta.

Si tu comando es lento, costoso, tiene límite de tasa o debe seguir usando un valor anterior en fallos transitorios, envuélvelo en tu propio script o comando que implemente el comportamiento de caché o TTL que desees.

Las comprobaciones de disponibilidad de /model usan la presencia de auth configurada y no ejecutan comandos shell.

{
"providers": {
"custom-proxy": {
"baseUrl": "https://proxy.example.com/v1",
"apiKey": "$MY_API_KEY",
"api": "anthropic-messages",
"headers": {
"x-portkey-api-key": "$PORTKEY_API_KEY",
"x-secret": "!op read 'op://vault/item/secret'"
},
"models": [...]
}
}
}
CampoObligatorioPredeterminadoDescripción
idIdentificador del modelo (se pasa a la API)
nameNoidEtiqueta legible del modelo. Se usa para coincidencias (patrones --model) y se muestra como texto de detalle secundario del modelo.
apiNoapi del proveedorSobrescribe la API del proveedor para este modelo
reasoningNofalseAdmite pensamiento extendido
thinkingLevelMapNoomitidoMapea niveles de pensamiento de pi a valores del proveedor y marca niveles no admitidos (ver abajo)
inputNo["text"]Tipos de entrada: ["text"] o ["text", "image"]
contextWindowNo128000Tamaño de la ventana de contexto en tokens
maxTokensNo16384Máximo de tokens de salida
costNotodos ceros{"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0} (por millón de tokens)
compatNocompat del proveedorSobrescrituras de compatibilidad del proveedor. Se fusiona con compat a nivel de proveedor cuando ambos están definidos.

Comportamiento actual:

  • /model, --list-models y el pie interactivo muestran entradas por id de modelo.
  • El name configurado se usa para coincidencias de modelos y texto de detalle secundario. No reemplaza el id del modelo en el pie/barra de estado.

Usa thinkingLevelMap en un modelo para describir controles de pensamiento específicos del modelo. Las claves son niveles de pensamiento de pi: off, minimal, low, medium, high, xhigh.

Los valores son triestado:

ValorSignificado
omitidoEl nivel está admitido y usa el mapeo predeterminado del proveedor
stringEl nivel está admitido y este valor se envía al proveedor
nullEl nivel no está admitido y se oculta/omite/limita

Ejemplo para un modelo que solo admite razonamiento off, high y max:

{
"id": "deepseek-v4-pro",
"reasoning": true,
"thinkingLevelMap": {
"minimal": null,
"low": null,
"medium": null,
"high": "high",
"xhigh": "max"
}
}

Ejemplo para un modelo en el que el pensamiento no se puede desactivar:

{
"id": "always-thinking-model",
"reasoning": true,
"thinkingLevelMap": {
"off": null
}
}

Migración: las configuraciones antiguas que usaban compat.reasoningEffortMap deben mover ese mapeo a thinkingLevelMap a nivel de modelo. Usa null para niveles que no deben aparecer en la UI.

Enruta un proveedor integrado a través de un proxy sin redefinir modelos:

{
"providers": {
"anthropic": {
"baseUrl": "https://my-proxy.example.com/v1"
}
}
}

Todos los modelos Anthropic integrados siguen disponibles. La auth OAuth o de clave API existente sigue funcionando.

Para fusionar modelos personalizados en un proveedor integrado, incluye el array models:

{
"providers": {
"anthropic": {
"baseUrl": "https://my-proxy.example.com/v1",
"apiKey": "$ANTHROPIC_API_KEY",
"api": "anthropic-messages",
"models": [...]
}
}
}

Semántica de fusión:

  • Se conservan los modelos integrados.
  • Los modelos personalizados se actualizan por id dentro del proveedor.
  • Si el id de un modelo personalizado coincide con el de un modelo integrado, el personalizado reemplaza al integrado.
  • Si el id del modelo personalizado es nuevo, se añade junto a los modelos integrados.

Usa modelOverrides para personalizar modelos integrados concretos sin reemplazar la lista completa de modelos del proveedor.

{
"providers": {
"openrouter": {
"modelOverrides": {
"anthropic/claude-sonnet-4": {
"name": "Claude Sonnet 4 (Bedrock Route)",
"compat": {
"openRouterRouting": {
"only": ["amazon-bedrock"]
}
}
}
}
}
}
}

modelOverrides admite estos campos por modelo: name, reasoning, input, cost (parcial), contextWindow, maxTokens, headers, compat.

Notas de comportamiento:

  • modelOverrides se aplican a modelos de proveedores integrados.
  • Los ID de modelo desconocidos se ignoran.
  • Puedes combinar baseUrl/headers a nivel de proveedor con modelOverrides.
  • Sobrescribir name cambia solo la coincidencia de modelos y el texto de detalle secundario; el pie y las listas principales de modelos siguen mostrando el id del modelo.
  • Si también se define models para un proveedor, los modelos personalizados se fusionan después de las sobrescrituras integradas. Un modelo personalizado con el mismo id reemplaza la entrada del modelo integrado sobrescrito.

Para proveedores o proxies que usan api: "anthropic-messages", usa compat para controlar la compatibilidad de solicitudes específica de Anthropic.

Por defecto pi envía eager_input_streaming: true por herramienta. Si un proxy o backend compatible con Anthropic rechaza ese campo, establece supportsEagerToolInputStreaming en false. Pi omitirá tools[].eager_input_streaming y enviará la cabecera beta heredada fine-grained-tool-streaming-2025-05-14 en solicitudes con herramientas habilitadas.

Algunos modelos Anthropic requieren pensamiento adaptativo (thinking.type: "adaptive" más output_config.effort) en lugar del payload de pensamiento heredado basado en presupuesto. Los modelos integrados lo configuran automáticamente. Para proveedores personalizados o alias que enrutan a esos modelos, establece forceAdaptiveThinking en true.

Algunos proveedores compatibles con Anthropic emiten bloques de pensamiento con firmas vacías y aún los esperan en la reproducción. Establece allowEmptySignature en true solo para esos proveedores; Anthropic real rechaza firmas de pensamiento vacías.

{
"providers": {
"anthropic-proxy": {
"baseUrl": "https://proxy.example.com",
"api": "anthropic-messages",
"apiKey": "$ANTHROPIC_PROXY_KEY",
"compat": {
"supportsEagerToolInputStreaming": false,
"supportsLongCacheRetention": true,
"forceAdaptiveThinking": true,
"allowEmptySignature": true
},
"models": [
{
"id": "claude-opus-4-7",
"reasoning": true,
"input": ["text", "image"]
}
]
}
}
}
CampoDescripción
supportsEagerToolInputStreamingSi el proveedor acepta eager_input_streaming por herramienta. Predeterminado: true. Establece en false para omitir ese campo y usar la cabecera beta heredada de streaming fino de herramientas en solicitudes con herramientas.
supportsLongCacheRetentionSi el proveedor acepta retención larga de caché de Anthropic (cache_control.ttl: "1h") cuando la retención de caché es long. Predeterminado: true.
sendSessionAffinityHeadersSi enviar x-session-affinity desde el id de sesión cuando la caché está habilitada. Predeterminado: autodetectado para proveedores conocidos.
supportsCacheControlOnToolsSi el proveedor acepta marcadores cache_control al estilo Anthropic en definiciones de herramientas. Predeterminado: true.
forceAdaptiveThinkingSi enviar pensamiento adaptativo (thinking.type: "adaptive" más output_config.effort) para este modelo. Los modelos adaptativos integrados lo configuran automáticamente. Predeterminado: false.
allowEmptySignatureSi reproducir firmas de pensamiento vacías como signature: "" en lugar de convertir el pensamiento a texto. Predeterminado: false.

Para proveedores con compatibilidad parcial con OpenAI, usa el campo compat.

  • compat a nivel de proveedor aplica valores predeterminados a todos los modelos bajo ese proveedor.
  • compat a nivel de modelo sobrescribe los valores a nivel de proveedor para ese modelo.
{
"providers": {
"local-llm": {
"baseUrl": "http://localhost:8080/v1",
"api": "openai-completions",
"compat": {
"supportsUsageInStreaming": false,
"maxTokensField": "max_tokens"
},
"models": [...]
}
}
}
CampoDescripción
supportsStoreEl proveedor admite el campo store
supportsDeveloperRoleUsar rol developer frente a system
supportsReasoningEffortSoporte del parámetro reasoning_effort
supportsUsageInStreamingAdmite stream_options: { include_usage: true } (predeterminado: true)
maxTokensFieldUsar max_completion_tokens o max_tokens
requiresToolResultNameIncluir name en mensajes de resultado de herramienta
requiresAssistantAfterToolResultInsertar un mensaje assistant antes de un mensaje user tras resultados de herramienta
requiresThinkingAsTextConvertir bloques de pensamiento a texto plano
requiresReasoningContentOnAssistantMessagesIncluir reasoning_content vacío en todos los mensajes assistant reproducidos cuando el razonamiento está habilitado
thinkingFormatUsar parámetros de pensamiento reasoning_effort, openrouter, deepseek, together, zai, qwen o qwen-chat-template
cacheControlFormatUsar marcadores cache_control al estilo Anthropic en el prompt del sistema, la última definición de herramienta y el último contenido de texto user/assistant. Actualmente solo se admite anthropic.
supportsStrictModeIncluir el campo strict en definiciones de herramientas
supportsLongCacheRetentionSi el proveedor acepta retención larga de caché cuando la retención es long: prompt_cache_retention: "24h" para caché de prompts de OpenAI, o cache_control.ttl: "1h" cuando cacheControlFormat es anthropic. Predeterminado: true.
openRouterRoutingPreferencias de enrutamiento de proveedor de OpenRouter. Este objeto se envía tal cual en el campo provider de la solicitud a la API de OpenRouter.
vercelGatewayRoutingConfiguración de enrutamiento de Vercel AI Gateway para selección de proveedor (only, order)

openrouter usa reasoning: { effort }. together usa reasoning: { enabled } y también reasoning_effort cuando supportsReasoningEffort está habilitado. qwen usa enable_thinking de nivel superior. Usa qwen-chat-template para servidores locales compatibles con Qwen que requieren chat_template_kwargs.enable_thinking.

cacheControlFormat: "anthropic" es para proveedores compatibles con OpenAI que exponen caché de prompts al estilo Anthropic mediante marcadores cache_control en contenido de texto y definiciones de herramientas.

Ejemplo:

{
"providers": {
"openrouter": {
"baseUrl": "https://openrouter.ai/api/v1",
"apiKey": "$OPENROUTER_API_KEY",
"api": "openai-completions",
"models": [
{
"id": "openrouter/anthropic/claude-3.5-sonnet",
"name": "OpenRouter Claude 3.5 Sonnet",
"compat": {
"openRouterRouting": {
"allow_fallbacks": true,
"require_parameters": false,
"data_collection": "deny",
"zdr": true,
"enforce_distillable_text": false,
"order": ["anthropic", "amazon-bedrock", "google-vertex"],
"only": ["anthropic", "amazon-bedrock"],
"ignore": ["gmicloud", "friendli"],
"quantizations": ["fp16", "bf16"],
"sort": {
"by": "price",
"partition": "model"
},
"max_price": {
"prompt": 10,
"completion": 20
},
"preferred_min_throughput": {
"p50": 100,
"p90": 50
},
"preferred_max_latency": {
"p50": 1,
"p90": 3,
"p99": 5
}
}
}
}
]
}
}
}

Ejemplo de Vercel AI Gateway:

{
"providers": {
"vercel-ai-gateway": {
"baseUrl": "https://ai-gateway.vercel.sh/v1",
"apiKey": "$AI_GATEWAY_API_KEY",
"api": "openai-completions",
"models": [
{
"id": "moonshotai/kimi-k2.5",
"name": "Kimi K2.5 (Fireworks via Vercel)",
"reasoning": true,
"input": ["text", "image"],
"cost": { "input": 0.6, "output": 3, "cacheRead": 0, "cacheWrite": 0 },
"contextWindow": 262144,
"maxTokens": 262144,
"compat": {
"vercelGatewayRouting": {
"only": ["fireworks", "novita"],
"order": ["fireworks", "novita"]
}
}
}
]
}
}
}