跳转到内容

自定义模型

通过 ~/.pi/agent/models.json 添加自定义模型提供方和模型(Ollama、vLLM、LM Studio、代理等)。

对于本地模型(Ollama、LM Studio、vLLM),每个模型只需提供 id

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

apiKey 的值只是一个占位符,因为 Ollama 会忽略它。Pi 仍会将这些模型视为需要身份验证才会在 /model 中显示,因此无密钥的本地服务器应保留一个占位值,通过 /login 为该模型提供方保存密钥,或在选择模型时传入 --api-key

部分兼容 OpenAI 的服务器无法理解用于推理型模型的 developer 角色。对于这些模型提供方,请将 compat.supportsDeveloperRole 设为 false,这样 Pi 就会改用 system 消息发送系统提示词。如果服务器同样不支持 reasoning_effort,也请将 compat.supportsReasoningEffort 设为 false

compat 可在模型提供方级别设置以应用于所有模型,也可在模型级别设置以覆盖特定模型。这一般适用于 Ollama、vLLM、SGLang 等兼容 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
}
]
}
}
}

当需要特定值时,覆盖默认配置:

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

每次打开 /model 时该文件都会重新加载。可在会话进行中编辑,无需重启。

google-generative-aibaseUrl 搭配使用,即可添加来自 Google AI Studio 的模型,包括自定义 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
}
]
}
}
}

google-generative-ai API 类型添加自定义模型时,baseUrl 是必需的。

API 说明
openai-completions OpenAI 聊天补全(Chat Completions,兼容性最好)
openai-responses OpenAI Responses API
anthropic-messages Anthropic Messages API
google-generative-ai Google 生成式 AI(Generative AI)

api 可在模型提供方级别设置(作为所有模型的默认值),也可在模型级别设置(覆盖单个模型)。

字段 说明
baseUrl API 端点 URL
api API 类型(见上文)
apiKey 可选的 API 密钥配置(见下文的值解析)。当身份验证由 /login/auth.json 或 CLI 的 --api-key 提供时,可省略此项。
oauth 动态 OAuth 模型提供方类型。目前支持 "radius";需要网关的 baseUrl
headers 自定义请求头(见下文的值解析)
authHeader 设为 true 可自动添加 Authorization: Bearer <apiKey>
models 模型配置数组
modelOverrides 针对该模型提供方上的内置模型或扩展注册模型的按模型覆盖

对于定义了 models 的模型提供方,非内置的提供方配置需要在提供方级别或模型级别提供 baseUrl 和一个 api 值。加载文件并不要求 apiKey:当通过 /login/auth.json、CLI 的 --api-key 或提供方 apiKey 配置了身份验证后,模型即可用。如果未配置身份验证,模型会加载,但不会在 /model--list-models 中可用。

apiKeyheaders 字段支持命令执行、环境变量插值和字面量:

  • Shell 命令:"!command" 开头的值会被整体作为命令执行,并使用其标准输出(stdout)
    "apiKey": "!security find-generic-password -ws 'anthropic'"
    "apiKey": "!op read 'op://vault/item/credential'"
  • 环境变量插值: "$ENV_VAR""${ENV_VAR}" 会使用指定环境变量的值。插值在更大的字面量内部同样有效。
    "apiKey": "$MY_API_KEY"
    "apiKey": "${KEY_PREFIX}_${KEY_SUFFIX}"
    $FOO_BAR 表示环境变量 FOO_BAR;当 BAR 是字面文本时应使用 ${FOO}_BAR。缺失的环境变量会使该值无法解析。
  • 转义: "$$" 会输出字面的 "$""$!" 会输出字面的 "!",而不会触发命令执行。
    "apiKey": "$$literal-dollar-prefix"
    "apiKey": "$!literal-bang-prefix"
  • 字面量: 直接使用。像 MY_API_KEY 这样的大写字符串是字面量;如需引用环境变量,请使用 $MY_API_KEY
    "apiKey": "sk-..."

对于 models.json,Shell 命令在发起请求时才解析。Pi 有意不对任意命令应用内置的 TTL、过期值复用或恢复逻辑。不同的命令需要不同的缓存和失败策略,Pi 无法推断出合适的策略。

如果你的命令速度慢、开销大、受限流影响,或希望在临时失败时继续使用上一次的值,请将其包装到你自己的脚本或命令中,自行实现所需的缓存或 TTL 行为。

