自定义模型
通过 ~/.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 消息发送系统提示。若服务器也不支持 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 示例
Section titled “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
Section titled “支持的 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 配置
Section titled “Provider 配置”| 字段 | 说明 |
|---|---|
baseUrl | API 端点 URL |
api | API 类型(见上文) |
apiKey | API 密钥(见下方值解析) |
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 可用性检查使用已配置的 auth 存在性,不会执行 shell 命令。
自定义请求头
Section titled “自定义请求头”{ "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 | 否 | 全零 | {"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0}(每百万 token) |
compat | 否 | provider 的 compat | Provider 兼容性覆盖。两者均设置时与 provider 级别的 compat 合并。 |
当前行为:
/model、--list-models和交互式页脚按模型id显示条目。- 配置的
name用于模型匹配和次要模型详情文本。它不会替换页脚/状态栏中的模型 id。
思考级别映射
Section titled “思考级别映射”在模型上使用 thinkingLevelMap 描述模型特定的思考控制。键为 pi 思考级别:off、minimal、low、medium、high、xhigh。
值为三态:
| 值 | 含义 |
|---|---|
| 省略 | 支持该级别,使用 provider 的默认映射 |
| string | 支持该级别,向 provider 发送此值 |
null | 不支持该级别,隐藏/跳过/限制 |
仅支持 off、high 和 max 推理的模型示例:
{ "id": "deepseek-v4-pro", "reasoning": true, "thinkingLevelMap": { "minimal": null, "low": null, "medium": null, "high": "high", "xhigh": "max" }}无法禁用思考的模型示例:
{ "id": "always-thinking-model", "reasoning": true, "thinkingLevelMap": { "off": null }}迁移:使用 compat.reasoningEffortMap 的旧配置应将该映射移至模型级别的 thinkingLevelMap。对不应出现在 UI 中的级别使用 null。
覆盖内置 Provider
Section titled “覆盖内置 Provider”通过代理路由内置 provider,无需重新定义模型:
{ "providers": { "anthropic": { "baseUrl": "https://my-proxy.example.com/v1" } }}所有内置 Anthropic 模型仍可用。现有 OAuth 或 API 密钥 auth 继续有效。
要将自定义模型合并到内置 provider,请包含 models 数组:
{ "providers": { "anthropic": { "baseUrl": "https://my-proxy.example.com/v1", "apiKey": "$ANTHROPIC_API_KEY", "api": "anthropic-messages", "models": [...] } }}合并语义:
- 保留内置模型。
- 自定义模型在 provider 内按
idupsert。 - 若自定义模型
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 兼容性
Section titled “Anthropic Messages 兼容性”对于使用 api: "anthropic-messages" 的 provider 或代理,使用 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),而非旧版基于预算的思考 payload。内置模型会自动设置。对于路由到这些模型的自定义 provider 或别名,将 forceAdaptiveThinking 设为 true。
部分 Anthropic 兼容 provider 会发出签名空的思考块,但在重放时仍期望它们存在。仅对这些 provider 将 allowEmptySignature 设为 true;真正的 Anthropic 会拒绝空的思考签名。
{ "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 是否接受每个工具的 eager_input_streaming。默认:true。设为 false 可省略该字段,并在启用工具的请求中使用旧版细粒度工具流 beta 请求头。 |
supportsLongCacheRetention | 当缓存保留为 long 时,provider 是否接受 Anthropic 长缓存保留(cache_control.ttl: "1h")。默认:true。 |
sendSessionAffinityHeaders | 启用缓存时是否从会话 id 发送 x-session-affinity。默认:对已知 provider 自动检测。 |
supportsCacheControlOnTools | provider 是否接受工具定义上的 Anthropic 风格 cache_control 标记。默认:true。 |
forceAdaptiveThinking | 是否为该模型发送自适应思考(thinking.type: "adaptive" 加 output_config.effort)。内置自适应模型会自动设置。默认:false。 |
allowEmptySignature | 是否将空思考签名重放为 signature: "",而非将思考转换为文本。默认:false。 |
OpenAI 兼容性
Section titled “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 | 在工具结果消息中包含 name |
requiresAssistantAfterToolResult | 在工具结果后的 user 消息之前插入 assistant 消息 |
requiresThinkingAsText | 将思考块转换为纯文本 |
requiresReasoningContentOnAssistantMessages | 启用推理时,在所有重放的 assistant 消息中包含空的 reasoning_content |
thinkingFormat | 使用 reasoning_effort、openrouter、deepseek、together、zai、qwen 或 qwen-chat-template 思考参数 |
cacheControlFormat | 在系统提示、最后一个工具定义以及最后一个 user/assistant 文本内容上使用 Anthropic 风格的 cache_control 标记。目前仅支持 anthropic。 |
supportsStrictMode | 在工具定义中包含 strict 字段 |
supportsLongCacheRetention | 当缓存保留为 long 时,provider 是否接受长缓存保留:OpenAI 提示缓存的 prompt_cache_retention: "24h",或当 cacheControlFormat 为 anthropic 时的 cache_control.ttl: "1h"。默认:true。 |
openRouterRouting | OpenRouter provider 路由偏好。此对象原样发送到 OpenRouter API 请求 的 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 的本地 Qwen 兼容服务器,使用 qwen-chat-template。
cacheControlFormat: "anthropic" 适用于通过文本内容和工具定义上的 cache_control 标记暴露 Anthropic 风格提示缓存的 OpenAI 兼容 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"] } } } ] } }}