Pi 官方文档
自定义模型
自定义模型
通过 ~/.pi/agent/models.json 添加自定义 Provider 和模型(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 会忽略它,所以填什么都可以。
有些兼容 OpenAI 的服务端不理解推理模型使用的 developer 角色。对于这类 Provider,把 compat.supportsDeveloperRole 设为 false,这样 pi 会改为把系统提示词作为 system message 发送。如果服务端也不支持 reasoning_effort,也把 compat.supportsReasoningEffort 设为 false。
你可以在 Provider 级别设置 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 AI Studio 示例
使用带 baseUrl 的 google-generative-ai,即可添加来自 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
| API | 说明 |
|---|---|
openai-completions | OpenAI Chat Completions(兼容性最好) |
openai-responses | OpenAI Responses API |
anthropic-messages | Anthropic Messages API |
google-generative-ai | Google Generative AI |
可以在 Provider 级别设置 api(作为所有模型的默认值),也可以在模型级别设置(按模型覆盖)。
Provider 配置
| 字段 | 说明 |
|---|---|
baseUrl | API 端点 URL |
api | API 类型(见上) |
apiKey | API key(见下面的值解析) |
headers | 自定义请求头(见下面的值解析) |
authHeader | 设为 true 时,自动添加 Authorization: Bearer <apiKey> |
models | 模型配置数组 |
modelOverrides | 这个 Provider 上内置模型的按模型覆盖配置 |
值解析
apiKey 和 headers 字段支持命令执行、环境变量插值和字面量:
- 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 | 否 | provider 的 api | 为这个模型覆盖 Provider 的 API |
reasoning | 否 | false | 支持扩展思考 |
thinkingLevelMap | 否 | 省略 | 将 pi 的思考等级映射到 Provider 值,并标记不支持的等级(见下文) |
input | 否 | ["text"] | 输入类型:["text"] 或 ["text", "image"] |
contextWindow | 否 | 128000 | 上下文窗口大小,单位是 token |
maxTokens | 否 | 16384 | 最大输出 token 数 |
cost | 否 | 全为 0 | {\"input\": 0, \"output\": 0, \"cacheRead\": 0, \"cacheWrite\": 0}(按每百万 token 计) |
compat | 否 | provider 的 compat | Provider 兼容性覆盖配置。两者都设置时,会与 Provider 级别的 compat 合并。 |
当前行为:
/model、--list-models和交互式页脚都会按模型id显示条目。- 配置的
name会用于模型匹配和次要模型详情文本。它不会替换页脚/状态栏里的模型 id。
Thinking Level Map
使用 thinkingLevelMap 在模型上描述模型特定的 thinking 控制。键是 pi thinking levels:off、minimal、low、medium、high、xhigh。
取值是三态:
| 取值 | 含义 |
|---|---|
| 省略 | 该级别受支持,并使用 Provider(模型提供方)的默认映射 |
| 字符串 | 该级别受支持,并将这个值发送给 Provider(模型提供方) |
null | 该级别不受支持,会被隐藏、跳过或裁掉 |
仅支持 off、high 和 max 推理的模型示例:
{
"id": "deepseek-v4-pro",
"reasoning": true,
"thinkingLevelMap": {
"minimal": null,
"low": null,
"medium": null,
"high": "high",
"xhigh": "max"
}
}
对于无法关闭 thinking 的模型,示例:
{
"id": "always-thinking-model",
"reasoning": true,
"thinkingLevelMap": {
"off": null
}
}
迁移:旧配置如果使用了 compat.reasoningEffortMap,应把这份映射迁移到模型级别的 thinkingLevelMap。对不应出现在 UI 中的级别,使用 null。
覆盖内置 Providers
通过代理转发内置 Provider(模型提供方),而不用重新定义模型:
{
"providers": {
"anthropic": {
"baseUrl": "https://my-proxy.example.com/v1"
}
}
}
所有内置 Anthropic 模型仍然可用。现有的 OAuth 或 API key 认证继续生效。
如果要把自定义模型合并到内置 Provider(模型提供方)中,请包含 models 数组:
{
"providers": {
"anthropic": {
"baseUrl": "https://my-proxy.example.com/v1",
"apiKey": "$ANTHROPIC_API_KEY",
"api": "anthropic-messages",
"models": [...]
}
}
}
合并语义:
- 保留内置模型。
- 自定义模型会按 Provider(模型提供方)内的
id进行更新或插入。 - 如果自定义模型的
id与内置模型的id相同,自定义模型会替换该内置模型。 - 如果自定义模型的
id是新的,它会与内置模型并列添加。
按模型覆盖
使用 modelOverrides 来定制特定的内置模型,而不用替换 Provider(模型提供方)的完整模型列表。
{
"providers": {
"openrouter": {
"modelOverrides": {
"anthropic/claude-sonnet-4": {
"name": "Claude Sonnet 4 (Bedrock Route)",
"compat": {
"openRouterRouting": {
"only": ["amazon-bedrock"]
}
}
}
}
}
}
}
modelOverrides 支持每个模型的这些字段:name、reasoning、input、cost(部分)、contextWindow、maxTokens、headers、compat。
行为说明:
modelOverrides会应用到内置 Provider(模型提供方)的模型上。- 未知的模型 ID 会被忽略。
- 可以把 Provider(模型提供方)级别的
baseUrl/headers与modelOverrides组合使用。 - 覆盖
name只会改变模型匹配和次级详情文本;页脚和主模型列表仍然显示模型id。 - 如果某个 Provider(模型提供方)也定义了
models,自定义模型会在内置覆盖之后合并。相同id的自定义模型会替换已经被覆盖的内置模型条目。
Anthropic Messages 兼容性
对于使用 api: "anthropic-messages" 的 Provider(模型提供方)或代理,请用 compat 控制 Anthropic 特定的请求兼容性。
默认情况下,pi 会为每个 tool 发送 eager_input_streaming: true。如果代理或兼容 Anthropic 的后端拒绝这个字段,把 supportsEagerToolInputStreaming 设为 false。这样,Pi 会省略 tools[].eager_input_streaming,并在启用 tool 的请求中改发旧版 fine-grained-tool-streaming-2025-05-14 beta header。
有些 Anthropic 模型需要自适应 thinking(thinking.type: "adaptive" 加上 output_config.effort),而不是旧的基于 budget 的 thinking payload。内置模型会自动设置这一点。对于路由到这些模型的自定义 Provider(模型提供方)或别名,请将 forceAdaptiveThinking 设为 true。
有些兼容 Anthropic 的 Provider(模型提供方)会输出带空签名的 thinking block,但在回放时仍然期望保留这些签名。只有这些 Provider(模型提供方)才把 allowEmptySignature 设为 true;真正的 Anthropic 会拒绝空的 thinking 签名。
{
"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 | Provider(模型提供方)是否接受按 tool 设置的 eager_input_streaming。默认:true。设为 false 后,会省略这个字段,并在启用 tool 的请求中使用旧版 fine-grained tool streaming beta header。 |
supportsLongCacheRetention | Provider(模型提供方)是否接受 Anthropic 的长缓存保留(cache_control.ttl: "1h"),前提是 cache retention 为 long。默认:true。 |
sendSessionAffinityHeaders | 在启用缓存时,是否根据 session id 发送 x-session-affinity。默认:针对已知 Provider(模型提供方)自动检测。 |
supportsCacheControlOnTools | Provider(模型提供方)是否接受 Anthropic 风格的 cache_control 标记用于 tool 定义。默认:true。 |
forceAdaptiveThinking | 是否为这个模型发送自适应 thinking(thinking.type: "adaptive" 加上 output_config.effort)。内置的自适应模型会自动设置这一点。默认:false。 |
allowEmptySignature | 回放空的 thinking 签名时,是否保留为 signature: "",而不是把 thinking 转成文本。默认:false。 |
OpenAI 兼容性
对于具备部分 OpenAI 兼容性的 Provider(模型提供方),请使用 compat 字段。
- Provider 级别的
compat会把默认值应用到该 Provider 下的所有模型。 - 模型级别的
compat会覆盖该模型的 Provider 级别值。
{
"providers": {
"local-llm": {
"baseUrl": "http://localhost:8080/v1",
"api": "openai-completions",
"compat": {
"supportsUsageInStreaming": false,
"maxTokensField": "max_tokens"
},
"models": [...]
}
}
}
| 字段 | 说明 |
|---|---|
supportsStore | Provider 支持 store 字段 |
supportsDeveloperRole | 使用 developer 还是 system 角色 |
supportsReasoningEffort | 支持 reasoning_effort 参数 |
supportsUsageInStreaming | 支持 stream_options: { include_usage: true }(默认:true) |
maxTokensField | 使用 max_completion_tokens 还是 max_tokens |
requiresToolResultName | 在 tool result 消息中包含 name |
requiresAssistantAfterToolResult | 在 tool results 之后、下一条 user 消息之前插入一条 assistant 消息 |
requiresThinkingAsText | 将 thinking blocks 转成纯文本 |
requiresReasoningContentOnAssistantMessages | 当启用 reasoning 时,在所有回放的 assistant 消息中包含空的 reasoning_content |
thinkingFormat | 使用 reasoning_effort、openrouter、deepseek、together、zai、qwen、chat-template 或 qwen-chat-template 的 thinking 参数 |
chatTemplateKwargs | 当 thinkingFormat: "chat-template" 时使用的 chat_template_kwargs 值;对于由 pi 控制的 thinking 值,使用 { "$var": "thinking.enabled" } 或 { "$var": "thinking.effort" } |
cacheControlFormat | 在 system prompt、最后一个 tool 定义以及最后一段 user/assistant 文本内容上使用 Anthropic 风格的 cache_control 标记。目前只支持 anthropic。 |
supportsStrictMode | 在 tool 定义中包含 strict 字段 |
supportsLongCacheRetention | 当 cache retention 为 long 时,Provider(模型提供方)是否接受长缓存保留:OpenAI prompt caching 使用 prompt_cache_retention: "24h",cacheControlFormat 为 anthropic 时使用 cache_control.ttl: "1h"。默认:true。 |
openRouterRouting | OpenRouter Provider(模型提供方)路由偏好。这个对象会原样放入 OpenRouter API request 的 provider 字段。 |
vercelGatewayRouting | 用于 Provider(模型提供方)选择的 Vercel AI Gateway 路由配置(only、order) |
openrouter 使用 reasoning: { effort }。together 使用 reasoning: { enabled },并且在启用 supportsReasoningEffort 时也会使用 reasoning_effort。qwen 使用顶层的 enable_thinking。对于需要 chat_template_kwargs.enable_thinking 和 preserve_thinking 的本地 Qwen 兼容服务器,请使用 qwen-chat-template。对于需要可配置 chat_template_kwargs 的 vLLM/Hugging Face chat templates,请使用 chat-template,例如 DeepSeek V3.x templates 的 chatTemplateKwargs: { "thinking": { "$var": "thinking.enabled" } }。
cacheControlFormat: "anthropic" 适用于那些与 OpenAI 兼容、并通过文本内容和 tool 定义上的 cache_control 标记暴露 Anthropic 风格 prompt caching 的 Provider(模型提供方)。
示例:
{
"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"]
}
}
}
]
}
}
}