/model 的可用性检查基于已配置的身份验证状态,不会执行 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": [...]
}
}
}
字段 是否必需 默认值 说明
id 模型标识符(传递给 API)
name id 人类可读的模型名称。用于匹配(--model 模式),并显示为次要的模型详情文本。
api 提供方的 api 覆盖该模型的提供方 API
reasoning false 支持扩展思考
thinkingLevelMap 省略 将 Pi 的思考级别映射到提供方取值,并标记不支持的级别(见下文)
input ["text"] 输入类型:["text"]["text", "image"]
contextWindow 128000 上下文窗口大小(以 token 计)
maxTokens 16384 最大输出 token 数
samplingParams 省略 逐字合并进每个请求体的采样参数(见下文)
cost 全为零 每百万 token 的费率,可带可选的全请求输入定价档位
compat 提供方的 compat 模型提供方兼容性覆盖。当两者同时设置时,与提供方级别的 compat 合并。

成本档位提供一套完整的备选费率,当总输入用量(input + cacheRead + cacheWrite)超过 inputTokensAbove 时应用于整个请求。多个档位同时匹配时,阈值最高的档位生效。

{
"cost": {
"input": 5,
"output": 30,
"cacheRead": 0.5,
"cacheWrite": 6.25,
"tiers": [
{
"inputTokensAbove": 272000,
"input": 10,
"output": 45,
"cacheRead": 1,
"cacheWrite": 12.5
}
]
}
}

当前行为:

  • /model--list-models 和交互式页脚按模型的 id 显示条目。
  • 配置的 name 用于模型匹配和次要的模型详情文本。它不会替换页脚/状态栏中的模型 id。

samplingParams 是一个自由格式对象,在 Pi 自行设置的字段之后逐字合并进该模型的每个请求体,因此其中的键具有更高优先级。用它来发送 Pi 尚未建模的采样参数——包括服务器特有的参数,如 llama.cpp 的 min_p 或 vLLM 的 top_k

{
"id": "deepseek-v4-flash",
"samplingParams": {
"temperature": 1.0,
"top_p": 0.95,
"top_k": 0,
"min_p": 0.0
}
}

只有兼容 OpenAI 的 API 会应用该对象(openai-completionsopenai-responsesazure-openai-responses);其他 API 会忽略它。其中的键会覆盖 Pi 的具名请求字段(例如这里的 temperature 键优先于请求级别的 temperature),因此建议将其作为模型采样参数的唯一来源。在 modelOverrides 中,samplingParams 按键与基础模型的取值合并。

在模型上使用 thinkingLevelMap 来描述模型特有的思考控制。键为 Pi 的思考级别:offminimallowmediumhighxhighmax。映射中允许有空洞;例如,模型可以暴露 highmax,而不暴露 xhigh

取值为三态:

取值 含义
省略 high 及以下的标准级别使用提供方的默认映射;扩展的 xhighmax 级别不受支持
字符串 该级别受支持,此值会发送给模型提供方
null 该级别不受支持,会被隐藏/跳过/钳制掉

仅支持 off、high、max 推理级别的模型示例:

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

无法禁用思考的模型示例:

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

迁移:旧配置中使用的 compat.reasoningEffortMap 应迁移为模型级别的 thinkingLevelMap。不希望出现在界面中的级别请使用 null

通过代理路由内置模型提供方,而无需重新定义模型:

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

所有内置 Anthropic 模型仍然可用。现有的 OAuth 或 API 密钥身份验证继续有效。

要将自定义模型并入内置模型提供方,请包含 models 数组:

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

合并语义:

  • 保留内置模型。
  • 自定义模型按 id 在该模型提供方内执行 upsert(存在则更新,不存在则新增)。
  • 如果自定义模型的 id 与内置模型的 id 相同,则自定义模型替换该内置模型。
  • 如果自定义模型的 id 是新的,则将其添加到内置模型旁边。

使用 modelOverrides 自定义内置模型以及匹配的扩展注册模型,而无需替换该模型提供方的完整模型列表。

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

modelOverrides 对每个模型支持以下字段:namereasoningthinkingLevelMapinputcost(部分)、contextWindowmaxTokenssamplingParams(按键合并)、headerscompat

