跳转到内容

自定义模型

通过 ~/.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 时文件会重新加载。可在会话中编辑,无需重启。

使用带 baseUrlgoogle-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说明
openai-completionsOpenAI Chat Completions(兼容性最广)
openai-responsesOpenAI Responses API
anthropic-messagesAnthropic Messages API
google-generative-aiGoogle Generative AI

在 provider 级别设置 api(作为所有模型的默认值),或在模型级别设置(按模型覆盖)。

字段说明
baseUrlAPI 端点 URL
apiAPI 类型(见上文)
apiKeyAPI 密钥(见下方值解析)
headers自定义请求头(见下方值解析)
authHeader设为 true 可自动添加 Authorization: Bearer <apiKey>
models模型配置数组
modelOverrides该 provider 上内置模型的按模型覆盖

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 可用性检查使用已配置的 auth 存在性,不会执行 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)
nameid人类可读的模型标签。用于匹配(--model 模式)并作为次要模型详情文本显示。
apiprovider 的 api覆盖该模型的 provider API
reasoningfalse支持扩展思考
thinkingLevelMap省略将 pi 思考级别映射到 provider 值,并标记不支持的级别(见下文)
input["text"]输入类型:["text"]["text", "image"]
contextWindow128000上下文窗口大小(token)
maxTokens16384最大输出 token 数
cost全零{"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0}(每百万 token)
compatprovider 的 compatProvider 兼容性覆盖。两者均设置时与 provider 级别的 compat 合并。

当前行为:

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

在模型上使用 thinkingLevelMap 描述模型特定的思考控制。键为 pi 思考级别:offminimallowmediumhighxhigh

值为三态:

含义
省略支持该级别,使用 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,无需重新定义模型:

{
"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 内按 id upsert。
  • 若自定义模型 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 每个模型支持以下字段:namereasoninginputcost(部分)、contextWindowmaxTokensheaderscompat

行为说明:

  • modelOverrides 应用于内置 provider 模型。
  • 未知模型 ID 会被忽略。
  • 可将 provider 级别的 baseUrl/headersmodelOverrides 组合使用。
  • 覆盖 name 仅更改模型匹配和次要详情文本;页脚和主模型列表仍显示模型 id
  • 若 provider 同时定义了 models,自定义模型在内置覆盖之后合并。相同 id 的自定义模型会替换已覆盖的内置模型条目。

对于使用 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"]
}
]
}
}
}
字段说明
supportsEagerToolInputStreamingprovider 是否接受每个工具的 eager_input_streaming。默认:true。设为 false 可省略该字段,并在启用工具的请求中使用旧版细粒度工具流 beta 请求头。
supportsLongCacheRetention当缓存保留为 long 时,provider 是否接受 Anthropic 长缓存保留(cache_control.ttl: "1h")。默认:true
sendSessionAffinityHeaders启用缓存时是否从会话 id 发送 x-session-affinity。默认:对已知 provider 自动检测。
supportsCacheControlOnToolsprovider 是否接受工具定义上的 Anthropic 风格 cache_control 标记。默认:true
forceAdaptiveThinking是否为该模型发送自适应思考(thinking.type: "adaptive"output_config.effort)。内置自适应模型会自动设置。默认:false
allowEmptySignature是否将空思考签名重放为 signature: "",而非将思考转换为文本。默认:false

对于部分 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": [...]
}
}
}
字段说明
supportsStoreProvider 支持 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_effortopenrouterdeepseektogetherzaiqwenqwen-chat-template 思考参数
cacheControlFormat在系统提示、最后一个工具定义以及最后一个 user/assistant 文本内容上使用 Anthropic 风格的 cache_control 标记。目前仅支持 anthropic
supportsStrictMode在工具定义中包含 strict 字段
supportsLongCacheRetention当缓存保留为 long 时,provider 是否接受长缓存保留:OpenAI 提示缓存的 prompt_cache_retention: "24h",或当 cacheControlFormatanthropic 时的 cache_control.ttl: "1h"。默认:true
openRouterRoutingOpenRouter provider 路由偏好。此对象原样发送到 OpenRouter API 请求provider 字段。
vercelGatewayRouting用于 provider 选择的 Vercel AI Gateway 路由配置(onlyorder

openrouter 使用 reasoning: { effort }together 使用 reasoning: { enabled },并在启用 supportsReasoningEffort 时也使用 reasoning_effortqwen 使用顶层 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"]
}
}
}
]
}
}
}