2026-02-09 15:50:20 +08:00
import { Fragment } from 'react'
2026-04-27 20:12:25 +08:00
import { Link } from 'wouter'
2026-02-09 15:50:20 +08:00
2025-12-01 20:11:12 +08:00
import CodeEditor from '@/components/CodeEditor'
2026-02-27 19:46:44 +08:00
import { Heading } from '@/components/Heading'
2026-01-30 16:47:23 +08:00
import { useLanguage } from '@/i18n/context'
2025-12-01 20:11:12 +08:00
2026-02-06 18:49:06 +08:00
const BASELINE = new Set ([
'gpt-5.1' ,
'claude-haiku-4.5' ,
'gemini-3-flash' ,
2026-04-27 20:49:17 +08:00
'deepseek-v4-flash' ,
2026-02-27 18:47:02 +08:00
'qwen3.5-plus' ,
2026-03-05 16:10:03 +08:00
'qwen3.5-flash' ,
2026-02-06 18:49:06 +08:00
])
// Models grouped by brand, newest first
const MODEL_GROUPS : Record < string , string [] > = {
2026-02-27 18:47:02 +08:00
Qwen : [
2026-04-20 21:28:49 +08:00
'qwen3.6-max' ,
2026-04-02 15:30:58 +08:00
'qwen3.6-plus' ,
2026-04-20 21:28:49 +08:00
'qwen3.6-flash' ,
2026-02-27 18:47:02 +08:00
'qwen3.5-plus' ,
'qwen3.5-flash' ,
'qwen3-coder-next' ,
'qwen-3-max' ,
'qwen-3-plus' ,
],
2026-03-09 22:31:35 +08:00
OpenAI : [ 'gpt-5.4' , 'gpt-5.2' , 'gpt-5.1' , 'gpt-5' , 'gpt-5-mini' , 'gpt-4.1' , 'gpt-4.1-mini' ],
2026-04-27 20:42:17 +08:00
DeepSeek : [ 'deepseek-v4-pro' , 'deepseek-v4-flash' , 'deepseek-3.2' ],
2026-02-27 18:47:02 +08:00
Google : [ 'gemini-3-pro' , 'gemini-3-flash' , 'gemini-2.5' ],
2026-02-09 15:50:20 +08:00
Anthropic : [
'claude-opus-4.6' ,
'claude-opus-4.5' ,
'claude-sonnet-4.5' ,
'claude-haiku-4.5' ,
'claude-sonnet-3.5' ,
],
2026-03-18 02:12:58 -05:00
MiniMax : [ 'MiniMax-M2.7' , 'MiniMax-M2.7-highspeed' , 'MiniMax-M2.5' , 'MiniMax-M2.5-highspeed' ],
2026-03-22 03:21:33 +08:00
xAI : [ 'grok-4.1-fast' , 'grok-4' , 'grok-code-fast' ],
2026-02-09 15:50:20 +08:00
MoonshotAI : [ 'kimi-k2.5' ],
2026-02-14 17:41:14 +08:00
'Z.AI' : [ 'glm-5' , 'glm-4.7' ],
2025-12-22 19:41:47 +08:00
}
2026-02-06 18:49:06 +08:00
const ModelBadge = ({ model , baseline } : { model : string ; baseline? : boolean }) => (
< div
2026-02-09 15:50:20 +08:00
className = { `px-3 py-1.5 rounded-md text-xs font-medium font-mono transition-colors ${
2026-02-06 18:49:06 +08:00
baseline
? 'bg-emerald-500 text-white shadow-sm'
: 'bg-white/80 dark:bg-gray-800/80 text-gray-800 dark:text-gray-200 border border-gray-300 dark:border-gray-600'
} ` }
>
{ model }
{ baseline && < span className = "ml-1" > ⭐ </ span >}
</ div >
)
2026-01-13 15:17:43 +08:00
export default function Models() {
2026-01-30 16:47:23 +08:00
const { isZh } = useLanguage ()
2025-10-22 22:35:25 +08:00
2025-09-29 16:33:15 +08:00
return (
2025-12-22 19:41:47 +08:00
< div className = "max-w-4xl" >
2026-01-30 15:13:31 +08:00
< h1 className = "text-4xl font-bold mb-4" >{ isZh ? '模型' : 'Models' }</ h1 >
< p className = "text-lg text-gray-600 dark:text-gray-400 mb-8" >
{ isZh
? '当前支持符合 OpenAI 接口规范且支持 tool call 的模型,包括公有云服务和私有部署方案。'
: 'Supports models that comply with OpenAI API specification and support tool calls, including public cloud services and private deployments.' }
</ p >
2025-09-29 16:33:15 +08:00
2025-12-22 19:41:47 +08:00
< section className = "mb-10" >
2026-02-27 20:36:00 +08:00
< Heading id = "tested-models" className = "text-2xl font-semibold mb-3" >
2026-02-27 19:46:44 +08:00
{ isZh ? '已测试模型' : 'Tested Models' }
</ Heading >
2025-12-22 19:41:47 +08:00
< div className = "bg-linear-to-br from-emerald-50 to-cyan-50 dark:from-emerald-950/30 dark:to-cyan-950/30 rounded-xl p-6 border border-emerald-200/50 dark:border-emerald-800/50" >
2026-02-09 15:50:20 +08:00
< div className = "grid grid-cols-[5rem_1fr] gap-x-3 gap-y-3 items-start" >
2026-02-06 18:49:06 +08:00
{ Object . entries ( MODEL_GROUPS ). map (([ brand , models ]) => (
2026-02-09 15:50:20 +08:00
< Fragment key = { brand }>
< span className = "text-xs font-semibold text-gray-500 dark:text-gray-400 pt-2" >
2026-02-06 18:49:06 +08:00
{ brand }
</ span >
2026-02-09 15:50:20 +08:00
< div className = "flex flex-wrap gap-2" >
{ models . map (( model ) => (
< ModelBadge key = { model } model = { model } baseline = { BASELINE . has ( model )} />
))}
</ div >
</ Fragment >
2026-02-06 18:49:06 +08:00
))}
2025-12-22 19:41:47 +08:00
</ div >
2025-09-29 16:33:15 +08:00
</ div >
2025-12-22 19:41:47 +08:00
</ section >
< section className = "mb-10" >
2026-04-04 01:15:41 +08:00
< Heading id = "tips" level = { 3 }>
Tips
</ Heading >
2025-12-22 19:41:47 +08:00
< div className = "p-4 bg-blue-50 dark:bg-blue-950/20 rounded-lg border border-blue-200 dark:border-blue-800" >
< ul className = "text-sm text-gray-700 dark:text-gray-300 space-y-2 list-disc pl-5" >
2026-03-09 22:31:35 +08:00
< li >
{ isZh
? '⭐ 推荐使用 ToolCall 能力强的轻量级模型'
: '⭐ Recommended: Fast, lightweight models with strong ToolCall capabilities' }
</ li >
2026-01-30 15:13:31 +08:00
< li >
{ isZh
? 'ToolCall 能力较弱的模型可能返回错误的格式,常见错误能够自动恢复,建议设置较高的 temperature'
: 'Models with weaker ToolCall capabilities may return incorrect formats. Common errors usually auto-recover. Higher temperature recommended' }
</ li >
< li >
{ isZh
? '小模型或者无法适应复杂 Tool 定义的模型,通常效果不佳'
: 'Small models or those unable to handle complex tool definitions typically perform poorly' }
</ li >
2025-09-29 16:33:15 +08:00
</ ul >
</ div >
2025-12-22 19:41:47 +08:00
</ section >
< section className = "mb-10" >
2026-02-27 20:36:00 +08:00
< Heading id = "configuration" >{ isZh ? '配置方式' : 'Configuration' }</ Heading >
2025-12-22 19:41:47 +08:00
< CodeEditor
code = { `// OpenAI-compatible services (e.g., Alibaba Bailian)
2025-09-29 16:33:15 +08:00
const pageAgent = new PageAgent({
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
apiKey: 'your-api-key',
2026-02-24 00:59:42 +08:00
model: 'qwen3.5-plus'
2025-09-29 16:33:15 +08:00
});
2025-10-21 17:22:05 +08:00
` }
2025-12-22 19:41:47 +08:00
/>
</ section >
2026-02-02 16:51:51 +08:00
2026-02-24 00:59:42 +08:00
< section className = "mb-10" >
2026-02-27 20:36:00 +08:00
< Heading id = "free-testing-api" >{ isZh ? '免费测试接口' : 'Free Testing API' }</ Heading >
2026-02-24 00:59:42 +08:00
< p className = "text-sm text-gray-600 dark:text-gray-400 mb-4" >
{ isZh
2026-03-09 22:31:35 +08:00
? '以下免费测试接口仅供 PageAgent.js 和 PageAgent Extension 的技术评估和测试使用。'
: 'The following free testing endpoint is provided for testing and technical evaluation.' }
2026-02-24 00:59:42 +08:00
</ p >
2026-03-09 22:31:35 +08:00
< div className = "my-4 p-4 bg-amber-50 dark:bg-amber-950/20 rounded-lg border border-amber-200 dark:border-amber-800" >
< p className = "text-xs text-gray-600 dark:text-gray-400" >
{ isZh
? '⚠️ 仅供技术评估和研发用途,禁止用于生产环境。数据通过中国大陆服务器处理。请勿输入任何个人身份信息或敏感数据。使用即表示您同意'
: '⚠️ Strictly for technical evaluation and R&D only. Data is processed via servers in Mainland China. Do not input any PII or sensitive data. By using this API you agree to the' }{ ' ' }
< a
href = "https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md#2-testing-api-and-demo-disclaimer--terms-of-use"
target = "_blank"
rel = "noopener noreferrer"
className = "text-blue-500 hover:underline"
>
{ isZh ? '使用条款' : 'Terms of Use' }
</ a >
</ p >
</ div >
2026-02-27 17:09:54 +08:00
< div className = "bg-gray-50 dark:bg-gray-900/30 rounded-lg p-5 border border-gray-200 dark:border-gray-800" >
< h3 className = "font-semibold text-gray-900 dark:text-gray-100 mb-2" >
Qwen ( Alibaba Cloud China )
</ h3 >
< p className = "text-xs text-gray-500 dark:text-gray-400 mb-3" >
{ isZh
? '通过阿里云函数计算(中国大陆)转发至百炼 Qwen 模型'
: 'Proxied via Alibaba Cloud FC (Mainland China) to BaiLian Qwen models' }
{ ' · ' }
< a
href = "https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md#2-testing-api-and-demo-disclaimer--terms-of-use"
target = "_blank"
rel = "noopener noreferrer"
className = "text-blue-500 hover:underline"
>
{ isZh ? '使用条款' : 'Terms of Use' }
</ a >
</ p >
< CodeEditor
2026-04-02 15:30:58 +08:00
code = { `# qwen3.5-plus / qwen3.5-flash
2026-02-27 17:09:54 +08:00
LLM_BASE_URL="https://page-ag-testing-ohftxirgbn.cn-shanghai.fcapp.run"
2026-04-04 01:15:41 +08:00
LLM_MODEL_NAME="qwen3.5-plus"` }
2026-02-27 17:09:54 +08:00
/>
2026-02-24 00:59:42 +08:00
</ div >
</ section >
2026-02-02 16:51:51 +08:00
< section className = "mb-10" >
2026-04-27 20:12:25 +08:00
< Heading id = "production-authentication" className = "text-2xl font-semibold mb-4" >
{ isZh ? '🔐 生产环境鉴权' : '🔐 Production Authentication' }
</ Heading >
< p className = "text-sm text-gray-600 dark:text-gray-400 mb-3" >
{ isZh
? '如果你只是将它用作个人助手,可以直接连接你的 LLM 服务。'
: 'If you only use it as a personal assistant, you can connect to your LLM service directly.' }
</ p >
< p className = "text-sm text-gray-600 dark:text-gray-400 mb-3" >
{ isZh ? (
<>
如果你计划将它集成到你的 Web 应用中,建议搭建一个后端代理来转发 LLM 请求,并使用 { ' ' }
< code > customFetch </ code > 携带 Cookie 或其他鉴权信息:
</>
) : (
<>
If you plan to integrate it into your web app , it 's better to have a backend proxy for
the LLM and use <code>customFetch</code> to authenticate the request with cookies or
other methods:
</>
)}
</p>
<CodeEditor
code={`const agent = new PageAgent({
baseURL: ' / api / llm - proxy ',
model: ' gpt - 5.1 ',
customFetch: (url, init) =>
fetch(url, { ...init, credentials: ' include ' }),
});`}
/>
<div className="mt-4 bg-yellow-50 dark:bg-yellow-950/20 border-l-4 border-yellow-500 p-4 rounded-r-lg">
<p className="text-sm font-semibold text-yellow-900 dark:text-yellow-200">
{isZh
? ' ⚠️ 永远不要把真实的 LLM API Key 提交到前端代码中 '
: ' ⚠️ NEVER commit real LLM API keys to your frontend code '}
</p>
</div>
</section>
<section className="mb-10">
<Heading id="prompt-caching">{isZh ? ' 主动缓存 ' : ' Prompt Caching '}</Heading>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
{isZh ? (
<>
一些 LLM 能从主动缓存中受益很多。由于各个供应商的主动缓存接口不同,推荐使用{' '}
<Link
href="/advanced/page-agent-core#configuration"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
transformRequestBody
</Link>{' '}
为你的模型供应商配置缓存提示。
</>
) : (
<>
Some LLMs benefit significantly from prompt caching. Because each provider exposes
caching differently, use{' '}
<Link
href="/advanced/page-agent-core#configuration"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
transformRequestBody
</Link>{' '}
to add provider-specific cache hints.
</>
)}
</p>
<div className="space-y-6">
<section>
<Heading id="prompt-caching-claude" level={3}>
Claude
</Heading>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
{isZh
? ' Claude 支持全局 Automatic prompt caching 。使用兼容 Claude 的代理时,只需要在请求体顶层添加 cache_control 。 '
: ' Claude supports global Automatic prompt caching . When using a Claude - compatible proxy , add cache_control at the top level of the request body . '}
</p>
<CodeEditor
language="typescript"
code={`const pageAgent = new PageAgent({
baseURL: ' https : //your-claude-proxy.example/v1',
apiKey : 'your-api-key' ,
model : 'claude-sonnet-4.5' ,
transformRequestBody : ( requestBody ) => ({
... requestBody ,
cache_control : { type : 'ephemeral' },
}),
}); `}
/>
</section>
<section>
<Heading id="prompt-caching-qwen" level={3}>
{isZh ? '阿里云百炼 Qwen' : 'Alibaba Cloud Bailian Qwen'}
</Heading>
<CodeEditor
language="typescript"
code={` const pageAgent = new PageAgent ({
baseURL : 'https://dashscope.aliyuncs.com/compatible-mode/v1' ,
apiKey : 'your-api-key' ,
model : 'qwen3.5-plus' ,
transformRequestBody : ( requestBody ) => {
const [ systemMessage , ... restMessages ] = requestBody . messages
if ( systemMessage . role !== 'system' || typeof systemMessage . content !== 'string' ) {
return requestBody
}
return {
... requestBody ,
messages : [
{
... systemMessage ,
content : [
{
type : 'text' ,
text : systemMessage.content ,
cache_control : { type : 'ephemeral' },
},
],
},
... restMessages ,
],
}
},
}); `}
/>
</section>
</div>
</section>
<section className="mb-10">
<Heading id="local-runtimes">{isZh ? '本地 LLMs' : 'Local LLMs'}</Heading>
2026-04-04 01:15:41 +08:00
<p className="text-sm text-gray-600 dark:text-gray-400 mb-6">
2026-02-02 16:51:51 +08:00
{isZh
2026-04-04 01:15:41 +08:00
? '通过 Ollama、LM Studio 等本地 OpenAI-compatible 运行时接入 PageAgent,实现离线或局域网部署。'
: 'Use local OpenAI-compatible runtimes such as Ollama and LM Studio with PageAgent for offline or LAN deployments.'}
2026-02-02 16:51:51 +08:00
</p>
2026-03-03 21:59:02 +08:00
2026-04-04 01:15:41 +08:00
<div className="space-y-10">
<section>
<Heading id="requirements" level={3}>
Requirements
</Heading>
<div className="mt-4 p-5 bg-amber-50 dark:bg-amber-950/20 rounded-xl border border-amber-200 dark:border-amber-800">
<ul className="text-sm text-gray-700 dark:text-gray-300 space-y-2 list-disc pl-5">
<li>
{isZh
? '务必打开 CORS,否则浏览器无法直接请求本地 LLM 服务。'
: 'Enable CORS, otherwise the browser cannot call your local LLM endpoint directly.'}
</li>
<li>
{isZh
? '将 context length 或 content length 至少设置为 8000。普通页面常常需要 15k token 左右,默认 4k 很容易被截断。'
: 'Set context length or content length to at least 8000. A typical page often needs around 15k tokens, so the default 4k usually truncates prompts.'}
</li>
<li>
{isZh ? '需要支持 tool_call 的模型。' : 'Use a model with tool_call support.'}
</li>
<li>
{isZh
? '小于 10B 参数的模型通常效果不佳。'
: 'Models smaller than 10B are usually not strong enough.'}
</li>
</ul>
</div>
</section>
2026-03-03 21:59:02 +08:00
2026-04-04 01:15:41 +08:00
<section>
<Heading id="local-configuration" level={3}>
{isZh ? '基础配置' : 'Basic Configuration'}
</Heading>
2026-03-03 21:59:02 +08:00
<CodeEditor
2026-04-04 01:15:41 +08:00
code={` // Local OpenAI-compatible runtime - no apiKey needed
const pageAgent = new PageAgent ({
baseURL : 'http://localhost:11434/v1' ,
model : 'qwen3:14b'
});
// Or connect to LM Studio
const lmStudioAgent = new PageAgent ({
baseURL : 'http://127.0.0.1:1234/v1' ,
model : 'qwen/qwen3.5-27b'
});
`}
2026-03-03 21:59:02 +08:00
/>
2026-04-04 01:15:41 +08:00
</section>
2026-03-03 21:59:02 +08:00
2026-04-04 01:15:41 +08:00
<section>
<Heading id="ollama" level={3}>
Ollama
</Heading>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
{isZh
? '已在 Ollama 0.15 + qwen3:14b (RTX3090 24GB) 上测试通过。'
: 'Tested on Ollama 0.15 with qwen3:14b (RTX3090 24GB).'}
2026-03-03 21:59:02 +08:00
</p>
<CodeEditor
2026-04-04 01:15:41 +08:00
code={` LLM_BASE_URL = "http://localhost:11434/v1"
LLM_MODEL_NAME = "qwen3:14b" `}
2026-03-03 21:59:02 +08:00
/>
2026-04-04 01:15:41 +08:00
<div className="mt-4 p-4 bg-amber-50 dark:bg-amber-950/20 rounded-lg border border-amber-200 dark:border-amber-800">
<h3 className="font-semibold text-amber-900 dark:text-amber-200 mb-2">
{isZh ? '⚠️ 注意事项' : '⚠️ Important Notes'}
</h3>
<p className="text-sm text-gray-700 dark:text-gray-300">
{isZh
? '如果浏览器侧请求失败,优先检查 Ollama 是否已按上面的要求开启 CORS。'
: 'If browser-side requests fail, check whether Ollama has CORS enabled as required above.'}
</p>
</div>
2026-03-09 22:31:35 +08:00
2026-04-04 01:15:41 +08:00
<div className="mt-4">
<h3 className="font-semibold text-gray-900 dark:text-gray-100 mb-3">
{isZh ? '建议启动参数' : 'Recommended Startup'}
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-3">
{isZh
? '启动 Ollama 时建议同时放大上下文窗口并开启跨域访问。'
: 'When starting Ollama, increase the context window and enable cross-origin access.'}
</p>
<div className="space-y-2">
<p className="text-xs font-medium text-gray-500 dark:text-gray-400">
macOS / Linux
</p>
<CodeEditor
code={` OLLAMA_CONTEXT_LENGTH = 64000 OLLAMA_HOST = 0.0.0.0 :11434 OLLAMA_ORIGINS = "*" ollama serve `}
/>
<p className="text-xs font-medium text-gray-500 dark:text-gray-400 pt-2">
Windows (PowerShell)
</p>
<CodeEditor
code={` $env :OLLAMA_CONTEXT_LENGTH = 64000 ; $env :OLLAMA_HOST = "0.0.0.0:11434" ; $env :OLLAMA_ORIGINS = "*" ; ollama serve `}
/>
</div>
</div>
</section>
<section>
<Heading id="lm-studio" level={3}>
LM Studio
</Heading>
<CodeEditor
code={` LLM_BASE_URL = "http://127.0.0.1:1234/v1"
LLM_MODEL_NAME = "qwen/qwen3.5-27b" `}
/>
<div className="mt-4 p-4 bg-amber-50 dark:bg-amber-950/20 rounded-lg border border-amber-200 dark:border-amber-800">
<h3 className="font-semibold text-amber-900 dark:text-amber-200 mb-2">
{isZh ? '⚠️ 注意事项' : '⚠️ Important Notes'}
</h3>
<ul className="text-sm text-gray-700 dark:text-gray-300 space-y-2 list-disc pl-5">
<li>
{isZh
? 'Agent 必须启用 disableNamedToolChoice,否则 tool_choice 参数会报错。'
: 'Enable disableNamedToolChoice in the agent config, otherwise the tool_choice parameter may fail.'}
</li>
</ul>
</div>
</section>
2026-03-09 22:31:35 +08:00
</div>
</section>
2025-09-29 16:33:15 +08:00
</div>
)
}