OpenAI 直连的 GPT-5.6 Sol、Terra 和 Luna 默认使用 272000 的上下文窗口,以确保请求保持在 OpenAI 的短上下文定价档位内。若要使用 OpenAI 的 1.05M 上下文窗口,请为你使用的每个模型增大该值:

{
"providers": {
"openai": {
"modelOverrides": {
"gpt-5.6-sol": {
"contextWindow": 1050000
}
}
}
}
}

该覆盖会保留内置的定价元数据。总输入 token 超过 272K 的请求将按 GPT-5.6 的长上下文费率对整个请求计费。需要时,可对 gpt-5.6-terragpt-5.6-luna 应用相同的覆盖。

行为说明:

  • modelOverrides 应用于内置模型提供方的模型,以及匹配的扩展注册模型。
  • 未知的模型 ID 会被忽略。
  • 可将提供方级别的 baseUrl/headersmodelOverrides 结合使用。
  • 覆盖 name 只会改变模型匹配和次要的详情文本;页脚和主模型列表仍显示模型 id
  • 如果某模型提供方也定义了 models,自定义模型会在内置覆盖之后合并。同 id 的自定义模型会替换已被覆盖的内置模型条目。

对于使用 api: "anthropic-messages" 的模型提供方或代理,请使用 compat 控制 Anthropic 特有的请求兼容性。

默认情况下,Pi 会为每个工具发送 eager_input_streaming: true。如果代理或兼容 Anthropic 的后端拒绝该字段,请将 supportsEagerToolInputStreaming 设为 false。Pi 将省略 tools[].eager_input_streaming,并在启用工具的请求中改发旧的 fine-grained-tool-streaming-2025-05-14 beta 请求头。

部分 Anthropic 模型需要自适应思考(thinking.type: "adaptive" 加上 output_config.effort),而不是旧的基于预算的思考载荷。内置模型会自动设置此项。对于路由到这些模型的自定义模型提供方或别名,请将 forceAdaptiveThinking 设为 true

部分兼容 Anthropic 的模型提供方会输出签名(signature)为空的思考块,并在重放时仍期望这些空签名。仅针对这些模型提供方将 allowEmptySignature 设为 true;真正的 Anthropic 会拒绝空思考签名。

内置 Anthropic 模型在其模型元数据中启用了 supportsStrictTools。当自定义的兼容 Anthropic 模型端点接受严格的 JSON-schema 工具定义时,必须将其设为 true

{
"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"]
}
]
}
}
}
字段 说明
supportsEagerToolInputStreaming 模型提供方是否接受每工具的 eager_input_streaming。默认:true。设为 false 可省略该字段,并在启用工具的请求中使用旧的细粒度工具流式传输(fine-grained tool streaming)beta 请求头。
supportsLongCacheRetention 当缓存保留策略为 long 时,模型提供方是否接受 Anthropic 长缓存保留(cache_control.ttl: "1h")。默认:true
sendSessionAffinityHeaders 启用缓存时,是否从会话 id 发送 x-session-affinity。默认:对已知模型提供方自动检测。
supportsCacheControlOnTools 模型提供方是否接受工具定义上的 Anthropic 风格 cache_control 标记。默认:true
forceAdaptiveThinking 是否对该模型发送自适应思考(thinking.type: "adaptive" 加上 output_config.effort)。内置的自适应模型会自动设置此项。默认:false
allowEmptySignature 是否将空思考签名重放为 signature: "",而不是把思考转换为文本。默认:false
supportsStrictTools 模型提供方是否接受严格的 JSON-schema 工具定义。默认:false;内置 Anthropic 模型在生成的元数据中会启用此项。

