Final rebrand: rename remaining Rust source file to complete the gsd → forge transition. All parser references already use forge_parser after earlier commits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
12 KiB
自定义模型
通过 ~/.sf/agent/models.json 添加自定义 providers 和 models(Ollama、vLLM、LM Studio、代理等)。
目录
最小示例
对于本地 models(Ollama、LM Studio、vLLM),每个 model 只要求提供 id:
{
"providers": {
"ollama": {
"baseUrl": "http://localhost:11434/v1",
"api": "openai-completions",
"apiKey": "ollama",
"models": [
{ "id": "llama3.1:8b" },
{ "id": "qwen2.5-coder:7b" }
]
}
}
}
apiKey 在 schema 中是必填,但 Ollama 会忽略它,因此任意值都可以。
有些 OpenAI-compatible server 不支持推理模型使用的 developer role。对于这类 provider,需要把 compat.supportsDeveloperRole 设为 false,这样 SF 会改用 system message 发送 system prompt。如果该 server 同时也不支持 reasoning_effort,还应把 compat.supportsReasoningEffort 也设为 false。
你可以在 provider 级别设置 compat,让它应用到该 provider 下的所有 models;也可以在 model 级别单独覆盖某个 model。这个设置常见于 Ollama、vLLM、SGLang 以及类似的 OpenAI-compatible server。
{
"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 时,这个文件都会重新加载。可以在会话过程中直接编辑,无需重启。
支持的 API
| API | 说明 |
|---|---|
openai-completions |
OpenAI Chat Completions(兼容性最好) |
openai-responses |
OpenAI Responses API |
anthropic-messages |
Anthropic Messages API |
google-generative-ai |
Google Generative AI |
api 可以设置在 provider 级别(作为该 provider 下所有 models 的默认值),也可以设置在 model 级别(覆盖单个 model)。
Provider 配置
| 字段 | 说明 |
|---|---|
baseUrl |
API endpoint URL |
api |
API 类型(见上) |
apiKey |
API key(见下方值解析) |
headers |
自定义请求头(见下方值解析) |
authHeader |
设为 true 时,自动添加 Authorization: Bearer <apiKey> |
models |
model 配置数组 |
modelOverrides |
针对该 provider 的内置 models 做按 model 覆盖 |
值解析
apiKey 和 headers 支持三种写法:
- Shell 命令:
"!command",执行后读取 stdout"apiKey": "!security find-generic-password -ws 'anthropic'" "apiKey": "!op read 'op://vault/item/credential'" - 环境变量: 取对应环境变量的值
"apiKey": "MY_API_KEY" - 字面量: 直接使用
"apiKey": "sk-..."
命令允许列表
Shell 命令(!command)只能执行一组已知的凭据工具。只有以下前缀开头的命令才会被允许:
pass、op、aws、gcloud、vault、security、gpg、bw、gopass、lpass
不在列表中的命令会被阻止,最终该值会解析为 undefined。同时会向 stderr 输出一条警告。
为了防止注入,命令参数中的 shell 操作符(;、|、&、`、$、>、<)同样会被阻止。
自定义允许列表:
如果你使用的凭据工具不在默认列表中,可以在全局设置(~/.sf/agent/settings.json)里覆盖:
{
"allowedCommandPrefixes": ["pass", "op", "sops", "doppler", "mycli"]
}
这会完全替换默认列表,因此如果你还想保留默认命令,需要一起写进去。
你也可以设置 SF_ALLOWED_COMMAND_PREFIXES 环境变量(逗号分隔)。环境变量优先级高于 settings.json:
export SF_ALLOWED_COMMAND_PREFIXES="pass,op,sops,doppler"
注意: 这是一个仅全局生效的设置。项目级 settings.json(
<project>/.sf/settings.json)不能覆盖命令 allowlist,以防克隆下来的仓库提升命令执行权限。
自定义 Headers
{
"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": [...]
}
}
}
Model 配置
| 字段 | 必填 | 默认值 | 说明 |
|---|---|---|---|
id |
是 | — | Model 标识符(会原样传给 API) |
name |
否 | id |
可读的 model 标签,用于匹配(例如 --model 模糊匹配)并显示在详情 / 状态文字里 |
api |
否 | provider 的 api |
为这个 model 覆盖 provider 的 API 类型 |
reasoning |
否 | false |
是否支持扩展 thinking |
input |
否 | ["text"] |
输入类型:["text"] 或 ["text", "image"] |
contextWindow |
否 | 128000 |
上下文窗口大小(tokens) |
maxTokens |
否 | 16384 |
最大输出 tokens |
cost |
否 | 全为 0 | {"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0}(每百万 tokens) |
compat |
否 | provider 的 compat |
OpenAI 兼容性覆盖项。如果 provider 和 model 两边都配置了,会合并 |
当前行为:
/model与--list-models都是按 modelid列出条目- 配置里的
name会用于 model 匹配,以及详情 / 状态文本展示
覆盖内置 Providers
如果你想把某个内置 provider 经由代理路由出去,但又不想重新定义全部 models,可以这样写:
{
"providers": {
"anthropic": {
"baseUrl": "https://my-proxy.example.com/v1"
}
}
}
这样所有内置 Anthropic models 仍然可用。已有的 OAuth 或 API key 认证也会继续生效。
如果你想把自定义 models 合并进某个内置 provider,就同时提供 models 数组:
{
"providers": {
"anthropic": {
"baseUrl": "https://my-proxy.example.com/v1",
"apiKey": "ANTHROPIC_API_KEY",
"api": "anthropic-messages",
"models": [...]
}
}
}
合并规则如下:
- 内置 models 会保留
- 自定义 models 会按
id在该 provider 下执行 upsert - 如果某个自定义 model 的
id与内置 model 相同,自定义 model 会替换那个内置 model - 如果某个自定义 model 的
id是新的,它会作为新增条目并列出现
按 model 覆盖
如果你只想修改某些特定的内置 model,而不想替换整个 provider 的 model 列表,可以使用 modelOverrides。
{
"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 的 models 上- 未知的 model ID 会被忽略
- 可以把 provider 级别的
baseUrl/headers与modelOverrides组合使用 - 如果某个 provider 同时定义了
models,那么自定义 models 会在应用完内置覆盖后再合并;如果它的id与已覆盖的内置 model 相同,最终会以自定义 model 为准
OpenAI 兼容性
对于只部分兼容 OpenAI 的 providers,可通过 compat 字段修正行为。
- provider 级别的
compat会作为该 provider 下所有 models 的默认值 - model 级别的
compat会覆盖该 model 的 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 role |
supportsReasoningEffort |
是否支持 reasoning_effort 参数 |
reasoningEffortMap |
把 SF 的 thinking levels 映射到 provider 专属 reasoning_effort 值 |
supportsUsageInStreaming |
是否支持 stream_options: { include_usage: true }(默认 true) |
maxTokensField |
使用 max_completion_tokens 还是 max_tokens |
requiresToolResultName |
tool result message 中是否必须包含 name |
requiresAssistantAfterToolResult |
tool result 之后、user message 之前是否需要插入 assistant message |
requiresThinkingAsText |
是否把 thinking block 转成纯文本 |
thinkingFormat |
使用 reasoning_effort、zai、qwen 或 qwen-chat-template 的 thinking 参数格式 |
supportsStrictMode |
是否在 tool definitions 中包含 strict 字段 |
openRouterRouting |
传给 OpenRouter 的路由配置,用于 model/provider 选择 |
vercelGatewayRouting |
Vercel AI Gateway 的路由配置,用于 provider 选择(only、order) |
qwen 使用顶层 enable_thinking。对于要求 chat_template_kwargs.enable_thinking 的本地 Qwen-compatible server,请使用 qwen-chat-template。
示例:
{
"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": {
"order": ["anthropic"],
"fallbacks": ["openai"]
}
}
}
]
}
}
}
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"]
}
}
}
]
}
}
}