对于部分兼容 OpenAI 的模型提供方,请使用 compat 字段。

  • 提供方级别的 compat 为该提供方下的所有模型应用默认值。
  • 模型级别的 compat 覆盖该模型的提供方级别取值。
{
"providers": {
"local-llm": {
"baseUrl": "http://localhost:8080/v1",
"api": "openai-completions",
"compat": {
"supportsUsageInStreaming": false,
"maxTokensField": "max_tokens"
},
"models": [...]
}
}
}
字段 说明
supportsStore 模型提供方是否支持 store 字段
supportsDeveloperRole 使用 developer 还是 system 角色
supportsReasoningEffort 是否支持 reasoning_effort 参数
supportsUsageInStreaming 是否支持 stream_options: { include_usage: true }(默认:true
supportsFinishReason 流式响应是否包含 finish_reason。设为 false 时,Pi 会在流结束时推断 stoptoolUse。默认:true
maxTokensField 使用 max_completion_tokens 还是 max_tokens
requiresToolResultName 工具结果消息中是否包含 name
requiresAssistantAfterToolResult 工具结果后、用户消息前是否插入一条助手消息
requiresThinkingAsText 是否将思考块转换为纯文本
requiresReasoningContentOnAssistantMessages 启用推理时,是否在所有重放的助手消息中包含空的 reasoning_content
thinkingFormat 使用 reasoning_effortopenrouterdeepseektogetherbasetenzaiqwenchat-templateqwen-chat-template 思考参数
chatTemplateKwargs thinkingFormat: "chat-template" 时使用的 chat_template_kwargs 取值;对于 Pi 控制的思考取值,使用 { "$var": "thinking.enabled" }{ "$var": "thinking.effort" }
chatTemplateArgs thinkingFormat: "baseten" 时使用的 chat_template_args 取值;对于 Pi 控制的思考取值,使用 { "$var": "thinking.enabled" }{ "$var": "thinking.effort" }
cacheControlFormat 在系统提示词、最后一个工具定义以及最后一条用户/助手/工具结果文本内容上使用 Anthropic 风格的 cache_control 标记。目前仅支持 anthropic
sendSessionAffinityHeaders 对于 openai-completions,启用缓存时从会话 id 发送会话亲和性(session-affinity)请求头。默认:false
sessionAffinityFormat 对于 openai-completionsopenai-responses,会话亲和性请求头格式:openai 发送 session_id/x-client-request-id(completions 还发送 x-session-affinity),openai-nosession 省略含下划线的 session_id 请求头,openrouter 发送 x-session-id。不影响 prompt_cache_key 请求体参数。默认:自动检测。
supportsStrictMode 模型提供方是否接受严格的 JSON-schema 函数工具定义。默认值取决于 API;内置 OpenAI 模型带有显式的能力元数据。
supportsOpenAIGrammarTools 兼容 OpenAI 的 API 是否输出自定义 Lark/regex 语法工具。设为 false 时,受语法约束的工具回退为普通函数工具。默认:false;内置模型目录会在 OpenAI、OpenAI Codex、Azure OpenAI、GitHub Copilot、opencode 和 Cloudflare AI Gateway 上为 GPT-5+ 模型启用此项。
deferredToolsMode 使用提供方特有的延迟工具序列化。目前仅对 Kimi 的 OpenAI 兼容 Chat Completions 格式支持 "kimi"
supportsLongCacheRetention 当缓存保留策略为 long 时,模型提供方是否接受长缓存保留:OpenAI 提示缓存使用 prompt_cache_retention: "24h",或当 cacheControlFormatanthropic 时使用 cache_control.ttl: "1h"。默认:true
openRouterRouting OpenRouter 模型提供方的路由偏好。该对象会原样发送到 OpenRouter API 请求provider 字段中。
vercelGatewayRouting 用于模型提供方选择(onlyorder)的 Vercel AI Gateway 路由配置

openrouter 使用 reasoning: { effort }together 使用 reasoning: { enabled },并在启用 supportsReasoningEffort 时同时使用 reasoning_effortqwen 使用顶层的 enable_thinking。对于需要 chat_template_kwargs.enable_thinkingpreserve_thinking 的本地兼容 Qwen 服务器,请使用 qwen-chat-template。对于需要可配置 chat_template_kwargs 的 vLLM/Hugging Face 聊天模板,请使用 chat-template,例如 DeepSeek V3.x 模板使用 chatTemplateKwargs: { "thinking": { "$var": "thinking.enabled" } }。对于通过 chat_template_args 暴露开关控制、并可选地支持顶层 reasoning_effort 的模型提供方,请将 thinkingFormat 设为 "baseten" 并配合 chatTemplateArgs

cacheControlFormat: "anthropic" 用于在文本内容和工具定义上通过 cache_control 标记暴露 Anthropic 风格提示缓存的兼容 OpenAI 的模型提供方。

示例:

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

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"]
}
}
}
]
}
}
}